前言

​ 早些时间过了一遍Games101,但是中间间隔了一段长时间的实习后发现自己忘了不少。所以在复习Games101的过程中也去跟着shader入门精要去实现一些毕设Demo可能要用到的效果。

概述

消融效果常见于游戏中的角色死亡、地图销毁等效果。

​ 其原理比较简单,概括来说就是噪声纹理+透明度测试,我们使用对噪声纹理采样的结果和某个控制消融程度的阈值比较,如果小于阈值,就使用clip函数把它对应的像素裁剪掉,这些部分就对应了图中被”销毁”的区域。而镂空区域边缘的烧焦效果则是将两种颜色混合,再用pow函数处理后,与原纹理颜色混合后的效果。

代码实现

Dissolve消融:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'

Shader "BattleEffect/Dissolve"
{
Properties
{
_MainColor("Color",Color)=(1,1,1,1)
_Specular("Specular",Color)=(1,1,1,1)
_BurnAmount("Burn Amount",Range(0.0,1.0))=0.0 //用于控制消融程度,当值为0时,物体为正常效果,当值为1时,物体会完全消融
_LineWidth("Burn Line Width",Range(0.0,0.2))=0.1 //用于控制模拟烧焦效果时的线宽,它的值越大,火焰边缘的蔓延范围越广
_MainTex("Base (RGB)",2D) = "White"{} //对应物体原本的漫反射纹理
_BumpMap("Normal Map",2D)="bump"{} //对应物体原本的法线纹理(凹凸映射并不会真的改变模型的顶点位置,只是修改模型表面的法线,以便为模型提供更多的细节)
//bump是Unity内置的法线纹理,当没有提供任何发现信息是,bump就对应了模型自带的法线信息
_BurnFirstColor("Burn First Color",Color)=(1,0,0,1) //对应了火焰边缘的颜色
_BurnSecondColor("Burn Second Color",Color)=(1,0,0,1)//对应了火焰边缘的颜色
_Gloss("Gloss",Range(8.0,256))=20
_BurnMap("Burn Map",2D)="White"{}
}
SubShader{
Pass{
Tags {"LightMode"="ForwardBase"} //设置类型为前向渲染

Cull Off //关闭剔除

CGPROGRAM

#pragma vertex vert
#pragma fragment frag


#include "Lighting.cginc"
#include "AutoLight.cginc"

#pragma multi_compile_fwdbase //设置pass类型为forwardPass

//与定义的Properties相关联
sampler2D _MainTex;
sampler2D _BumpMap;
sampler2D _BurnMap;
float _BurnAmount;
float _LineWidth;
float4 _MainColor;
float4 _BurnFirstColor;
float4 _BurnSecondColor;
float4 _MainTex_ST;
float4 _BumpMap_ST;
float4 _BurnMap_ST;
float _Gloss;
fixed4 _Specular;


//定义顶点着色器
struct a2v
{
float4 vertex:POSITION; //获得顶点模型坐标
float3 normal:NORMAL; //获得顶点模型坐标系下法线
float4 texcoord:TEXCOORD0; //用第一套纹理坐标来初始化texcoord(自身理解的话像是每一个顶点的映射,然后在像素着色阶段时可以是每一个像素的映射)
//UV本质是把一张平面图像的不同区域(不同位置)映射到3D模型的不同面上


};
struct v2f
{
float4 pos:SV_POSITION;
float2 uvMainTex:TEXCOORD0;
float2 uvBumpMap:TEXCOORD1;
float2 uvBurnMap:TEXCOORD2;
//float3 lightDir:TEXCOORD3;
float3 worldNormal:TEXCOORD3;
float3 worldPos:TEXCOORD4;
SHADOW_COORDS(5)// 相当于float4 _ShadowCoord:TEXCOORD5
};

v2f vert(a2v v)
{
v2f o;
o.pos=UnityObjectToClipPos(v.vertex); //获取对应顶点的归一化齐次裁剪坐标


o.uvMainTex=TRANSFORM_TEX(v.texcoord,_MainTex);//计算纹理对应的纹理坐标
o.uvBumpMap=TRANSFORM_TEX(v.texcoord,_BumpMap);
o.uvBurnMap=TRANSFORM_TEX(v.texcoord,_BurnMap);

//TANGENT_SPACE_ROTATION; //定义从世界坐标到切线坐标的rotation
//o.lightDir=mul(rotation,ObjSpaceLightDir(v.vertex)).xyz

//o.lightDir=UnityWorldSpaceLightDir(v.vertex); //得到世界空间中这个顶点到光源的方向

o.worldNormal=UnityObjectToWorldNormal(v.normal);
o.worldPos=mul(unity_ObjectToWorld,v.vertex).xyz; //计算世界空间下的顶点坐标


TRANSFER_SHADOW(o);//计算阴影纹理的采样坐标

return o;
}

//实现片元着色器模拟消融效果
fixed4 frag(v2f i):SV_Target{
fixed3 burn=tex2D(_BurnMap,i.uvBurnMap); //根据得到的纹理坐标进行采样

clip(burn.r-_BurnAmount); //消融的关键,剔除掉小于指定阈值的像素 //对于灰度图来说,其rgb都是相同的

//float3 tangentLightDir=normalize(i.lightDir);
//fixed3 tangentNormal=UnpackNormal(tex2D(_BumpMap,i.uvBumpMap));

fixed3 worldNormal=normalize(i.worldNormal); //计算世界坐标下像素
fixed3 worldLightDir=UnityWorldSpaceLightDir(i.worldPos);

fixed3 albedo=tex2D(_MainTex,i.uvMainTex).rgb;//计算材质的反射率

fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz*albedo;//计算环境光照
fixed3 diffuse=_LightColor0.rgb*albedo*max(0,dot(worldNormal,worldLightDir));

//fixed3 reflectDir=normalize(reflect(-lightDir,worldNormal));
//fixed3 viewDir=normalize(_WorldSpaceCameraPos.xyz-i.worldPos.xyz);
//fixed3 specular=_LightColor0.rgb*_Specular.rgb*pow(saturate(dot(viewDir,reflectDir)),_Gloss);

fixed t=1-smoothstep(0.0,_LineWidth,burn.r-_BurnAmount);//用t混合两种火焰颜色_BurnFirstColor和_BurnSecondColor(smoothstep可以生成v3从v1到v2对应0到1的平滑过渡值)
fixed3 burnColor=lerp(_BurnFirstColor,_BurnSecondColor,t);
burnColor=pow(burnColor,5);//对烧焦颜色结果进行了处理

UNITY_LIGHT_ATTENUATION(atten,i,i.worldPos);//进行光照衰减适配,对阴影映射纹理进行采样
fixed3 finalColor=lerp(ambient+diffuse*atten,burnColor,t*step(0.0001,_BurnAmount)); //用step来保证当_BurnAmount为0时,不显示任何消融效果

fixed4 resultColor=fixed4(finalColor,1)*_MainColor; //进行颜色混合

return resultColor;

}


ENDCG
}

}
FallBack "VertexLit"
}