1 消融特效原理

消融特效 中基于 Shader Graph 实现了消融特效,本文将基于 Shader 实现消融特效。

​ 当前实现消融特效的方法主要有 Alpha 测试消融、clip(或 discard)消融,它们的本质都是随机丢弃一些片元,以实现消融效果。

​ 本文完整资源见→Unity3D选中物体消融特效,Shader Graph 实现的消融特效见→消融特效

1)噪声纹理

​ 为模拟随机效果,可以通过对噪声纹理进行采样实现,如下是一些常用的噪声纹理。

​ 这些噪声纹理有一个共同特点:在较小的邻域范围内,灰度是渐变的,使得模拟的消融效果更加和谐。

2)Alpha 测试消融原理

​ 将片元的 alpha 通道设置为随机值,通过 AlphaTest 剔除 alpha 值小于阈值的片元,以实现消融效果,代码如下。案例见→固定管线着色器二。由于改变了 alpha 通道值,该方案会影响半透明物体的混合效果。

AlphaTest Greater [_AlphaThreshold]
...
fixed4 frag(v2f i) : SV_Target {
fixed noise = tex2D(_NoiseTex, i.uvNoiseTex).r; // 噪声采样
...
return fixed4(r, g, b, noise);
}

3)clip(或 discard)消融原理

​ 对噪声纹理进行采样,使得每个片元都对应一个噪声值,通过 clip(或 discard)函数剔除噪声值小于阈值的片元,代码如下。

fixed4 frag(v2f i) : SV_Target {
fixed noise = tex2D(_NoiseTex, i.uvNoiseTex).r; // 噪声采样
float factor = noise - _BurnAmount;
clip(factor); // 剔除factor小于0的片元, 即: if(factor < 0) discard;
...
}

2 消融特效实现

​ DieController.cs

using UnityEngine;

public class DieController : MonoBehaviour {
private RaycastHit hit; // 碰撞信息 private void Start() {
hit = new RaycastHit();
} private void Update() {
if (Input.GetMouseButtonUp(0)) {
GameObject hitObj = GetHitObj();
if (hitObj != null) {
GameObject rootObj = GetRootObj(hitObj);
rootObj.AddComponent<DissolveEffect>();
}
}
} private GameObject GetHitObj() { // 获取屏幕射线碰撞的物体
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
return hit.collider.gameObject;
}
return null;
} private GameObject GetRootObj(GameObject obj) { // 获取根对象
while (obj.transform.parent != null && obj.layer == obj.transform.parent.gameObject.layer) {
obj = obj.transform.parent.gameObject;
}
return obj;
}
}

​ 说明:DieController 脚本组件挂在相机上。

​ DissolveEffect.cs

using UnityEngine;

[DisallowMultipleComponent] // 不允许在同一对象上挂载多个该组件
public class DissolveEffect : MonoBehaviour {
private Renderer[] renderers; // 渲染器
private Material dissolveMat; // 消融材质
private float burnSpeed = 0.25f; // 燃烧速度
private float burnAmount = 0; // 燃烧量, 值越大模型镂空的越多 private void Awake() {
dissolveMat = Resources.Load<Material>("DissolveMat");
renderers = GetComponentsInChildren<Renderer>();
} private void OnEnable() {
foreach (Renderer renderer in renderers) {
Material[] materials = renderer.sharedMaterials;
Material[] dissolveMaterials = new Material[materials.Length];
for (int i = 0; i < materials.Length; i++) {
Material newMaterial = new Material(dissolveMat);
SetTexture(materials[i], newMaterial);
SetColor(materials[i], newMaterial);
newMaterial.SetFloat("_BurnAmount", 0);
dissolveMaterials[i] = newMaterial;
}
renderer.sharedMaterials = dissolveMaterials;
}
} private void Update() {
burnAmount += Time.deltaTime * burnSpeed;
foreach (Renderer renderer in renderers) {
Material[] materials = renderer.sharedMaterials;
foreach (Material material in materials) {
material.SetFloat("_BurnAmount", burnAmount);
}
}
if (burnAmount >= 1f) {
Destroy(gameObject);
}
} private void SetTexture(Material oldMaterial, Material newMaterial) { // 设置材质
if (oldMaterial.HasTexture("_MainTex")) {
Texture texture = oldMaterial.GetTexture("_MainTex");
newMaterial.SetTexture("_MainTex", texture);
}
} private void SetColor(Material oldMaterial, Material newMaterial) { // 设置颜色
Color color = Color.white;
if (oldMaterial.HasColor("_Color")) {
color = oldMaterial.GetColor("_Color");
}
newMaterial.SetColor("_Color", color);
}
}

​ DissolveEffect.shader

