Unity3D中的Shader

簡單的說,Shader是爲渲染管線中的特定處理階段提供算法的一段代碼。Shader是伴隨着可編程渲染管線出現的,從而能夠對渲染過程加以控制。算法

1. Unity提供了不少內建的Shader,這些能夠從官網下載,打開looking for older version的連接就能看到Build-in shaders。選擇合適的Shader很重要,如下是開銷從低到高的排序:編程

(1)Unlit:僅使用紋理顏色,不受光照影響app

(2)VertexLit:頂點光照dom

(3)Diffuse:漫反射函數

(4)Specular:在漫反射基礎上增長高光計算優化

(5)Normal mapped:法線貼圖,增長了一張法線貼圖和幾個着色器指令ui

(6)Normal Mapped Specular:帶高光法線貼圖spa

(7)Parallax Normal Mapped:視差法線貼圖,增長了視差貼圖的計算開銷code

(8)Parallax Normal Mapped Specular:帶高光視差法線貼圖orm

  對於如今流行的移動平臺遊戲,Unity提供了幾種專門的着色器放在Shader->Mobile下,它們是專門優化過的。

2. 在Unity中,能夠編寫3種類型的Shader:

表面着色器(Surface Shaders):最經常使用的Shader,能夠與燈光、陰影、投影器交互,以Cg/HLSL語言進行編寫,不涉及光照時儘可能不要使用。

頂點和片斷着色器(Vertex and Fragment Shaders):全屏圖像效果,代碼比較多,以Cg/HLSL編寫,難以和光照交互。

固定功能管線着色器(Fixed Function Shaders):遊戲要運行在不支持可編程管線的老舊機器上時,須要用ShaderLab語言來編寫。

不管編寫哪一種Shader,實際的Shader代碼都須要嵌入ShaderLab代碼中,Unity經過ShaderLab代碼來組織Shader結構。

下面是我新建的一個Shader的默認內容:

