UnityShader入門精要-3.5 UnityShader的形式
UnityShader可以做的事情非常多(例如設置渲染狀態等),但是其最重要的任務還是指定各種著色器所需的代碼。這些著色器代碼可以寫在SubShader語義塊中(表面著色器的做法),也可以寫在Pass語義塊中(定點/片元著色器和固定函數著色器的做法)。
在Unity中,我們可以使用下面3中形式來編寫UnityShader。而不管使用哪種形式,真正意義上的Shader代碼都需要包含在ShaderLab語義塊中,如下所示:
Shader "MyShader"{ Properties{ //所需的各種屬性 } SubShader{ //真正意義上的Shader代碼會出現在這裏//表面著色器(Surface Shader)或者 //定點/片元著色器(Vertex/Fragment Shader)或者 // 固定函數著色器 (Fixed Function Shader) } SubShader{ //和上一個SubShader類似 } }
- 表面著色器
表面著色器(Surface Shader)是Unity自己創造的一種著色器代碼類型。它需要的代碼量很小,Unity在背後做了很多工作,但渲染的代價比較大。它在本質上和下面要講到的定點/片元著色器是一樣的。也就是說,當Unity提供一個表面著色器的時候,它在背後仍舊把它轉換成對應的頂點/片元著色器。我們可以理解成,表面著色器是Unity對頂點/片元著色器的更高一層的抽象。它存在的價值在於,Unity為我們處理了很多光照細節,使得我們不需要操心這些“煩人的事情”。
一個非常簡單的表面著色器的示例代碼如下:
Shader "Custom/Simple Surface Shader"{ SubShader{ Tags{ "RenderType" = "Opaque"} CGPROGRAM #prama surface surf Lambert struct Input{ float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o){ o.Albedo=1; } ENDCG } FallBack "Diffuse" }
從上述程序中可以看出,表面著色器被定義在SubShader語義塊(而非Pass語義塊)中的CGPROGAM和ENDCG之間。原因是,表面著色器不需要開發者關心使用多少個Pass、每個Pass如何渲染等問題,Unity會在背後為我們做好這些事情,我們只要告訴他:“嘿!使用這些紋理去填充顏色,使用這個法線紋理去填充法線,使用Lambert光照模型,其他的事情不要來煩我!”。
CGPRORAM和ENDCG之間的代碼使用CG/HLSL編寫的,也就是說,我們需要把CG/HLSL語言嵌套在ShaderLab語言中。值得註意的是,這裏的CG/HLSL是Unity經封裝後提供的,他的語法和標準的CG/HLSL語法幾乎一樣,但還是有細微的不同,例如有些原生的函數和用法Unity並沒有提供支持。
2。最聰明的孩子:定點/片元著色器
在Unity中我們可以使用CG/HLSL語言來編寫 頂點/片元著色器(Verter/Fragment Shader)。它們更加復雜,但靈活性也更高。
一個非常簡單的頂點/片元著色器示例代碼如下:
Shader "Custom/Simple VertexFragment Shader"{ SubShader{ Pass{ CGPROGRAM #prama vertex vert #prama fragment frag float4 vert (float4 v : POSITION) : SV_POSITION{ return mul (UNITY_MATRIX_MVP , v); } float4 frag ( ) : SV_Target{ return fixed4 (1.0 , 0.0 , 0.0 , 1.0); } ENDCG } } }
和表面著色器類似,頂點/片元著色器的代碼也需要定義在CGPROGRAM和ENDCG之間,但不同的是,頂點/片元著色器是寫在Pass語義塊內,而非SubShader內的,原因是,我們需要自已定義每個Pass需要使用的Shader代碼。雖然我們可能需要編寫更多的代碼,但帶來的好處是靈活性很高,更重要的是,我們可以控制渲染的實現細節,同樣這裏的CGPROGRAM和ENDCG之間的代碼也是使用CG/HLSL編寫的。
3. 被拋棄的角落:固定函數著色器
上面兩種Unity Shader 形式都使用了可編程管線。而對於一些較舊的設備(其CPU僅支持DirectX7.0、OpenGL 1.5 或 OpenGL ES 1.1),例如iPhone3,他們不支持可編程管線著色器,因此,這時候我們就需要使用固定函數著色器(Fixed Function Shader)來完成渲染。這些著色器往往可以完成一些非常簡單的效果。
一個非常簡單的固定函數著色器示例代碼如下:
Shader "Tutorial/Basic"{ Properties{ _Color("Main Color",Color) = (1, 0.5, 0.5 , 1) } SubShader{ Pass{ Material { Diffuse [ _Color] } Lighting On } } }
可以看出,固定函數著色器的代碼被定義在Pass語義塊中,這些代碼相當於Pass中的一些渲染設置,正如我們之前提到的一樣。
對於固定函數著色器來說,我們需要完全使用ShaderLab的語法(即 使用ShaderLab的渲染設置命令) 來編寫,而非使用 CG/HLSL。
由於現在大多數GPU都支持可編程的渲染管線,這種固定管線的編程方式已經逐漸被拋棄。實際上,在Unity5.2中,所有固定函數著色器都會在背後被Unity編譯成對應的頂點/片元著色器,因此真正意義上的固定函數著色器已經不存在了。
對於選擇Shader 的一些建議。
- 除非你有非常明確的需求必須要使用固定函數著色器,例如需要在非常舊的設備上運行你的遊戲(這些設備非常少見),否則請使用可編程管線的著色器,即表明著色器或頂點/片元著色器。
- 如果你想和各種光源打交道,你可能更喜歡使用表明著色器,但需要小心他在移動平臺的性能表現。
- 如果你需要使用的光照數目非常少,例如只有一個平行光,那麽使用頂點/片元著色器是一個更好的選擇。
- 最重要的是如果你有很多自定義的渲染效果,那麽請選擇頂點/片元著色器。
UnityShader入門精要-3.5 UnityShader的形式