Different Diffuse and Decal in Surface Shader with Vertex & Fragment Shader

After experiencing it by myself, I discovered that the surface shader does hide a lot of internal implementations, like multiple light sources that I didn’t consider. Shadows, attenuation and other issues are hidden and implemented in surface shader, and it is compatible with multiple platforms. Vertex & Fragment Shader needs to be written by yourself Fortunately, Unity also provides us with some ready-made methods to adjust. _(:зゝ∠)_

So to summarize, when writing materials that need to receive complex light source information, writing surface shader should be simple A lot.
For materials that do not receive light or very little light, Vertex & Fragment Shader should be clearer and easier to understand, and faster.
Unity’s surface shader technology can save us a lot of things and increase the development speed.

Note: The following two shaders only consider the situation where there is only one parallel light, and do not consider multiple light sources, shadows, attenuation and other issues.

The implementation of surface shader can be found in the official shader resource resources. The following code can be optimized, here In order to make the steps clear, we divided some more variables

// Diffuse shader, corresponding to Legacy Shaders/DiffuseShader "Z_TestShader/TestDiffuse"{ Properties {_Color ("Color", Color) = (1,1,1,1)} SubShader {Tags{ "RenderType" = "Opaque"} Pass {Tags {"LightMode"  = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag // Forward rendering compilation instruction#pragma multi_compile_fwdbase #include "UnityCG.cginc"  #include "Lighting.cginc"  uniform float4 _Color; struct v2f {float4 pos: SV_POSITION; float3 normal: NORMAL;< /span> float3 lightDir: TEXCOORD0; }; v2f vert (appdata_full v) {v2f o; o.pos = mul(UNITY_MATRIX_MVP, v< span class="hljs-preprocessor">.vertex); // Turn the model normal of model space to world space, the following are equivalent 2 methods //o.normal = normalize(< span class="hljs-keyword">mul(v.normal, _World2Object)); o.normal = UnityObjectToWorldNormal(v.normal);  // Take the direction of the light, the following are equivalent 2 methods //o.lightDir = normalize(_WorldSpaceLightPos0.xyz); o. lightDir = ObjSpaceLightDir(v.vertex); o.lightDir = normalize(mul(_Object2World, o.lightDir)); return o;} fixed4 frag (v2f i): COLOR {// Ambient light float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb; // diffuse reflection fixed diff = max (0, dot(i.normal, i.lightDir)); float3 diffCol = _LightColor0.rgb * diff; // final color synthesis float4 col; col = float4(ambientLight + diffCol, 1) * _Color; return col;} ENDCG} }}
// Decal (that is, paste a texture on top of another) shader, corresponding to Legacy Shaders/DecalSh ader "Z_TestShader/TestDecal"{ Properties {_Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2 D) = "white" {} _DecalTex ("Decal (RGBA)", 2D) = "black" {}} SubShader {Tags {"RenderType" ="Opaque"} LOD 250 Pass {Tags {"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG. cginc"  #include "Lighting.cginc" uniform sampler2D _MainTex; uniform sampler2D _DecalTex; uniform fixed4 _Color; float4 _MainTex_ST; float4 _DecalTex_ST ; struct v2f {float4 pos: SV_POSITION; float2 uv_MainTex: TEXCOORD0; float2 uv_DecalTex: TEXCOORD1; float3 normal: NORMAL; float3 lightDir: TEXCOORD3; }; v2f vert (appdata_full v) {v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); // Use material Tiling, Offset 2One parameter to calculate the texture uv o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex); o.uv_DecalTex = TRANSFORM_TEX(v.texcoord , _DecalTex); //o.normal = normalize( mul(v.normal, _World2Object)); o.normal = UnityObjectToWorldNormal(v< span class="hljs-preprocessor">.normal); //o.lightDir = normalize(_WorldSpaceLightPos0.xyz); o.lightDir< /span> = ObjSpaceLightDir(v.vertex); o .lightDir = normalize(mul(_Object2World, o.lightDir)); return o;} fixed4 frag (v2f i): COLOR {// decal algorithm float4 tex = tex2D(_M ainTex, i.uv_MainTex); float4 decal = tex2D(_DecalTex, i.uv_DecalTex); tex.rgb = lerp (tex.rgb, decal.rgb, decal.a ); tex *= _Color; float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT. rgb; fixed diff = max (0, dot(i.normal, i.lightDir)); float3 diffCol = _LightColor0< span class="hljs-preprocessor">.rgb * diff; // The final color synthesis float4 col; col = float4(ambientLight + diffCol, 1) * tex; return col;} ENDCG} }} pre> 

Note: One more thing, if you want to compare the effect with the surface shader in a scene, remember to set the Ambient Source option of the ambient light in the Window->Lighting interface, from SkyBox was changed to Color, because I found that in SkyBox mode (that is, the ambient light comes from the sky box), the ambient light obtained by the macro UNITY_LIGHTMODEL_AMBIENT is not accurate, at least it is different from the ambient light obtained by the surface shader. Maybe if you want to get the same ambient light, you need to go through some processing. So if you want the two shaders to have the same effect, remember to change this.

