噪声是个很神奇的东西,以前接触的时候就是在自动生成地图上,因为噪声本来就近自然所以,很多特效也是基于噪声的。
前几篇文章介绍了纹理和光照,这回其实也就是用这么多。就是光照加一个噪声的法线纹理。
你可能就玩过一款游戏,剑灵,游戏中怪物死亡的时候会有一种消融的效果。让我们来看一看是怎么实现的。
下面三个图片分别是BurnAmount = 0、BurnAmount = 0.25、BurnAmount = 0.5时的效果。
这里我们用两个Pass来处理这个效果
- 第一个Pass:渲染模型的正常颜色和消融的混合颜色
- 第二个Pass:处理消融的阴影,如不特殊处理则影子不会有消融效果。
下面给出代码(有必要的注释)
1 Shader "Shader/Shader" 2 { 3 Properties 4 { 5 _MainTex ("Main Tex", 2D) = "white" {} 6 // 法线贴图 7 _BumpMap ("Bump Map", 2D) = "bump" {} 8 // 噪声贴图 9 _BurnMap ("Burn Map", 2D) = "white" {} 10 // 控制消融程度 11 _BurnAmount ("Burn Amount", Range(0, 1)) = 0 12 // 控制模型烧焦效果的线宽 13 _LineWidth ("Burn Line Width", Range(0.0, 0.2)) = 0.1 14 // 烧焦边缘的颜色 15 _BurnColor ("Burn Color", Color) = (1, 0, 0, 1) 16 } 17 SubShader 18 { 19 Tags { "RenderType"="Opaque" "Queue"="Geometry" } 20 21 Pass 22 { 23 Tags { "LightMode"="ForwardBase" } 24 25 // 所有面都不被剔除,因为消融的时候应该可以看到物体的内部。 26 Cull Off 27 28 CGPROGRAM 29 #pragma vertex vert 30 #pragma fragment frag 31 // 为得到正确的光照设置的编译指令 32 #pragma multi_compile_fwdbase 33 34 #include "Lighting.cginc" 35 #include "AutoLight.cginc" 36 37 sampler2D _MainTex; 38 sampler2D _BumpMap; 39 sampler2D _BurnMap; 40 float4 _MainTex_ST; 41 float4 _BumpMap_ST; 42 float4 _BurnMap_ST; 43 fixed _BurnAmount; 44 fixed _LineWidth; 45 fixed4 _BurnColor; 46 47 struct a2v 48 { 49 float4 vertex : POSITION; 50 float3 normal : NORMAL; 51 float2 texcoord : TEXCOORD0; 52 fixed4 tangent : TANGENT; 53 }; 54 55 struct v2f 56 { 57 float4 pos : SV_POSITION; 58 float2 uvMainTex : TEXCOORD0; 59 float2 uvBumpMap : TEXCOORD1; 60 float2 uvBurnMap : TEXCOORD2; 61 // 切线视图的光照方向 62 float3 lightDir : TEXCOORD3; 63 float3 worldPos : TEXCOORD4; 64 // Shader 阴影宏:用于对阴影纹理采样的坐标 65 // 参数:一个可用的差值寄存器的索引值,用世界的顶点坐标。即5(从0开始) 66 SHADOW_COORDS(5) 67 }; 68 69 v2f vert(a2v v) 70 { 71 v2f o; 72 73 o.pos = UnityObjectToClipPos(v.vertex); 74 75 o.uvMainTex = TRANSFORM_TEX(v.texcoord, _MainTex); 76 o.uvBumpMap = TRANSFORM_TEX(v.texcoord, _BumpMap); 77 o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap); 78 79 TANGENT_SPACE_ROTATION; 80 o.lightDir = mul(rotation, ObjSpaceLightDir(v.vertex)); 81 o.worldPos = UnityObjectToWorldDir(v.vertex); 82 83 // 阴影宏:在顶点着色器中计算上一步中声明的阴影纹理坐标 84 TRANSFER_SHADOW(o); 85 86 return o; 87 } 88 89 fixed4 frag(v2f i) : SV_Target 90 { 91 // 取噪声贴图进行采样 92 fixed3 burn = tex2D(_BurnMap, i.uvBurnMap); 93 // 用于剔除像素点,if (采样结果-消融程度阀值 > 0) 存留 or 丢弃 94 clip(burn.r - _BurnAmount); 95 96 fixed3 albedo = tex2D(_MainTex, i.uvMainTex).rgb; 97 98 fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz * albedo; 99 100 fixed3 tangentLightDir = normalize(i.lightDir);101 fixed3 tangentNormal = UnpackNormal(tex2D(_BumpMap, i.uvBumpMap));102 fixed3 diffuse = _LightColor0.rgb * albedo * max(0, dot(tangentLightDir, tangentNormal));103 104 // 阴影宏:计算阴影值,最后与漫反射的颜色叠加105 UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);106 107 // smoothsetp(e1,e2,x)平滑过度函数108 // 原理:if (x < e1) 结果 = 0109 // if (x > e2) 结果 = 1110 // if (e1 < x < e2) 结果 = 3 * pow(x, 2) - 2 * pow(x, 3)111 // t = 0时,该像素为正常的模型颜色112 // t = 1时,该像素位于消融的边界处113 fixed t = 1 - smoothstep(0.0, _LineWidth, burn.r - _BurnAmount);114 115 // 最终颜色,在消融和正常中差值,系数为t116 fixed3 finalColor = lerp(ambient + diffuse * atten, _BurnColor, t);117 118 return fixed4(finalColor, 1);119 }120 ENDCG121 }122 123 // 此Pass是处理阴影的,我们需要按照正常的Pass的处理来剔除片元或进行顶点动画。以便阴影可以和物体正常渲染的结果相匹配。124 Pass125 {126 // 光照模式定义为阴影127 Tags { "LightMode"="ShadowCaster" }128 129 CGPROGRAM130 #pragma vertex vert131 #pragma fragment frag132 // 处理阴影的编译指令133 #pragma multi_compile_shadowcaster134 135 #include "UnityCG.cginc"136 137 fixed _BurnAmount;138 sampler2D _BurnMap;139 float4 _BurnMap_ST;140 141 struct a2v142 {143 float4 vertex : POSITION;144 float2 texcoord : TEXCOORD0;145 float3 normal : NORMAL;146 float4 tangent: TANGENT;147 };148 149 struct v2f150 {151 // 定义阴影投射需要定义的变量152 V2F_SHADOW_CASTER;153 float2 uvBurnMap : TEXCOORD0;154 };155 156 v2f vert (a2v v)157 {158 v2f o;159 160 // 填充V2F_SHADOW_CASTER定义的变量161 TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)162 o.uvBurnMap = TRANSFORM_TEX(v.texcoord, _BurnMap);163 164 return o;165 }166 167 fixed4 frag (v2f i) : SV_Target168 {169 fixed3 burn = tex2D(_BurnMap, i.uvBurnMap);170 171 clip(burn - _BurnAmount);172 173 // 让Unity完成阴影投射174 SHADOW_CASTER_FRAGMENT(i)175 }176 ENDCG177 }178 }179 }
本文引自Unity Shader 入门精要