实现思路
消融效果首先需要一张噪声图,噪声图可以用一些软件来生成例如ps,也可以自己写噪声算法来生成。
获得噪声图之后,在shader中对噪声纹理的亮度值进行采样,丢弃小于阈值的片元。
还需要添加一些补充效果,例如表现烧焦的边缘之类的,这也可以通过一个亮度的比较再加上插值来完成。
噪声图
简单起见,我们直接使用PS来生成,打开PS的工具栏。滤镜->渲染->分层云彩。之后再添加图层进行一些调色,得到我们使用的噪声图。
如果想要自己来生成噪声图,也可以查阅perlin噪声算法相关的资料。
clip函数
关键代码也很简单
tex2D对_NoiseTex噪声图进行采样,此时获得的是rgb值,使用abs获取亮度值。之后使用clip函数和_Threshold阈值进行比较,就可以丢弃小于阈值的片元。
fixed burnAmount = abs(tex2D(_NoiseTex, i.uvNoise));
clip(burnAmount - _Threshold);
添加了简单的lambert漫反射模型。此时效果如下
边缘烧焦效果
有时候可能还需要一些更加艺术化的处理,例如边缘的烧焦效果。
大致就是离被丢弃的片元越近的片元,它就烧焦的更多(需要定义一个烧焦的颜色。)这里就直接用噪声纹理的亮度值减掉阈值,结果值越大,说明离烧焦的边缘越远。
最后使用lerp函数进行插值就可以了。这里还使用了min函数,确保lerp函数的第三个参数小于1,不然会出现奇怪的效果。
float burnEdge = burnAmount - _Threshold;
diffuse = lerp(_BurnColor, diffuse, min(1, burnEdge * _BurnColorExpand));
最终调整参数后效果如下
还可以用一个脚本来动态调整阈值,造成一种物体渐渐被烧光的错觉,这也可以搭配粒子效果。
void Update()
{
material.SetFloat("_Threshold",threshold);
threshold += Time.deltaTime/10f;
}
完整代码
Shader "LX/dissolve"
{
Properties
{
_MainTex ("Texture", 2D) = "white" {}
_NoiseTex("NoiseTex",2D)="white"{}
_Threshold("Threshold",float)=0.5
_BurnColor("BurnColor",color)=(1,1,1,1)
_BurnColorExpand("BurnColorExpand",float)=1
}
SubShader
{
Tags
{
"RenderType"="Opaque"
}
LOD 100
Cull Off
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct appdata
{
float4 vertex : POSITION;
float2 uv : TEXCOORD0;
float3 normal:NORMAL;
};
struct v2f
{
float2 uvMain : TEXCOORD0;
float4 vertex : SV_POSITION;
fixed worldLightDir:TEXCOORD1;
fixed worldNormal:TEXCOORD2;
float2 uvNoise : TEXCOORD3;
};
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _NoiseTex;
float4 _NoiseTex_ST;
float _Threshold;
float3 _BurnColor;
float _BurnColorExpand;
v2f vert(appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uvMain = TRANSFORM_TEX(v.uv, _MainTex);
o.uvNoise = TRANSFORM_TEX(v.uv, _NoiseTex);
float4 worldPos = mul(unity_ObjectToWorld, v.vertex);
o.worldLightDir = UnityWorldSpaceLightDir(worldPos);
o.worldNormal = UnityObjectToWorldNormal(v.normal);
return o;
}
fixed4 frag(v2f i) : SV_Target
{
fixed burnAmount = abs(tex2D(_NoiseTex, i.uvNoise));
clip(burnAmount - _Threshold);
fixed3 worldLightDir = i.worldLightDir;
fixed3 worldNormal = i.worldNormal;
fixed4 col = tex2D(_MainTex, i.uvMain);
fixed3 diffuse = dot(worldLightDir, worldNormal) * col;
float burnEdge = burnAmount - _Threshold;
diffuse = lerp(_BurnColor, diffuse, min(1, burnEdge * _BurnColorExpand));
return fixed4(diffuse, 0);
}
ENDCG
}
}
}
另外代码也传到github仓库里了,大家也可以关注一下哦~
我的github