Shader "MyShader/DissolveEffect" {
Properties {
_MainTex("Main Tex", 2D) = "white" {} // 主纹理
_Color("Color", Color) = (1, 1, 1, 1) // 模型颜色
_NoiseTex("Noise Tex", 2D) = "white"{} // 噪声纹理
_BurnAmount("Burn Amount", Range(0, 1)) = 0 // 燃烧量, 值越大模型镂空的越多
_LineWidth("Burn Line Width", Range(0, 0.2)) = 0.1 // 燃烧的线条宽度
_BurnOuterColor("Burn Outer Color", Color) = (1, 0, 0, 1) // 燃烧线条的外侧颜色
_BurnInnerColor("Burn Inner Color", Color) = (1, 0, 0, 1) // 燃烧线条的内侧颜色
} SubShader {
Tags { "RenderType"="Opaque" "Queue"="Geometry"} Pass {
Tags { "LightMode"="ForwardBase" } Cull Off // 关闭剔除, 正反面都渲染, 因为消融会裸漏模型内部结构 CGPROGRAM #include "Lighting.cginc" #pragma vertex vert
#pragma fragment frag sampler2D _MainTex; // 主纹理
fixed4 _Color; // 模型颜色
sampler2D _NoiseTex; // 噪声纹理
fixed _BurnAmount; // 燃烧量, 值越大模型镂空的越多
fixed _LineWidth; // 燃烧的线条宽度
fixed4 _BurnOuterColor; // 燃烧线条的外侧颜色
fixed4 _BurnInnerColor; // 燃烧线条的内侧颜色
float4 _MainTex_ST; // _MainTex的缩放和偏移
float4 _NoiseTex_ST; // _NoiseTex的缩放和偏移 struct a2v {
float4 vertex : POSITION; // 模型空间顶点坐标
float3 normal : NORMAL; // 模型空间顶点法线向量
float4 texcoord : TEXCOORD0; // 顶点纹理坐标
}; struct v2f {
float4 pos : SV_POSITION; // 裁剪空间顶点坐标
float3 normal : Normal; // 世界空间顶点法线向量
half2 uvMainTex : TEXCOORD0; // 主纹理的纹理坐标
half2 uvNoiseTex : TEXCOORD1; // 噪声纹理的纹理坐标
float3 worldPos : TEXCOORD2; // 世界空间顶点坐标
}; v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex); // 模型空间顶点坐标变换到裁剪空间, 等价于: mul(UNITY_MATRIX_MVP, v.vertex)
o.normal = UnityObjectToWorldNormal(v.normal); // 将模型空间法线向量变换到世界空间(已归一化)
o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex); // 主纹理坐标缩放和偏移
o.uvNoiseTex = TRANSFORM_TEX(v.texcoord, _NoiseTex); // 噪声纹理坐标缩放和偏移
o.worldPos = mul(unity_ObjectToWorld, v.vertex).xyz; // 将模型空间顶点坐标变换到世界空间
return o;
} fixed4 frag(v2f i) : SV_Target {
fixed noise = tex2D(_NoiseTex, i.uvNoiseTex).r; // 噪声采样
float factor = noise - _BurnAmount;
clip(factor); // 剔除factor小于0的片元, 即: if(factor < 0) discard;
fixed3 lightDir = normalize(UnityWorldSpaceLightDir(i.worldPos)); // 世界空间灯光向量
fixed4 albedo = tex2D(_MainTex, i.uvMainTex) * _Color; // 模型颜色
fixed4 ambient = UNITY_LIGHTMODEL_AMBIENT * albedo; // 环境光颜色
fixed4 diffuse = _LightColor0 * albedo * max(0, dot(i.normal, lightDir)); // 漫反射光颜色
fixed t = smoothstep(0, _LineWidth, factor);
fixed4 burnColor = lerp(_BurnOuterColor, _BurnInnerColor, t);
fixed4 finalColor = lerp(burnColor, ambient + diffuse, t);
return fixed4(finalColor.xyz, 1);
} ENDCG
}
} FallBack "Diffuse"
}

​ 说明:在 Assets 目录下面新建 Resources 目录,接着在 Resources 目录下面创建材质,重命名为 DissolveMat,将 DissolveEffect.shader 与 DissolveMat 材质绑定,并将噪声纹理拖拽到 DissolveMat 的 Noise Tex 中。

3 运行效果

​ 声明:本文转自【Unity3D】选中物体消融特效

