Unity-編寫表面著色器
編寫與光照交互的著色器非常復(fù)雜。有不同的光源類型,不同的陰影選項,不同的渲染路徑(前向和延遲渲染);著色器應(yīng)該以某種方式應(yīng)對所有這些復(fù)雜性。
Unity 中的__表面著色器__是一種代碼生成方法,與使用低級頂點/像素著色器程序相比,可以更輕松地編寫光照著色器。請注意,表面著色器中沒有自定義的語言、魔法或忍者;它只能生成所有那些本來必須要手工編寫的重復(fù)代碼。您仍然需要使用 HLSL 編寫著色器代碼。
如需了解示例,請查看表面著色器示例和表面著色器自定義光照示例。
工作原理
您可以定義一個“表面函數(shù)”,它將您需要的所有 UV 或數(shù)據(jù)作為輸入,并填充輸出結(jié)構(gòu)?SurfaceOutput
。SurfaceOutput 基本上描述了_表面的屬性_(反照率顏色、法線、發(fā)光、鏡面反射等)。您需要使用 HLSL 編寫此代碼。
表面著色器編譯器隨后計算出需要的輸入、填充的輸出等等,并生成實際的頂點和像素著色器以及渲染通道來處理前向和延遲渲染。
以下是表面著色器的標準輸出結(jié)構(gòu):
struct SurfaceOutput { ? ? ? fixed3 Albedo; ?// 漫射顏色 ? ? ? fixed3 Normal; ?// 切線空間法線(如果已寫入) ? ? ? fixed3 Emission; ? ? ? half Specular; ? ? // 0..1 范圍內(nèi)的鏡面反射能力 ? ? ? fixed Gloss; ? ? ? // 鏡面反射強度 ? ? ? fixed Alpha; ? ? ? // 透明度 ? ?Alpha };
在 Unity 5 中,表面著色器還可以使用基于物理的光照模型。內(nèi)置標準光照模型和標準鏡面反射光照模型(見下文)分別使用以下輸出結(jié)構(gòu):
struct SurfaceOutputStandard { ? ?
?fixed3 Albedo; ? ? ?
?// 基礎(chǔ)(漫射或鏡面反射)顏色 ? ?
?fixed3 Normal; ? ? ?
?// 切線空間法線(如果已寫入) ?
?half3 Emission; ? ?
?half Metallic; ? ? ?
?// 0=非金屬,1=金屬 ? ?
?half Smoothness; ? ?
?// 0=粗糙,1=平滑 ? ?
?half Occlusion; ? ?
?// 遮擋(默認為 1) ? ?
?fixed Alpha; ? ? ? ?
?// 透明度 Alpha
};
?struct SurfaceOutputStandardSpecular { ? ?
?fixed3 Albedo; ? ?
?// 漫射顏色 ? ?
?fixed3 Specular; ? ?
?// 鏡面反射顏色 ? ?
?fixed3 Normal; ? ? ?
?// 切線空間法線(如果已寫入) ? ?
?half3 Emission; ? ?
?half Smoothness; ? ?
?// 0=粗糙,1=平滑 ? ?
?half Occlusion; ? ?
?// 遮擋(默認為 1) ? ?
?fixed Alpha; ? ? ? ?
?// 透明度
?Alpha
};
示例
請參閱表面著色器示例、表面著色器自定義光照示例和表面著色器曲面細分頁面。
表面著色器編譯指令
就像任何其他著色器一樣,表面著色器放置在?CGPROGRAM..ENDCG
?代碼塊內(nèi)。不同之處在于:
它必須放在?SubShader?代碼塊內(nèi),不能在?Pass?內(nèi)。表面著色器本身將編譯為多個通道。
它使用?
#pragma surface ...
?指令來指示自己是表面著色器。
#pragma surface
?指令為:
# pragma surface surfaceFunction lightModel [optionalparams]
必需參數(shù)
surfaceFunction
?- 具有表面著色器代碼的 Cg 函數(shù)。該函數(shù)的格式應(yīng)為?void surf (Input IN, inout SurfaceOutput o)
,其中 Input 是您定義的結(jié)構(gòu)。Input 應(yīng)包含表面函數(shù)所需的任何紋理坐標和額外自動變量。lightModel
?- 要使用的光照模型。內(nèi)置光照模型是基于物理的?Standard
?和?StandardSpecular
,以及簡單的非基于物理的?Lambert
(漫射)和?BlinnPhong
(鏡面反射)。請參閱自定義光照模型頁面以了解如何編寫自己的光照模型。Standard
?光照模型使用?SurfaceOutputStandard
?輸出結(jié)構(gòu),并與 Unity 中的標準(金屬性工作流)著色器匹配。StandardSpecular
?光照模型使用?SurfaceOutputStandardSpecular
?輸出結(jié)構(gòu),并與 Unity 中的標準(鏡面反射設(shè)置)著色器匹配。Lambert
?和?BlinnPhong
?光照模型不是基于物理的(來自 Unity 4.x),但使用這兩個光照模型的著色器在低端硬件上可以提高渲染速度。
可選參數(shù)
透明度和 Alpha 測試由?alpha
?和?alphatest
?指令控制。透明度通常可以有兩種:傳統(tǒng)的 Alpha 混合(用于淡出對象)或更符合物理規(guī)律的“預(yù)乘混合”(允許半透明表面保留適當(dāng)?shù)溺R面反射)。啟用半透明度會使生成的表面著色器代碼包含混合命令;而啟用 Alpha 鏤空將根據(jù)給定的變量在生成的像素著色器中執(zhí)行片元廢棄。
alpha
?或?alpha:auto
?- 對于簡單的光照函數(shù),將選擇淡化透明度(與?alpha:fade
?相同);對于基于物理的光照函數(shù),將選擇預(yù)乘透明度(與?alpha:premul
?相同)。alpha:blend
?- 啟用 Alpha 混合。alpha:fade
?- 啟用傳統(tǒng)淡化透明度。alpha:premul
?- 啟用預(yù)乘 Alpha 透明度。alphatest:VariableName
?- 啟用 Alpha 鏤空透明度。剪切值位于具有 VariableName 的浮點變量中。您可能還想使用?addshadow
?指令生成正確的陰影投射物通道。keepalpha
?- 默認情況下,無論輸出結(jié)構(gòu)的 Alpha 輸出是什么,或者光照函數(shù)返回什么,不透明表面著色器都將 1.0(白色)寫入 Alpha 通道。使用此選項可以保持光照函數(shù)的 Alpha 值,即使對于不透明的表面著色器也是如此。decal:add
?- 附加貼花著色器(例如 terrain AddPass)。這適用于位于其他表面之上并使用附加混合的對象。請參閱表面著色器示例decal:blend
?- 半透明貼花著色器。這適用于位于其他表面之上并使用 Alpha 混合的對象。請參閱表面著色器示例
自定義修改器函數(shù)可用于更改或計算傳入的頂點數(shù)據(jù),或更改最終計算的片元顏色。
vertex:VertexFunction
?- 自定義頂點修改函數(shù)。在生成的頂點著色器的開始處調(diào)用此函數(shù),并且此函數(shù)可以修改或計算每頂點數(shù)據(jù)。請參閱表面著色器示例。finalcolor:ColorFunction
?- 自定義最終顏色修改函數(shù)。請參閱表面著色器示例。finalgbuffer:ColorFunction
?- 用于更改 G 緩沖區(qū)內(nèi)容的自定義延遲路徑。finalprepass:ColorFunction
?- 自定義預(yù)通道基本路徑。
陰影和曲面細分?- 可以提供其他指令來控制陰影和曲面細分的處理方式。
addshadow
?- 生成陰影投射物通道。常用于自定義的頂點修改,以便陰影投射也可以獲得程序化頂點動畫。通常情況下,著色器不需要任何特殊的陰影處理,因為它們可以通過回退機制來使用陰影投射物通道。fullforwardshadows
?- 支持前向渲染路徑中的所有光源陰影類型。默認情況下,著色器僅支持前向渲染中來自一個方向光的陰影(以節(jié)省內(nèi)部著色器變體數(shù)量)。如果在前向渲染中需要點光源陰影或聚光燈陰影,請使用此指令。tessellate:TessFunction
?- 使用 DX11 GPU 曲面細分;該函數(shù)計算曲面細分因子。有關(guān)詳細信息,請參閱表面著色器曲面細分。
代碼生成選項?- 默認情況下,生成的表面著色器代碼會嘗試處理所有可能的光照/陰影/光照貼圖情況。但是在某些情況下,您知道您不需要其中的一部分,可以調(diào)整生成的代碼以跳過它們。這樣可以減小著色器,從而提高加載速度。
exclude_path:deferred
、exclude_path:forward
?和?exclude_path:prepass
?- 不為給定的渲染路徑(分別對應(yīng)延遲著色路徑、前向路徑和舊版延遲路徑)生成通道。noshadow
?- 禁用此著色器中的所有陰影接受支持。noambient
?- 不應(yīng)用任何環(huán)境光照或光照探針。novertexlights
?- 在前向渲染中不應(yīng)用任何光照探針或每頂點光源。nolightmap
?- 禁用此著色器中的所有光照貼圖支持。nodynlightmap
?- 禁用此著色器中的運行時動態(tài)全局光照支持。nodirlightmap
?- 禁用此著色器中的方向光照貼圖支持。nofog
?- 禁用所有內(nèi)置霧效支持。nometa
?- 不生成“Meta”通道(由光照貼圖和動態(tài)全局光照用于提取表面信息)。noforwardadd
?- 禁用前向渲染附加通道。這會使著色器支持一個完整方向光,所有其他光源均進行每頂點/SH 計算。也能減小著色器。nolppv
?- 禁用此著色器中的光照探針代理體支持。noshadowmask
?- 為此著色器禁用陰影遮罩支持(包括?Shadowmask?和?Distance Shadowmask)。
其他選項
softvegetation
?- 僅在開啟 Soft Vegetation 時才渲染表面著色器。interpolateview
?- 在頂點著色器中計算視圖方向并進行插值;而不是在像素著色器中計算。這可以使像素著色器更快,但會額外消耗一個紋理插值器。halfasview
?- 將半方向矢量傳入光照函數(shù)而不是視圖方向。計算半方向并按每個頂點對其進行標準化。這更快,但并不完全正確。approxview
?- 在 Unity 5.0 中已刪除。請改用?interpolateview
。dualforward
?- 在前向渲染路徑中使用雙光照貼圖。dithercrossfade
?- 使表面著色器支持抖動效果。然后,可將此著色器應(yīng)用于使用細節(jié)級別組 (LOD Group)?組件(配置為交叉淡入淡出過渡模式)的游戲?qū)ο蟆?/p>
要了解與上述不同選項的具體區(qū)別,使用著色器檢視面板 (Shader Inspector)?中的“Show Generated Code”按鈕會很有幫助。
表面著色器輸入結(jié)構(gòu)
輸入結(jié)構(gòu)?Input
?通常具有著色器所需的所有紋理坐標。紋理坐標必須命名為“uv
”后跟紋理名稱的形式(如果要使用第二個紋理坐標集,則以“uv2
”開頭)。
可以放入輸入結(jié)構(gòu)的其他值:
float3 viewDir
?- 包含視圖方向,用于計算視差效果、邊緣光照等等。具有?
COLOR
?語義的?float4
?- 包含插值的每頂點顏色。float4 screenPos
?- 包含反射或屏幕空間效果的屏幕空間位置。請注意,這不適合?GrabPass;您需要使用?ComputeGrabScreenPos
?函數(shù)自己計算自定義 UV。float3 worldPos
?- 包含世界空間位置。float3 worldRefl
?- 在_表面著色器不寫入 o.Normal_ 的情況下,包含世界反射矢量。有關(guān)示例,請參閱反光漫射 (Reflect-Diffuse) 著色器。float3 worldNormal
?- 在_表面著色器不寫入 o.Normal_ 的情況下,包含世界法線矢量。float3 worldRefl; INTERNAL_DATA
?- 在_表面著色器寫入 o.Normal_ 的情況下,包含世界反射矢量。要獲得基于每像素法線貼圖的反射矢量,請使用?WorldReflectionVector (IN, o.Normal)
。有關(guān)示例,請參閱反光凹凸 (Reflect-Bumped) 著色器。float3 worldNormal; INTERNAL_DATA
?- 在_表面著色器寫入 o.Normal_ 的情況下,包含世界法線矢量。要獲得基于每像素法線貼圖的法線矢量,請使用?WorldNormalVector (IN, o.Normal)
。
表面著色器和 DirectX 11 HLSL 語法
目前,表面著色器編譯管線的某些部分不能理解?DirectX 11?特有的 HLSL 語法,因此如果您正在使用 HLSL 功能,如 StructuredBuffers、RWTextures 和其他非 DX9 語法,需要將其包裝到僅限 DX11 的預(yù)處理器宏中。
有關(guān)詳細信息,請參閱平臺特定差異和著色語言頁面。