標籤:shader unity 移動平台
本系列主要參考《Unity Shaders and Effects Cookbook》一書(感謝原書作者),同時會加上一點個人理解或拓展。
這裡是本書所有的插圖。這裡是本書所需的代碼和資源(當然你也可以從官網下載)。
========================================== 分割線 ==========================================
寫在前面
在上一篇裡,我們學習了一些技巧來初步最佳化Shader。這次,我們學習更多的技術來實現一個更複雜的Shader:Normal-Mapped Specular Shader。這些技術包括:使用光照函數的兩個新變數halfasview或者approxview,減少使用的貼圖數量,以及對貼圖進行更好的壓縮。
準備工作
- 建立一個新的情境和一個球體,添加一個平行光。
- 建立一個新的Shader和Material,可以命名為MobileShader。
- 把Shader賦給Material,把Material賦給球體。
實現
- 首先,還是修改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; // 該像素的不透明度 };
- 下面是建立#pragma聲明。這可以控制Surface Shader各屬性的開關,使得Shader更高效或者更低效:
CGPROGRAM#pragma surface surf MobileBlinnPhong exclude_path:prepass nolightmap noforwardadd halfasview
解釋:忽略延遲光照,不支援光照貼圖,只接受一個單一的平行光光源作為逐像素光源。最後,使用halfasview聲明告訴Unity,我們使用一個介於光照方向和觀察方向之間的half vector來代替真正的觀察方向viewDir來計算光照函數。這將加速Shader的處理時間,因為這是基於逐頂點而非逐像素計算而得的。雖然這樣得到的結果是近似值,但對於移動平台來說足夠了。
- 建立和Properties塊中各變數的聯絡。和之前不同,我們這次使用fixed來得到高光指數滑條的值:
sampler2D _Diffuse;sampler2D _NormalMap;fixed _SpecIntensity;
- 得到貼圖的UV座標。在上一篇就提過,為了節省變數空間,我們僅使用一個UV值:
struct Input {half2 uv_Diffuse;};
- 由於我們在聲明中添加了新的變數,我們可以在光照函數中使用新的參數:
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;}
- 最後,我們在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只工作在特定的渲染器上,使用近似值代替精確值,以及減少或壓縮貼圖。