1. 程式人生 > >UnityShader入門精要-3.5 UnityShader的形式

UnityShader入門精要-3.5 UnityShader的形式

移動平臺 cnblogs float material ren 1.5 targe tar get

UnityShader可以做的事情非常多(例如設置渲染狀態等),但是其最重要的任務還是指定各種著色器所需的代碼。這些著色器代碼可以寫在SubShader語義塊中(表面著色器的做法),也可以寫在Pass語義塊中(定點/片元著色器和固定函數著色器的做法)。

在Unity中,我們可以使用下面3中形式來編寫UnityShader。而不管使用哪種形式,真正意義上的Shader代碼都需要包含在ShaderLab語義塊中,如下所示:

Shader "MyShader"{
  Properties{
    //所需的各種屬性
  }
  SubShader{
    //真正意義上的Shader代碼會出現在這裏
    
//表面著色器(Surface Shader)或者 //定點/片元著色器(Vertex/Fragment Shader)或者 // 固定函數著色器 (Fixed Function Shader) } SubShader{ //和上一個SubShader類似 } }
  1. 表面著色器

表面著色器(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的形式