1. 程式人生 > >【Unity Shaders】Mobile Shader Adjustment —— 為手機定製Shader

【Unity Shaders】Mobile Shader Adjustment —— 為手機定製Shader

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

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

寫在前面

上一篇裡,我們學習了一些技巧來初步優化Shader。這次,我們學習更多的技術來實現一個更復雜的Shader:Normal-Mapped Specular Shader。這些技術包括:使用光照函式的兩個新變數halfasview或者approxview,減少使用的貼圖數量,以及對貼圖進行更好的壓縮。

準備工作

  1. 建立一個新的場景和一個球體,新增一個平行光。
  2. 建立一個新的Shader和Material,可以命名為MobileShader。
  3. 把Shader賦給Material,把Material賦給球體。

實現

  1. 首先,還是修改Properties塊。本節我們需要一張diffuse貼圖,它的alpha通道值對應畫素的光滑度(Gloss);以及一張法線貼圖,和高光指數的滑動條。
    	Properties {
    		_Diffuse ("Base (RGB) Specular Amount (A)", 2D) = "white" {}
    		_NormalMap ("Normal Map", 2D) = "bump"{}
    		_SpecIntensity ("Specular Width", Range(0.01, 1)) = 0.5
    	}

    解釋:一直沒有徹底搞懂Unity SurfaceOutput裡面各變數的計算細節。這裡再詳細解釋下。SurfaceOutput裡面的內建變數可以見這篇,如下:
    struct SurfaceOutput {  
        half3 Albedo;      // 該畫素的反射率,反應了畫素的基色 
        half3 Normal;     // 該畫素的法線方向
        half3 Emission;   // 該畫素的自發光顏色,使得即便沒有光照也可以物體本身也可以發出光
        half Specular;     // 該畫素的高光指數  
        half Gloss;         // 該畫素的高光光滑度,值越大高光反射越清晰,反之越模糊  
        half Alpha;         // 該畫素的不透明度 
    };

  2. 下面是建立#pragma宣告。這可以控制Surface Shader各屬性的開關,使得Shader更高效或者更低效:
    		CGPROGRAM
    		#pragma surface surf MobileBlinnPhong exclude_path:prepass nolightmap noforwardadd halfasview

    解釋忽略延遲光照,不支援光照貼圖,只接受一個單一的平行光光源作為逐畫素光源。最後,使用halfasview宣告告訴Unity,我們使用一個介於光照方向和觀察方向之間的half vector來代替真正的觀察方向viewDir來計算光照函式。這將加速Shader的處理時間,因為這是基於逐頂點而非逐畫素計算而得的。雖然這樣得到的結果是近似值,但對於移動平臺來說足夠了。

  3. 建立和Properties塊中各變數的聯絡。和之前不同,我們這次使用fixed來得到高光指數滑條的值:
    		sampler2D _Diffuse;
    		sampler2D _NormalMap;
    		fixed _SpecIntensity;

  4. 得到貼圖的UV座標。在上一篇就提過,為了節省變數空間,我們僅使用一個UV值:
    		struct Input 
    		{
    			half2 uv_Diffuse;
    		};

  5. 由於我們在宣告中添加了新的變數,我們可以在光照函式中使用新的引數:
    		inline fixed4 LightingMobileBlinnPhong (SurfaceOutput s, fixed3 lightDir, fixed3 halfDir, fixed atten)
    		{
    			fixed diff = max (0, dot (s.Normal, lightDir));
    			fixed nh = max (0, dot (s.Normal, halfDir));
    			fixed spec = pow (nh, s.Specular * 128) * s.Gloss;
    			
    			fixed4 c;
    			c.rgb = (s.Albedo * _LightColor0.rgb * diff + _LightColor0.rgb * spec) * (atten * 2);
    			c.a = 0.0;
    			return c;
    		}

  6. 最後,我們在surf函式中完成對畫素顏色的計算:
    		void surf (Input IN, inout SurfaceOutput o) 
    		{
    			fixed4 diffuseTex = tex2D (_Diffuse, IN.uv_Diffuse);
    			o.Albedo = diffuseTex.rgb;
    			o.Gloss = diffuseTex.a;
    			o.Alpha = 0.0;
    			o.Specular = _SpecIntensity;
    			o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_Diffuse));
    		}


最後,得到的效果如下:

解釋

我們最後總結一下使用過的所有技術:優化變數型別,共享UV座標,減少處理的光源個數,讓Shader只工作在特定的渲染器上,使用近似值代替精確值,以及減少或壓縮貼圖。