[Unity Shader] Half Lambert and Ramp Texture Diffuse Shading

Recently I am reading the book “Unity Shaders and Effects Cookbook” to learn the basic ideas of Unity Shader. And I would like to share two frequently used shaders with you guys today, which are Half Lambert and Ramp Texture Diffuse Shading.

/*****************************************  Normal Diffuse Shader  *****************************************/

First, let’s see the basic shader script named “BasicDisffuseShader”. And here is the code:

  1. Shader “Custom/BasicDiffuse” {  
  2.     Properties {  
  3.         _EmissiveColor (“Emissive Color”, Color) = (1,1,1,1)  
  4.         _AmbientColor  (“Ambient Color”, Color) = (1,1,1,1)  
  5.         _MySliderValue (“This is a Slider”, Range(0,10)) = 2.5  
  6.     }  
  7.     SubShader {  
  8.         Tags { “RenderType”=”Opaque” }  
  9.         LOD 200  
  10.         CGPROGRAM  
  11.         #pragma surface surf BasicDiffuse  
  12.           
  13.         //We need to declare the properties variable type inside of the  
  14.         //CGPROGRAM so we can access its value from the properties block.  
  15.         float4 _EmissiveColor;  
  16.         float4 _AmbientColor;  
  17.         float _MySliderValue;  
  18.           
  19.         struct Input  
  20.         {  
  21.             float2 uv_MainTex;  
  22.         };  
  23.           
  24.         void surf (Input IN, inout SurfaceOutput o)  
  25.         {  
  26.             //We can then use the properties values in our shader  
  27.             float4 c;  
  28.             c =  pow((_EmissiveColor + _AmbientColor), _MySliderValue);  
  29.             o.Albedo = c.rgb;  
  30.             o.Alpha = c.a;  
  31.         }  
  32.           
  33.         inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)  
  34.         {  
  35.             float difLight = max(0, dot (s.Normal, lightDir));  
  36.             float4 col;  
  37.             col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);  
  38.             col.a = s.Alpha;  
  39.             return col;  
  40.         }  
  41.           
  42.         ENDCG  
  43.     }   
  44.     FallBack “Diffuse”  
  45. }  

Now the effect of current shader is like this:

http://www.douban.com/photos/photo/2192545018/large

/*****************************************  Half Lambert Diffuse Shader  *****************************************/

Half Lambert Diffuse Shader

Before we move to Half Lambert Diffuse Shader, let me give you a brief introduction of it:

“Half Lambert” lighting is a technique first developed in the original Half-Life. It is designed to prevent the rear of an object losing it’s shape and looking too flat. Half Lambert is a completely non-physical technique and gives a purely percieved visual enhancement and is an example of a forgiving lighting model.

In the book “Unity Shaders and Effects Cookbook”, it says:

The Half Lambert technique works by taking the range of values of the diffuse lighting, dividing it in half, and then adding 0.5 back to it. This basically means that if you have a value of 1 and you cut it in half, you will have 0.5. Then, if you add 0.5 back to it, you will have 1 again. If you did this same operation to a value of 0, you would end up with 0.5. So, we have taken a range of values from 0 to 1 and re-mapped it to be within a range of 0.5 to 1.0.

Here is the new code:

  1.   inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)  
  2.         {  
  3.             float difLight = max(0, dot (s.Normal, lightDir));  
  4.             // Add this line  
  5.             float hLambert = difLight * 0.5 + 0.5;  
  6.               
  7.             float4 col;  
  8.             // Modify this line  
  9.             col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);  
  10.             col.a = s.Alpha;  
  11.             return col;  
  12. }  

Pay attention to the 5th line: float hLambert = difLight * 0.5 + 0.5;

In origin, the value of difLight is from -1 to 1. When the value is smaller than zero, the output image could be dark. In order to avoid the object’s edge losing and flat looking, we set the value difLight in the range of 0 to 1. This is the function of 5th line. Half Lambert lighting can most often be seen being used on the characters face materials. It is enabled by the use of $halflambert within a materials VMT file.

Here is the result:

http://www.douban.com/photos/photo/2192545032/large

/***************************************** Ramp Texture Diffuse Shader  *****************************************/

Ramp Texture Diffuse Shader

For this shader, you can use a ramp texture to drive the color of the diffuse lighting. This allows to accentuate the surface’s colors to fake the effects of more bounce light or a more advanced lighting setup. You can see this technique used a lot more for cartoony games, especially after it becomes popualr with Team Fortress 2, where Valve came up with a unique approach to lighting their characters. PS: the Valve White Paper on Team Fortress 2 Lighting and shading avilable at :http://www.valvesoftware.com/publications/2007/NPAR07_IllustrativeRenderingInTeamFortress2.pdf

Here is the new code:

  1. Shader “Custom/RampDiffuse” {  
  2.     Properties {  
  3.         _EmissiveColor (“Emissive Color”, Color) = (1,1,1,1)  
  4.         _AmbientColor  (“Ambient Color”, Color) = (1,1,1,1)  
  5.         _MySliderValue (“This is a Slider”, Range(0,10)) = 2.5  
  6.         // Add this line  
  7.         _RampTex (“Ramp Texture”, 2D) = “white”{}  
  8.     }  
  9.     SubShader {  
  10.         Tags { “RenderType”=”Opaque” }  
  11.         LOD 200  
  12.           
  13.         CGPROGRAM  
  14.         #pragma surface surf BasicDiffuse  
  15.           
  16.         //We need to declare the properties variable type inside of the  
  17.         //CGPROGRAM so we can access its value from the properties block.  
  18.         float4 _EmissiveColor;  
  19.         float4 _AmbientColor;  
  20.         float _MySliderValue;  
  21.         // Add this line  
  22.         sampler2D _RampTex;  
  23.           
  24.         struct Input  
  25.         {  
  26.             float2 uv_MainTex;  
  27.         };  
  28.           
  29.         void surf (Input IN, inout SurfaceOutput o)  
  30.         {  
  31.             //We can then use the properties values in our shader  
  32.             float4 c;  
  33.             c =  pow((_EmissiveColor + _AmbientColor), _MySliderValue);  
  34.             o.Albedo = c.rgb;  
  35.             o.Alpha = c.a;  
  36.         }  
  37.           
  38.         inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)  
  39.         {  
  40.             float difLight = max(0, dot (s.Normal, lightDir));  
  41.             float hLambert = difLight * 0.5 + 0.5;  
  42.             // Add this line  
  43.             float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb;  
  44.               
  45.             float4 col;  
  46.             // Modify this line  
  47.             col.rgb = s.Albedo * _LightColor0.rgb * (ramp);  
  48.             col.a = s.Alpha;  
  49.             return col;  
  50.         }  
  51.           
  52.         ENDCG  
  53.     }   
  54.     FallBack “Diffuse”  
  55. }  

Pay attention to line 43:  float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb; it takes the re-mapped diffuse values from the Half Lambert operation and pass them into float2() to create the lookup values for the texture. When a value of 0 is set as the hLambert variable, the tex2D function looks up the pixel value at the UV value of (0,0).

Here is the effect:

http://www.douban.com/photos/photo/2192545046/large

At Last

These are the first two shaders I have ever touched. Although they are normal and simple, we can see their contribution to the game industry, especially you can find them in Half Life and Team Fortress! There is still a long way for me to move on and I am so glad I am on my way.