1. 程式人生 > >【Unity Shaders】Diffuse Shading——漫反射光照改善技巧

【Unity Shaders】Diffuse Shading——漫反射光照改善技巧

這裡是本書所有的插圖。這裡是本書所需的程式碼和資源(當然你也可以從官網下載)。

========================================== 分割線 ==========================================



上一篇中,我們演示瞭如何使用自定義的光照模型進行渲染。這一次,我們將進一步看一下怎樣對它做一些變化來得到更好的效果!

我們會列出兩種方法:使用Half Lambert lighting model(半蘭伯特光照模型)和使用一個ramp texture來控制diffuse shading。

準備工作

同樣,我們需要你已經做好了上一篇文章中的內容,並得到了如下shader:
Shader "Custom/BasicDiffuse" {
	Properties {
		_EmissiveColor ("Emissive Color", Color) = (1,1,1,1)
		_AmbientColor  ("Ambient Color", Color) = (1,1,1,1)
		_MySliderValue ("This is a Slider", Range(0,10)) = 2.5
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
        LOD 200
        CGPROGRAM
        #pragma surface surf BasicDiffuse
        
       	//We need to declare the properties variable type inside of the
        //CGPROGRAM so we can access its value from the properties block.
        float4 _EmissiveColor;
        float4 _AmbientColor;
        float _MySliderValue;
        
        struct Input
        {
          	float2 uv_MainTex;
        };
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            //We can then use the properties values in our shader
            float4 c;
            c =  pow((_EmissiveColor + _AmbientColor), _MySliderValue);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
       	{
       	  	float difLight = max(0, dot (s.Normal, lightDir));
       	  	float4 col;
       	  	col.rgb = s.Albedo * _LightColor0.rgb * (difLight * atten * 2);
       	  	col.a = s.Alpha;
       	  	return col;
		}
        
		ENDCG
	} 
	FallBack "Diffuse"
}

建立一個Half Lambert lighting model(半蘭伯特光照模型)

如果你看過之前的文章中,相信還記得Lambert這個名字。沒錯,它就是Unity預設的diffuse lighting model。簡單來說,Lambert定律認為,在平面某點漫反射光的光強與該反射點的法向量和入射光角度的餘弦值成正比(即我們之前使用dot函式得到的結果)。Half Lambert最初是由Valve(遊戲半條命2使用的引擎即是其開發的)提出來,用於提高物體在一些光線無法照射到的區域的亮度的。簡單說來,它提高了漫反射光照的亮度,使得漫反射光線可以看起來照射到一個物體的各個表面。而Half Lambert最初也是被用於遊戲半條命的畫面渲染,為了防止某個物體的背光面丟失形狀並且顯得太過平面化。這個技術是完全沒有基於任何物理原理的,而僅僅是一種感性的視覺增強(參考
這裡
)。
好啦,說了這麼多還是要演示一下,程式碼非常簡單!我們只需要稍微更改上述的LightingBasicDiffuse函式:
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
       	{
       	  	float difLight = max(0, dot (s.Normal, lightDir));
       	  	// Add this line
       	  	float hLambert = difLight * 0.5 + 0.5;
       	  	
       	  	float4 col;
       	  	// Modify this line
       	  	col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
       	  	col.a = s.Alpha;
       	  	return col;
	}

由程式碼可以看出,我們定義了一個新的變數hLambert來替換difLight用於計算某點的顏色值。difLight的範圍是0.0-1.0,而通過hLambert,我們將結果由0.0-1.0對映到了0.5-1.0,從而達到了增加亮度的目的。下圖顯示了這一變化:
我們可以通過對比來看一下Lambert和Half Lambert的渲染區別(分別對應左圖和右圖):

建立一個ramp texture來控制diffuse shading

