在很多游戏中能够看到消散,燃烧等特效。
如:
所以今天写简单的消散特效的shader代码!!!
(学习的庄懂的视频学到的,强烈新手学习庄懂视频)
第一步思考:我们能看到什么!
我们能够看到网格的消散,这个消散过程中:
第一:网格的偏移,我们可以简单的用顶点向法线方向的偏移,所以我们需要先有一张网格遮罩的贴图。
第二:有些前面一点的网格提前消散,所以我们需要模型网格随机灰度遮罩。
第三:第二张图中可以看到这不是一个同一高度一起消散的,说明有弧度,这里我们用模型面坡度遮罩实现。
然后节约采样次数,所以可以合并在一张图中。
第二步如何写:
首先获取定义2张贴图,和控制参数:
[Header(Effect)]
_EffMap01 ("特效纹理1",2D) = "gray"{}
_EffMap02 ("特效纹理2",2D) = "gray"{}
[HDR]_EffCol ("特效颜色",Color) = (0,0,0,0)
_EffParams ("X:波密度 Y:波速度 Z:混乱度 W:消散强度",vector) = (0.03,3.0,0.3,2.5)
uniform sampler2D _EffMap01;
uniform sampler2D _EffMap02 ;
uniform float3 _EffCol;
uniform float4 _EffParams;
其中第一张图就是前面制作的网格相关的贴图,后者就是noise扰动图。
然后我们先写出顶点动画:
float4 CyberpunkAnim(float noise,float mask,float3 normal,inout float3 vertex){
//锯齿波
float baseMask = abs(frac(vertex.y * _EffParams.x - _Time.x * _EffParams.y) - 0.5) * 2.0;
//返回值
return baseMask;
}
两张图看不出是什么波,这里用锯齿波后面能够控制强度能够多次显示,好看点。
为什么锯齿波这么写!!
f(x) = abs(frac(x))
然后这个波的值域在[0,1]之间 所以直接输出这个的话
出现了严重锯齿感,没有缓慢的起伏的效果所以
f(x) = frac(x)-0.5
值域控制在[-0.5,0.5]之间
f(x) = abs(frac(x)-0.5) * 2
值域就能控制在[0,1]并且锯齿感消除了,有了明显的起伏
加上时间速度和强度的配合就有了简单的波动的效果了。
为了让我们的波不那么连续,我们控制了白的时间是黑的2倍。
baseMask = min(1.0,baseMask * 2.0);
f(x) = min(1.0,(abs(frac(x)-0.5) * 2) * 2)
相当于原本值域[0,1]变成了[0,2]然后y=1来截取了这个图形 所以值域还是在[0,1]之间。
//noise偏移锯齿波
baseMask += (noise - 0.5) * _EffParams.z;
在顶点处理阶段先采样了noise扰动图 记住noise 区间也在[0,1]想要偏移方向正确 控制到[-0.5,0.5]之间。
较为关键的地方:
//smoothstep出各级Mask
float4 effectMask = float4(0,0,0,0);
effectMask.x = smoothstep(0.0,0.9,baseMask);
effectMask.y = smoothstep(0.2,0.7,baseMask);
effectMask.z = smoothstep(0.4,0.5,baseMask);
smoothstep(a, b, x)
f(x) = smoothstep(0.2,0.7,x)
smoothstep会给我们一个很平滑的过程。小于前者则为0,大于后者则为1,中间就是3a^2-2a^3。
然后3个作为rgb通道的控制,也是一个显示范围。
//顶点色遮罩
effectMask.w = mask;
//顶点动画
vertex.xz += normal.xz * (1.0-effectMask.y) * _EffParams.w * mask;
最后就是顶点向法线方向的偏移,前面2张图能看出就是波动范围内才有偏移,然后用了g通道的范围,其他范围都可以看自己的需求了,还有消散强度。不要忘记底座是没有消散的所以要使用上遮罩。
最后就是贴上网格图来控制咯!
float3 _EffMap01_var = tex2D(_EffMap01, i.uv1).xyz;
float meshMask = _EffMap01_var.x;
float faceRandomMask = _EffMap01_var.y;
float faceSlopeMask = _EffMap01_var.z;
采样网格贴图,并且获取xyz通道内容。x:网格遮罩,y:模型网格随机灰度遮罩。 z:模型面坡度遮罩。
// 获取EffectMask
float smallMask = i.effectMask.x;
float midMask = i.effectMask.y;
float bigMask = i.effectMask.z;
float baseMask = i.effectMask.w;
获取顶点动画返回的各通道值。xyz:三个范围大小,w:底座遮罩。
// 计算Opacity
float midOpacity= saturate(floor(min(faceRandomMask, 0.999999) + midMask));
float bigOpacity= saturate(floor(min(faceSlopeMask, 0.999999) + bigMask));
float opacity = lerp(1.0, min(bigOpacity, midOpacity), baseMask);
前两行是分别是模型网格随机灰度遮罩用和中范围的叠加,模型面坡度遮罩和大范围的叠加。
最后进行了2者实现的判定,和控制底座遮罩。
// 叠加自发光
float meshEmitInt = (bigMask - smallMask) * meshMask;
meshEmitInt = meshEmitInt * meshEmitInt;
float3 emission = _EffCol * meshEmitInt * baseMask;
最后就是自发光,前面是控制自发光的范围在大范围和小范围之间,这一块我来显示特效自发光颜色。