從今天起,開始翻譯Unity關於shader的官方文檔。翻譯水平比較一般,目的主要是通過翻譯來提升對shader的見解,也讓其他人更容易的瞭解shader。以下開始正文內容:
編寫Surface Shaders
和光交互的shader寫起來很複雜,有不同的光照類型、陰影選項、渲染路徑(正向渲染和延遲渲染),有時shader需要考慮所有的複雜性。
Unity中的Surface Shader是一個代碼生成器,用它來寫光照shader(lit shader)相比於使用低階的頂點/像素shader(vertex/pixel shader)程序,會更加容易。注意Surface Shader中並沒有固定的語言和奇幻的東西(magic or ninjas involved)。它僅僅是生成原本必須由手工重複編寫的代碼。你也可以用Cg/HLSL來寫shader代碼。
這裏有一些例子: Surface Shader Examples、Surface Shader Custom Lighting Examples。
它如何工作
你定義一個“surface 函數”,其輸入是你所需要的任意UVs或數據,輸出是SurfaceOutput數據結構。SurfaceOutput簡單地描述了surface的屬性(properties of the surface),如反射率顏色(albedo color)、法線(normal)、散射(emission)、鏡面反射(specularity )等。
Surface Shader編譯器會確定需要什麼輸入,有什麼輸出等,也會產生實際的頂點&像素shader(vertex&pixel shaders),以及渲染路徑來處理正向和延遲渲染。
surface標準的輸出結構如下:
struct SurfaceOutput
2 {
3 fixed3 Albedo; // 漫反射顏色
4 fixed3 Normal; // 切線空間法線,如果賦值的話
5 fixed3 Emission;
6 half Specular; // 高光強度,範圍是0-1
7 fixed Gloss; // specular intensity
8 fixed Alpha; // 透明度
9 };
在Unity 5中,surface shader 也能使用物理光照模型。內建的標準和標準鏡面光照模型(見下文)分別使用以下輸出結構:
struct SurfaceOutputStandard
{
fixed3 Albedo; // 基礎 (漫反射或鏡面反射) 顏色
fixed3 Normal; // 切線空間法線,如果賦值的話
half3 Emission;
half Metallic; // 0=非金屬, 1=金屬
half Smoothness; // 0=粗糙, 1=光滑
half Occlusion; // 遮擋(默認1)
fixed Alpha; // 透明度
};
struct SurfaceOutputStandardSpecular
{
fixed3 Albedo; // 漫反射顏色
fixed3 Specular; // 鏡面反射顏色
fixed3 Normal; // 切線空間法線,如果賦值的話
half3 Emission;
half Smoothness; // 0=粗糙, 1=光滑
half Occlusion; // 遮擋(默認1)
fixed Alpha; // 透明度
};
例子
參見:Surface Shader Examples, Surface Shader Custom Lighting Examples and Surface Shader Tessellation。
Surface Shader編譯指令
Surface shader放在CGPROGRAM..ENDCG塊中,就像其他任何的shader一樣。不同處在於:
#pragma surface指令如下:
#pragma surface surfaceFunction lightModel [optionalparams]
必須參數
- surfaceFunction — 擁有surface shader代碼的Cg函數。此函數應有這樣的格式:void surf (Input IN, inout SurfaceOutput o),其中Input是你定義好的結構,它應該包含任何紋理座標以及surface函數所需的額外的自動變量。lightModel — 要使用的光照模型。內建的光照模型是基於物理的標準和標準鏡面光照模型,以及簡單的非物理Lambert(漫反射)和BlinnPhong(鏡面)光照模型。參見
-
來學習如何編寫。
- 標準光照模型使用SurfaceOutputStandard輸出結構,並匹配Unity中的標準(金屬工作流)shader。
- 標準鏡面光照模型使用SurfaceOutputStandardSpecular輸出結構,並匹配Unity中的標準(高光設置)shader。
- Lambert和BinnPhong光照模型是不基於物理的(來自Unity 4.x),但是使用它們的shader在低配電腦上能夠渲染地更快。
可選參數
透明度和alpha測試(Transparency and alpha testing)由alpha和alphatest指令控制。通常透明度有兩種類型:傳統alpha混合(用於對象淡出)或更逼近物理的“混合預乘”(允許半透明的表面保持合適的鏡面反射)。開啓半透明度使得產生的surface shader代碼包含blending指令:基於給定的變量,開啓alpha裁剪將會在生成的像素shader中進行碎片丟棄。
alpha
或alpha:auto — 將會選擇fade-transparency (同
alpha:fade
)作爲簡單的光照函數,選擇premultiplied transparency (同alpha:premul
)作爲物理光照函數。alpha:fade
— 允許傳統的透明度漸隱。alpha:premul
— 允許預乘alpha透明度。alphatest:VariableName
— 允許alpha裁剪透明度。截斷值是一個名爲VariableName的float類型變量。你還可以使用addshadow指令來生成合適的投影通道。keepalpha
— 默認alpha通道中的不透明度爲1.0(白色),無論輸出結構中的Alpha是多少或者光照函數的返回值是多少。decal:add
— 附加的貼花shader(如terrain AddPass)。這對位於其他表面正上方和使用附加混合的對象來說是有意義的。decal:blend
— 半透明貼花shader。這對位於其他表面正上方和使用alpha混合的對象來說是有意義的。
定製修改器函數(Custom modifier functions)能夠用來改變或者計算輸入的頂點數據,或者改變最終計算出的片段顏色。
vertex:VertexFunction
— 定製頂點修改器函數. 此函數在生成的頂點shader的開始處被調用,可以修改或計算預頂點數據,參見 Surface Shader Examples。finalcolor:ColorFunction
— 定製的最終顏色修改器函數。參見Surface Shader Examples。
陰影和鑲嵌(Shadows and Tessellation)— 附加指令,用於控制陰影和鑲嵌的處理。
addshadow
— 生成一個投影通道。一般還要使用定製頂點修改器,這樣投影也能獲取任何程序上的頂點動畫。 當shader通過fallback來使用投影時,通常不需要任何特別的陰影處理。fullforwardshadows
— 支持 Forward 渲染路徑中所有的光照陰影模型。默認shader只支持正向渲染中來自單方向光產生的陰影。如果你需要用點光源或聚光光源來產生陰影,使用該指令。tessellate:TessFunction
— 使用DX11 GPU 鑲嵌; 該函數計算鑲嵌因子。詳情參見 Surface Shader Tessellation。
代碼生成選項 — 默認生成的surface shader代碼會嘗試去處理所有可能的光照/陰影/光照貼圖場景。儘管如此,在某些情況下你並不需要其中一些,你可以調整生成的代碼來跳過它們。這樣就能產生更小、加載速度更快的shader。
exclude_path:deferred
,exclude_path:forward
,exclude_path:prepass
- 對於給定的渲染路徑(分別是Deferred Shading, Forward 和 Legacy Deferred),不生成相應的通道。noshadow
— 在此shader中關閉所有支持陰影功能。noambient
— 不應用任何環境光或光照探測(light probes)。novertexlights
— 不在正向渲染中應用任何光照探測或預頂點光照。nolightmap
— 在此shader中關閉所有支持光照貼圖功能。nodynlightmap
— 在此shader中關閉支持運行時動態全局光照(runtime dynamic global illumination)功能。nodirlightmap
- 在此shader中關閉支持方向光照貼圖功能。nofog
— 關閉內建的支持所有霧效果功能。nometa
— 不產生“meta”通道(該meta用來由光照貼圖和動態全局光照提取表面信息)。noforwardadd
— 關閉Forward 渲染附加通道。 這使得shader支持單方向完全光照,以及所有其他由每個頂點/SH計算的光照。同時使得shader更小。
混合選項
softvegetation
— 當柔性植被開啓時,surface shader纔會被渲染。interpolateview
— 在頂點shader中計算視線方向並進行插值,而不是在像素shader中進行計算。這使得像素shader更快,但會多消耗一個紋理插值器。halfasview
— 將half-direction 向量,而不是視線方向向量,傳遞給光照函數。Half-direction 將被逐頂點計算和單位化。這會更快,但不會完全正確。approxview
— 在Unity 5.0中被移除,請用interpolateview
替代。dualforward
- 在forward渲染路徑中使用dual lightmaps 。
要了解使用上述不同選項所帶來確切的變化,使用Shader Inspector中的“Show Generated Code” 按鈕將會有所幫助。
Surface Shader 輸入結構
輸入結構 Input
通常有shader所需的任意紋理座標。紋理座標必須命名爲“uv”+“紋理名稱”(或者以“uv2”開頭,來使用第二個紋理座標集)。
輸入結構中還能放入一下額外的變量:
float3 viewDir
— 將會包含視線方向,用來計算視差影響,邊緣光照等。float4
withCOLOR
semantic — 將會包含每個頂點插值後的顏色。float4 screenPos
— 將會包含反射或屏幕空間影響下的屏幕空間座標。float3 worldPos
— 將會包含世界空間座標。float3 worldRefl
— 如果surface shader沒有賦值o.Normal,將會包含世界反射向量。參見例子:Reflect-Diffuse shader。float3 worldNormal
— 如果surface shader沒有賦值o.Normal,將會包含世界法向量。float3 worldRefl; INTERNAL_DATA
— 如果surface shader沒有賦值o.Normal,將會包含世界法向量。爲了獲得逐像素法線貼圖的反射向量,請使用WorldReflectionVector (IN, o.Normal)。參見例子:
Reflect-Bumped shader。float3 worldNormal; INTERNAL_DATA
— 如果surface shader沒有賦值o.Normal,將會包含世界法向量。爲了獲得逐像素法線貼圖的法向量,請使用WorldNormalVector (IN, o.Normal)。
Surface shaders 和 DirectX 11
目前,surface shader編譯管道的部分內容並不能理解 DirectX 11-特定的HLSL 語法, 所以如果你在使用HLSL特性,諸如StructuredBuffers, RWTextures 和其他非DX9 語法,你必須將之包含在只針對DX11的預處理器宏中。詳情參見Platform Specific Differences 。