下面介紹另一種簡單實用的方法——使用一張ramp texture(漸變圖)來控制漫反射光照的顏色。這允許你著重強調surface的顏色而減弱漫反射光線或者其他更高階光線的影響。 可以在很多卡通風格的遊戲中看到這種技術,通常在這些情況下你需要一個更加藝術而非寫實風格的畫面,並且不需要很多的真實物理模擬的光照模型。 這個技術在Team Fortress 2(軍團要塞2)中流行起來,這個技術也是由Valve提出來用於渲染他們的遊戲角色的。他們發表了一個非常有名的論文,強烈建議你應該讀一下它!這篇論文講解了軍團要塞2中使用的光照和渲染技術。 上程式碼! 我們重新修改LightingBasicDiffuse函式,增加一個新的變數ramp:
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
       	{
       	  	float difLight = max(0, dot (s.Normal, lightDir));
       	  	float hLambert = difLight * 0.5 + 0.5;
       	  	float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb;
       	  	
       	  	float4 col;
       	  	col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
       	  	col.a = s.Alpha;
       	  	return col;
	}

其中,我們還需要一張texture,即_RampTex。即之前說到的漸變圖。回憶一下,為了能夠在Inspector中拖拽一個texture,並在shader中使用需要怎麼做?首先,我們需要在Properties塊中宣告它,然後在SubShader中宣告一個相同名字的變數,並制定它的型別,之後就可以在函式中訪問它啦!忘記的請翻看之前的幾篇文章。完整的程式碼如下:
Shader "Custom/RampDiffuse" {
	Properties {
		_EmissiveColor ("Emissive Color", Color) = (1,1,1,1)
		_AmbientColor  ("Ambient Color", Color) = (1,1,1,1)
		_MySliderValue ("This is a Slider", Range(0,10)) = 2.5
		// Add this line
		_RampTex ("Ramp Texture", 2D) = "white"{}
	}
	SubShader {
		Tags { "RenderType"="Opaque" }
        LOD 200
        
        CGPROGRAM
        #pragma surface surf BasicDiffuse
        
       	//We need to declare the properties variable type inside of the
        //CGPROGRAM so we can access its value from the properties block.
        float4 _EmissiveColor;
        float4 _AmbientColor;
        float _MySliderValue;
        // Add this line
        sampler2D _RampTex;
        
        struct Input
        {
          	float2 uv_MainTex;
        };
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            //We can then use the properties values in our shader
            float4 c;
            c =  pow((_EmissiveColor + _AmbientColor), _MySliderValue);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        
        inline float4 LightingBasicDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten)
       	{
       	  	float difLight = max(0, dot (s.Normal, lightDir));
       	  	float hLambert = difLight * 0.5 + 0.5;
       	  	// Add this line
       	  	float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb;
       	  	
       	  	float4 col;
       	  	// Modify this line
       	  	col.rgb = s.Albedo * _LightColor0.rgb * (ramp);
       	  	col.a = s.Alpha;
       	  	return col;
		}
        
		ENDCG
	} 
	FallBack "Diffuse"
}
使用的ramp texture(漸變圖)如下:
其中最重要的程式碼只有一行:
float3 ramp = tex2D(_RampTex, float2(hLambert)).rgb;

這行程式碼返回一個rgb值。tex2D函式接受兩個引數:第一個引數是操作的texture,第二個引數是需要取樣的UV座標。這裡,我們並不像使用一個vertex來代表一個UV座標進行取樣,而僅僅想使用一個漫反射浮點值(即hLambert)來對映到漸變圖上的某一個顏色值。最後得到的結果便是,我們將會根據計算得到的Half Lambert光照值來決定光線照射到一個物體表面的顏色變化。
我們再來對比看一下Half Lambert和添加了ramp texture控制後的渲染區別(分別對應左圖和右圖):

結束語

Diffuse Shader還有最後一篇文章就會階段性結束了。通過這一些文章,相信已經對Unity Shaders有了一個大致的瞭解。呼,作為一個初學者,現在的渲染結果可能看起來還狠簡陋,但是一口氣吃個胖子是不現實的!呼呼,加油!相信學習這些對遊戲渲染還是很有幫助的,畢竟每一個出色遊戲幾乎全部都使用了自己編寫的shader,希望自己以後也可以有所創新,可以為遊戲增光添彩。