Surface Shader
本小結對Unity的Surface Shader做一個大概的了解。主要了解在Surface Shader當中比較重要的幾個部分,分別是:
- SurfaceOutput
- Input
- lighing
- shadow
首先查看一下Unity的官方手冊中的Writing Surface Shaders,其中描述道:如果要編寫一個shader去和光進行交互是比較復雜的,因為光照會有不同的光照類型,不同的陰影選項和不同的渲染路徑(包括foward和deferred rendering等),因此shader需要找到處理這些復雜事物的方法。
在使用Surface Shader時,它可以自動生成一些代碼,比直接去使用低階的頂點和像素着色器來說要容易許多。但是需要注意的是Surface Shader並不是一種定制的語言,也不是一種神奇的東西,它只不過自動生成了以前必須去手寫的代碼。Surface Shader還是使用Cg或HLSL語言編寫。在前面課程當中,已經初步解到,Surface Shader實際上就是對頂點和像素着色器的一種包裝,它讓我們不用去關注更多的頂點和片段程序的細節,能夠快速地得到想要的着色器。接下來試着編寫一些Surface Shader程序。
創建一個默認的Surface Shader,如下:

1 Shader "Lesson/SurfaceShader1" { 2 Properties { 3 _Color ("Color", Color) = (1,1,1,1) 4 _MainTex ("Albedo (RGB)", 2D) = "white" {} 5 _Glossiness ("Smoothness", Range(0,1)) = 0.5 6 _Metallic ("Metallic", Range(0,1)) = 0.0 7 } 8 SubShader { 9 Tags { "RenderType"="Opaque" } 10 LOD 200 11 12 CGPROGRAM 13 // Physically based Standard lighting model, and enable shadows on all light types 14 #pragma surface surf Standard fullforwardshadows 15 16 // Use shader model 3.0 target, to get nicer looking lighting 17 #pragma target 3.0 18 19 sampler2D _MainTex; 20 21 struct Input { 22 float2 uv_MainTex; 23 }; 24 25 half _Glossiness; 26 half _Metallic; 27 fixed4 _Color; 28 29 void surf (Input IN, inout SurfaceOutputStandard o) { 30 // Albedo comes from a texture tinted by color 31 fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; 32 o.Albedo = c.rgb; 33 // Metallic and smoothness come from slider variables 34 o.Metallic = _Metallic; 35 o.Smoothness = _Glossiness; 36 o.Alpha = c.a; 37 } 38 ENDCG 39 } 40 FallBack "Diffuse" 41 }
首先來了解一下這個Shader結構。這個Shader在Properties中有四個屬性:
- _Color為顏色值。
- _MainTex是主紋理。
- _Glossiness是一個浮點值,用於計算高光的光澤度。
- _Metallic也是一個浮點值,用於計算表現金屬的光澤度。
該shader有一個SubShader,但是,在這個SubShader中並沒有Pass通道。這里需要注意的是,在Surface Shader當中不需要去編寫Pass通道,原因是Surface Shader就是對Vertex & Fragment Shader的一種包裝,它能夠在自動生成着色器代碼,生成的過程不需要我們干預,Pass通道也能自動生成,因此,如果添加了Pass通道,就會出現編譯錯誤。最后,作為ShaderLab的基本結構,該默認的Surface Shader擁有一個FallBack,如果shader中的某一種特性不能夠被使用,那么會回滾到Diffuse。
接下來重點了解一下SubShader擁有哪些內容。其中“Tags { "RenderType"="Opaque" }”描述的是渲染類型,“Opaque”表示不透明的物體;“LOD 200”是指層級細節;“CGPROGRAM”到“ENDCG”是一個代碼塊,表示其中使用Cg語法,要真正地學會Surface Shader編程,有必要先學會Cg語言,不過現在可以簡單地從Surface Shader的結構出發,基本了解一下Surface Shader的內容。
首先“#pragma surface surf Standard fullforwardshadows”是Surface Shader當中比較重要的一部分。“pragma”是一個編譯指令,這個編譯指令有具體的格式,可以從Unity官方手冊中了解一下有關的內容,其格式為:“#pragma surface surfaceFunction lightModel [optionalparams]”。
- 以“#”開頭,“surface”關鍵詞表示該shader以Surface Shader格式來編寫,Unity引擎在處理這個shader結構的時候,就會自動地進行編譯低階的代碼。
- “surfaceFunction”是指surface的函數,在一定條件下可以隨便取名,在默認創建的Surface Shader中的"surf"就是函數名,下面也有對應的函數體。
- “lightModel”描述的是光照模型,在默認創建的Surface Shader中使用的是“Standard”,那么這個光照模型實際上也是一個函數,我們可以在Unity(5.0以上)的安裝目錄上找到一個光照的代碼文件"UnityPBSLighting.cginc",其中的函數“LightingStandard”,其中“Lighting”后的名稱是我們真正使用的函數名。
- “[optionalparams]”表示其他的一些選項,默認Surface Shader使用的是“fullforwardshadows”,從字面上理解是關於陰影的一些功能,查看Unity手冊可以了解到默認使用的“fullforwardshadows”表示它能夠在Forward渲染路徑下支持所有的陰影類型,默認的shader僅僅能支持一個方向光的陰影,如要在Forward渲染路徑下使用點光源或聚光燈產生的陰影就需要使用該指令。這里需要注意的是,在其上還有一個“addshadow”指令,它的主要功能就是生成一個陰影投射器,在現目前的代碼當中也可以直接使用“addshadow”。
接下來在工程中實際使用一下這個shader。
創建一個材質並使用該shader,然后在場景中創建一個Cube當做地面,在其上創建一個Sphere,並使用我們創建的材質,可以看到球體的檢視面板中已經擁有了shder當中編寫的四個屬性:“Color”是顏色,默認為白色;“Albedo (RGB)”是主紋理,這里先拖放一張貼圖;“Smoothness”是平滑度,描述的是高光的強度;“Metallic”表示的是金屬質感,調整這個系數可以修改金屬質感的強度,金屬質感原理就是如果光照射非常光滑的金屬物體,有鏡面反射的部分光會很集中,形成高亮,沒有鏡面反射的地方就會很暗淡。效果如圖:
以上就是對該shader的基本使用。
接下來在該shader中,“#pragma target 3.0”表示我們將要對這個着色器使用硬件的“shader model 3.0”的能力,硬件的“shader model”是硬件一個用於着色處理的基礎的指令范圍和能力,值越高表示能使用越高級的功能,如果沒有使用“#pragma target 3.0”這句指令,默認使用的是“shader model 2.0”。
“sampler2D”是一個二維紋理,這里表示該shader的主紋理參數。
struct Input { float2 uv_MainTex; };
之后的Input結構體,用於描述紋理的uv坐標,查看Unity文檔,在"Surface Shader input structure"目錄中,關於這個紋理坐標,必須以“uv”或“uv2”開頭,使用“uv”表示用第一套uv坐標集合,用“uv2”表示第二套uv坐標集合。因此,如果在Properties中使用類似"_MainTex"紋理屬性,就需要在SubShader中的一個輸入結構體當中必須以“uv”開頭的變量,否則就得不到該紋理采樣值。
除了Input結構體之外,在CGPROGRAM要對Properties中的屬性做對應的聲明。兩者的類型不一樣,“2D”對應“sampler2D”,“Range”對應“half”,“Color”對應“fixed4”。