Shader "Custom/TestShader" { Properties { _MainTex ("Base (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }

Properties:用來定義着色器中使用的貼圖資源或者數值參數等,這裏定義了一個Base (RGB)的2D紋理。

SubShader:一個着色器包含的一個或多個子着色器。Unity從上到下遍歷子着色器,找到第一個能被設備支持的着色器。

FallBack:備用着色器,一個對硬件要求最低的Shader名字。

(1)Properties定義的屬性

名稱("顯示名稱", Vector) = 默認向量值,一個四維向量 名稱("顯示名稱", Color) = 默認顏色值,一個顏色(取值0~1的四維向量)屬性 名稱("顯示名稱", Float) = 默認浮點數值,一個浮點數 名稱("顯示名稱", Range(min,max)) = 默認浮點數值,一個浮點數,取值min~max 名稱("顯示名稱", 2D) = 默認貼圖名稱{選項},一個2D紋理屬性 名稱("顯示名稱", Rect) = 默認貼圖名稱{選項},一個矩形紋理屬性(非2的n次冪) 名稱("顯示名稱", Cube) = 默認貼圖名稱{選項},一個立方體紋理屬性

選項指的是一些紋理的可選參數,包括:

TexGen:紋理生成模式,能夠是ObjectLinear、EyeLinear、SphereMap、CubeReflect、CubeNormal中的一種。若是使用了自定義的頂點程序,這些參數會被忽略。

LightmapMode:紋理將受渲染器的光照貼圖參數影響。紋理將不會從材質中獲取,而是取自渲染器的設置。

示例以下:

Properties { _RefDis ("Reflect Distance", Range(0, 1)) = 0.3 //範圍數值 _Color ("Reflect Color", Color) = (.34, .85, .92, 1) //顏色 _MainTex ("Reflect Color", 2D) = "white"{} //紋理 }

經常使用的變量類型以下:

顏色和向量:float4, half4, fixed4 範圍和浮點數:float, half, fixed 2D紋理貼圖:sampler2D Cubemap:samplerCUBE 3D紋理貼圖:sampler3D

(2)SubShader,子着色器由標籤(可選)、通用狀態(可選)、Pass列表組成。使用子着色器渲染時,每一個pass都會渲染一次對象,因此應儘可能減小Pass數量。

(3)Category,分類用於提供讓子着色器繼承的命令。

3. 表面着色器,使用Cg/HLSL編寫,而後嵌在ShaderLab的結構代碼中使用。僅需編寫最關鍵的表面函數,其他代碼由Unity生成,包括適配各類光源類型、渲染實時陰影以及集成到前向/延遲渲染管線中。若是你須要的效果與光照無關,最好不要使用表面着色器,不然會進行不少沒必要要的光照計算。使用#pragma surface...來指明是一個表面着色器。輸入結構體Input通常包含必須的紋理座標,還能夠在輸入結構中加入一些附加數據。

CGPROGRAM #pragma surface surf Lambert sampler2D _MainTex; fixed4 _Color;  struct Input { float2 uv_MainTex; };  void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;     o.Albedo = c.rgb;     o.Alpha = c.a; } ENDCG

SurfaceOutpu描述了表面的各類參數,它的標準結構以下:

struct SurfaceOutput{ half3 Albedo; //反射光
    half3 Normal;    //法線
    half3 Emission;  //自發光
    half Specular;   //高光
    half Gloss;      //光澤度
    half Alpha;      //透明度
}

4. 頂點和片斷着色器,運行於具備可編程渲染管線的硬件上,它包括頂點程序和片斷程序。使用該着色器渲染時,固定功能管線將會關閉,即編寫好的頂點程序替代原有的3D變換、光照、紋理座標生成等功能,片斷程序會替換掉SetTexture命令中的紋理混合模式。代碼使用Cg/HLSL編寫,放在Pass命令中,格式以下:

SubShader{ Pass{ CGPROGRAM #pragma vertex vert
        #pragma fragment frag

        //cg code
 ENDCG } }

 編譯命令說明以下:

#pragma vertex name-----------------------------將函數name的代碼編譯成頂點程序
#pragma fragment name---------------------------將函數name的代碼編譯成片斷程序
#pragma geometry name---------------------------將函數name的代碼編譯成DX10的幾何着色器
#pragma hull name-------------------------------將函數name的代碼編譯成DX11的hull着色器
#pragma domain name-----------------------------將函數name的代碼編譯成DX11的domain着色器
#pragma fragmentoption option-------------------添加選項到編譯的OpenGL片斷程序,對於頂點程序或編譯目標不是OpenGL的無效
#pragma target name-----------------------------設置着色器的編譯目標
#pragma only_renderers space separated names----僅編譯到指定的渲染平臺
#pragma exclude_renderers space separated names-不編譯到指定的渲染平臺
#pragma glsl------------------------------------爲桌面系統的OpenGL進行編譯時,將Cg/HLSL代碼轉換成GLSL代碼
#pragma glsl_no_auto_normalization--------------編譯到移動平臺GLSL時,關閉頂點着色器中對法線和切線進行自動規範化

示例代碼,使用命令:

Shader "Custom/Shader1" { Properties { _Color("Main Color", Color) = (1,1,1, 0.5) _SpecColor("Spec Color", Color) = (1,1,1,1) _Emission("Emmisive Color", Color) = (0,0,0,0) _Shininess("Shininess", Range(0.01, 1)) = 0.7 _MainTex("Base (RGB)", 2D) = "white" {} } SubShader { Pass{ Material{ Diffuse[_Color] Ambient[_Color] Shininess[_Shininess] Specular[_SpecColor] Emission[_Emission] } Lighting On SeparateSpecular On SetTexture[_MainTex]{ constantColor[_Color] Combine texture * primary DOUBLE, texture * constant } } } FallBack "Diffuse" }

 示例代碼,使用Cg。其中的包含文件能夠在/Data/CGIncludes/目錄下找到。

Shader "Custom/Shader2" { //定義屬性(變量)
 Properties { _MainTex ("Texture", 2D) = "white" {} //紋理
        _Color ("Main Color", Color) = (1,1,1,0.5) //顏色
 } //子着色器
 SubShader { //每一個Pass中,對象幾何體都被渲染一次
 Pass{ CGPROGRAM //Cg代碼開始
            #pragma vertex vert //將函數vert編譯爲頂點程序
            #pragma fragment frag //將函數frag編譯爲片斷程序

            //包含一個內置的cg文件,提供了經常使用的聲明和函數,好比appdata_base結構
            #include "UnityCG.cginc" float4 _Color; //變量,顏色的向量表示
 sampler2D _MainTex; float4 _MainTex_ST; //定義一個結構體v2f
            struct v2f{ float4 pos:SV_POSITION; float2 uv:TEXCOORD0; }; //頂點處理程序
 v2f vert(appdata_base v) { v2f o; //3D座標被投影到2D窗口中,與矩陣Model-View-Projection相乘
                o.pos = mul(UNITY_MATRIX_MVP, v.vertex); o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); return o; } //片斷處理程序
 half4 frag(v2f i):COLOR { half4 texcol = tex2D(_MainTex, i.uv); //自定義顏色_Color與紋理的融合
                return texcol * _Color; } ENDCG //Cg代碼結束
 } } //備用着色器
    FallBack "VertexLit" }

上面的例子用到了一些內置的變量,有下面這些:

UNITY_MATRIX_MVP--------------------------當前的model*view*projection矩陣
UNITY_MATRIX_MV---------------------------當前的model*view矩陣
UNITY_MATRIX_V----------------------------當前的view矩陣
UNITY_MATRIX_P----------------------------當前的projection矩陣
UNITY_MATRIX_VP---------------------------當前的view*projection矩陣
UNITY_MATRIX_T_MV-------------------------model*view矩陣的轉置矩陣
UNITY_MATRIX_IT_MV------------------------model*view矩陣的轉置逆矩陣
UNITY_MATRIX_TEXTURE0
UNITY_MATRIX_TEXTURE1
UNITY_MATRIX_TEXTURE2
UNITY_MATRIX_TEXTURE3---------------------紋理變換矩陣
UNITY_LIGHTMODEL_AMBIENT------------------當前的環境光顏色

下面是官方文檔中的一個例子,能夠產生不一樣顏色交錯的效果:

Shader "Custom/Bars" { SubShader { Pass { CGPROGRAM #pragma vertex vert
            #pragma fragment frag #include "UnityCG.cginc"

            struct vertOut { float4 pos:SV_POSITION; float4 scrPos; }; vertOut vert(appdata_base v) { vertOut o; o.pos = mul (UNITY_MATRIX_MVP, v.vertex); //ComputeScreenPos將返回片斷着色器的屏幕位置scrPos
                o.scrPos = ComputeScreenPos(o.pos); return o; } fixed4 frag(vertOut i) : COLOR0 { float2 wcoord = (i.scrPos.xy/i.scrPos.w); fixed4 color; //改變50能夠調整間距
                if (fmod(50.0*wcoord.x,2.0)<1.0) { color = fixed4(wcoord.xy,0.6,1.0);//這裏能夠改變顏色
                } else { color = fixed4(0.1,0.3,0.7,1.0);//這裏能夠改變顏色
 } return color; } ENDCG } } }

下面的例子來自官方手冊,棋盤格效果:

Shader "Custom/Chess" { SubShader { Pass { CGPROGRAM #pragma vertex vert
            #pragma fragment frag #include "UnityCG.cginc"

            //輸入頂點結構體,包含位置和顏色
            struct vertexInput { float4 vertex : POSITION; float4 texcoord0 : TEXCOORD0; }; //片斷結構體,包含位置和顏色
            struct fragmentInput{ float4 position : SV_POSITION; float4 texcoord0 : TEXCOORD0; }; //頂點處理
 fragmentInput vert(vertexInput i){ fragmentInput o; o.position = mul (UNITY_MATRIX_MVP, i.vertex); o.texcoord0 = i.texcoord0; return o; } //片斷處理
 float4 frag(fragmentInput i) : COLOR { float4 color; //fmod用來取餘數,物體表面X方向被分紅了8/2=4個區間 //X座標對2求餘,因此這裏用1來做爲比較,黑、白各佔一半
                if ( fmod(i.texcoord0.x*8.0,2.0) < 1.0 ){ if ( fmod(i.texcoord0.y*8.0,2.0) < 1.0 ) { color = float4(1.0,1.0,1.0,1.0);//白色
                    } else { color = float4(0.0,0.0,0.0,1.0);//黑色
 } } else { if ( fmod(i.texcoord0.y*8.0,2.0) > 1.0 ) { color = float4(1.0,1.0,1.0,1.0);//白色
                    } else { color = float4(0.0,0.0,0.0,1.0);//黑色
 } } return color; } ENDCG } } FallBack "Diffuse" }

相同效果的簡化代碼:

Shader "Custom/ChessOpt" { SubShader { Pass { CGPROGRAM #pragma vertex vert_img
            #pragma fragment frag #include "UnityCG.cginc" float4 frag(v2f_img i) : COLOR { bool p = fmod(i.uv.x*8.0,2.0) < 1.0; bool q = fmod(i.uv.y*8.0,2.0) > 1.0; return float4(float3((p && q) || !(p || q)),1.0); } ENDCG } } }

上一個例子中有個texcoord0變量,它的x和y的值都是從0到1的,恰好映射到一張特殊的紋理上。

相關文章
相關標籤/搜索