After experiencing it by myself, I discovered that the surface shader does hide a lot of internal implementations. For issues like multiple light sources, shadows, attenuation, etc. that I didn’t consider, they are all hidden and implemented in surface shader, and it’s still compatible with multiple platforms, Vertex & Fragment Shader< /code> You have to write it yourself. Fortunately, Unity also provides us with some ready-made methods to adjust. _(:зゝ∠)_

So to summarize, when writing materials that need to receive complex light source information, writing surface shader should be simple A lot.
For materials that do not receive light or very little light, Vertex & Fragment Shader should be clearer and easier to understand, and faster.
Unity's surface shader technology can save us a lot of things and increase the development speed.

Note: The following two shaders only consider the situation where there is only one parallel light, and do not consider multiple light sources, shadows, attenuation and other issues.

The implementation of surface shader can be found in the official shader resource resources. The following code can be optimized, here In order to make the steps clear, we divided some more variables

// Diffuse shader, corresponding to Legacy Shaders/DiffuseShader "Z_TestShader/TestDiffuse"{ Properties {_Color ("Color", Color) = (1,1,1,1)} SubShader {Tags{ "RenderType" = "Opaque"} Pass {Tags {"LightMode"  = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag // Forward rendering compilation instruction#pragma multi_compile_fwdbase #include "UnityCG.cginc"  #include "Lighting.cginc"  uniform float4 _Color; struct v2f {float4 pos: SV_POSITION; float3 normal: NORMAL;< /span> float3 lightDir: TEXCOORD0; }; v2f vert (appdata_full v) {v2f o; o.pos = mul(UNITY_MATRIX_MVP, v< span class="hljs-preprocessor">.vertex); // Turn the model normal of model space to world space, the following are equivalent 2 methods //o.normal = normalize(mul(v.normal, _World2Object)); o.normal = UnityObjectToWorldNormal(v.normal);  // Take the direction of the light, the following are equivalent 2 methods //o.lightDir = normalize(_WorldSpaceLightPos0.xyz); o. lightDir = ObjSpaceLightDir(v.vertex); o.lightDir = normalize(mul(_Object2World, o.lightDir)); return o; } fixed4 frag (v2f i): COLOR {// Ambient light float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT.rgb; / / Diffuse fixed diff = max (0, dot(i.normal, i.lightDir)); float3 diffCol = _LightColor0.rgb * diff ; // The final color synthesis float4 col; col = float4(ambientLight + diffCol, 1) * _Color; return col;} ENDCG} }} 
// Decal (that is, paste a texture on top of another) shader, corresponding to Legacy Shaders/DecalShade r "Z_TestShader/TestDecal"{ Properties {_Color ("Main Color", Color) = (1,1,1,1) _MainTex ("Base (RGB)", 2 D) = "white" {} _DecalTex ("Decal (RGBA)", 2D) = "black" {}} SubShader {Tags {"RenderType" ="Opaque"} LOD 250 Pass {Tags {"LightMode" = "ForwardBase"} CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma multi_compile_fwdbase #include "UnityCG. cginc"  #include "Lighting.cginc" uniform sampler2D _MainTex; uniform sampler2D _DecalTex; uniform fixed4 _Color; float4 _MainTex_ST; float4 _DecalTex_ST ; struct v2f {float4 pos: SV_POSITION; float2 uv_MainTex: TEXCOORD0; float2 uv_DecalTex: TEXCOORD1; float3 normal: NORMAL; float3 lightDir: TEXCOORD3; }; v2f vert (appdata_full v) {v2f o; o.pos = mul(UNITY_MATRIX_MVP, v.vertex); // Use material Tiling, Offset 2One parameter to calculate the texture uv o.uv_MainTex = TRANSFORM_TEX(v.texcoord, _MainTex); o.uv_DecalTex = TRANSFORM_TEX(v.texcoord , _DecalTex); //o.normal = normalize( mul(v.normal, _World2Object)); o.normal = UnityObjectToWorldNormal(v< span class="hljs-preprocessor">.normal); //o.lightDir = normalize(_WorldSpaceLightPos0.xyz); o.lightDir< /span> = ObjSpaceLightDir(v.vertex); o .lightDir = normalize(mul(_Object2World, o.lightDir)); return o;} fixed4 frag (v2f i): COLOR {// decal algorithm float4 tex = tex2D(_Main Tex, i.uv_MainTex); float4 decal = tex2D(_DecalTex, i.uv_DecalTex); tex.rgb = lerp (tex.rgb, decal.rgb, decal.a ); tex *= _Color; float3 ambientLight = UNITY_LIGHTMODEL_AMBIENT. rgb; fixed diff = max (0, dot(i.normal, i.lightDir)); float3 diffCol = _LightColor0< span class="hljs-preprocessor">.rgb * diff; // Final color synthesis float4 col; col = float4(ambientLight + diffCol,  1) * tex; return col;} ENDCG} }}

< p>Note: One more thing, if you want to compare the effect with the surface shader in a scene, remember to change the Ambient Source option of the ambient light from SkyBox to the Window->Lighting interface Color, because I found that in SkyBox mode (that is, the ambient light comes from the sky box), the ambient light obtained by the macro UNITY_LIGHTMODEL_AMBIENT is not accurate, at least it is different from the ambient light obtained by the surface shader. Maybe if you want to get the same ambient light, you need to go through some processing. So if you want the two shaders to have the same effect, remember to change this.

Leave a Comment

Your email address will not be published.