【Unity3D】选中物体消融特效的更多相关文章

  1. unity3d点击屏幕选中物体

    原文  http://blog.csdn.net/mycwq/article/details/19906335 前些天接触unity3d,想实现点击屏幕选中物体的功能.后来研究了下,实现原理就是检测从 ...

  2. unity3d 游戏插件 溶解特效插件 - Dissolve Shader

    unity3d 游戏插件 溶解特效插件 - Dissolve Shader   链接: https://pan.baidu.com/s/1hr7w39U 密码: 3ed2

  3. ThreeJs 选中物体事件

    选中物体变红色demo: https://threejs.org/examples/#webgl_raycast_sprite <!DOCTYPE html> <html lang= ...

  4. Unity合并选中物体的Mesh

    using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; pu ...

  5. unity3D 游戏物体同时绑定单击、双击事件

    前言 在unity中我们常用的获取鼠标点击的方法有 在3D场景中,一般用在Update方法中,每一帧调用 void Update(){ )){ Debug.log("鼠标左键点击" ...

  6. Unity3D 角色(物体) 移动方法 合集

    1. 简介 在Unity3D中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position. 2. 通过Transform组件移动物体 Transform 组件用于描述物体在 ...

  7. unity3d笔记:控制特效的播放速度

           一般在游戏中,主角或者怪物会受到减速效果,或者攻击速度减慢等类似的状态.本身动作减速的同时,衔接在角色上的特效也需要改变相应的播放速度.一般特效有三个游戏组件:   关键点就是改变Ani ...

  8. 时光煮雨 Unity3D让物体动起来③—UGUI DoTween&Unity Native2D实现

    本文首发蛮牛,次发博客园.接系列 第一篇,第二篇,本文为第三篇,再次感谢“武装三藏”在前两篇无私且精彩的问题解答 写在最前,时光煮雨,为了怀念 以下引用曾今读过的一些教程文章 其实这3种动画都有它特定 ...

  9. unity3d游戏物体跟着鼠标方向移动

    效果:当点击鼠标左键时,游戏对象会朝鼠标点击的方向移动,类似魔兽争霸一样. 思路:把鼠标的坐标转化成世界坐标(鼠标默认是屏幕坐标),然后当点击鼠标时,物体将朝着鼠标的世界坐标方向移动. 如果你看到这的 ...

  10. unity3d中物体的控制

    一.物体的循环移动和旋转 思路:通过对时间的计算,每隔一段时间让物体旋转,实现来回移动. float TranslateSpeed = 0.02f; float TranslateSpeedTime ...

随机推荐

  1. 【SHELL】获取脚本输入参数

    参数获取 EXEC_PARAMS=(${@:index}) 示例 ./do.sh test a b c d e f EXEC_PARAMS=(${@:0}) ./do.sh test a b c d ...

  2. 如何从零开始实现TDOA技术的 UWB 精确定位系统(5)

    这是一个系列文章<如何从零开始实现TDOA技术的 UWB 精确定位系统>第5部分. 重要提示(劝退说明): Q:做这个定位系统需要基础么? A:文章不是写给小白看的,需要有电子技术和软件编 ...

  3. html监听标签的resize

    <html> <body> <div id="div1" style="width:100%;height:100%;"> ...

  4. Linux-服务管理-service-checkconfig

  5. Go-插入排序

    // InsertSort 插入排序 // 思路: // 1. 第一个元素默认是已经排好序的 // 2. 从第二个元素开始,依次比较前面一个元素中,如果小于则交换位置 // 插入排序思路: 将一个元素 ...

  6. MySQL调优学习-快速获取占用CPU较高的SQL语句

    MySQL调优学习-快速获取占用CPU较高的SQL语句 背景 早上突然发现一个MySQL数据库的CPU使用率居高 因为是一个混布的环境上面还有一个redis 怕影响业务就上去像查看一下具体是何种原因导 ...

  7. [转帖]MySQL InnoDB存储引擎大观

      https://baijiahao.baidu.com/s?id=1709263187856706948&wfr=spider&for=pc MySQL InnoDB 引擎现在广为 ...

  8. [转帖]Tcpdump抓包命令

    tcpdump和ethereal可以用来获取和分析网络通讯活动,他们都是使用libpcap库来捕获网络封包的. ​在混杂模式下他们可以监控网络适配器的所有通讯活动并捕获网卡所接收的所有帧. ​要想设置 ...

  9. Spring缓存是如何实现的?如何扩展使其支持过期删除功能?

    前言:在我们的应用中,有一些数据是通过rpc获取的远端数据,该数据不会经常变化,允许客户端在本地缓存一定时间. 该场景逻辑简单,缓存数据较小,不需要持久化,所以不希望引入其他第三方缓存工具加重应用负担 ...

  10. js快速获取当前时间并且返回想要的格式

    function backCurrentTime (type) { let currentTime=new Date( new Date() + 8 * 3600 * 1000 ).toJSON(). ...