unity3d里的Surface shader中的surf,LightingName,finalColor,到底屬於渲染管線的哪個階段?

最近在學Surface shader,也在看real-time rendering這本書,對surf表面,LightingName光照,finalColor函數有些迷惑,不知道它們分別隸屬於渲染管線的哪個或哪些階段,對應的編譯代碼屬於哪個或哪些階段。

surf 函數是否會編譯成vertex shader和 pixel shader。LightingName函數是否也會編譯成vertex shader里基於頂點的光照計算,和rasterization里的基於像素的光照計算呢?還有裡面的finalColor函數,又是會編譯成什麼呢?

之前看過Surface shader編譯之後的代碼,但還是沒搞清楚,求各位朋友指點迷津。

PS:之前問的問題點有點問題,重新改了一下。

下面圖片摘抄於http://blog.csdn.net/candycat1992/article/details/39994049


surface shader的來龍去脈,請看Shaders must die。一共有三部分,包括如何想到這個主意、如何試驗、如何實用化。

不過說實話,這不能算aras發明的。這個概念在reyes裡面已經將近30年了。


其實我沒有寫過 surface shader,以下內容都是看文檔現學的,結合自己以往的知識,可能有些漏洞。

先說定義

surface shader 是 Unity3D 自定義的一種 shader,用於減少程序員編寫重複的 shader 代碼。

以下是官方的一個簡單範例

Shader "Example/Diffuse Simple" {
SubShader {
Tags { "RenderType" = "Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = 1;
}
ENDCG
}
Fallback "Diffuse"
}

surf() 函數

主體是一個 surf 函數,它不能直接用於圖形管線,需要由 Surface Shader compiler 編譯成對應的 vertex shader 和 pixel shader。它生成的 vs 和 ps 同時支持 forward rendering 和 deferred rendering。

surf 函數輸入的是 Input IN,輸出的是 SurfaceOutput o。SurfaceOutput 是什麼呢?

struct SurfaceOutput {
half3 Albedo;
half3 Normal;
half3 Emission;
half Specular;
half Gloss;
half Alpha;
};

LightingName() 函數

系統默認的光照模型是 Lambert 和 BlinnPhong,你也可以通過 LightingName 函數來自定義。

比如

#pragma surface surf SimpleLambert

half4 LightingSimpleLambert (SurfaceOutput s, half3 lightDir, half atten) {
half NdotL = dot (s.Normal, lightDir);
half4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (NdotL * atten * 2);
c.a = s.Alpha;
return c;
}

那麼 surf 函數會在 ps 里展開成調用 LightingSimpleLambert 函數來計算顏色。

finalColor() 函數

finalColor 函數則是在 ps 的結尾處提供一次機會,讓你修改最終的返回值。

總結下

suf() 通過 unity 的編譯器展開成 vs() 和 ps()

ps() 中會調用 LightingName 函數進行顏色的計算,可以用默認的 Lambert 和 BlinnPhong,還可以自定義。

ps() 中還會調用 finalColor 函數對顏色進行後處理,默認的話就是不處理。


今天搜資料的時候偶然發現這個問題。看了下題目描述覺得圖片非常眼熟,原來是出自我的博客(【Unity Shaders】初探Surface Shader背後的機制),這是我在2014年10月10日畫的,電腦上還有原稿,能不能請題主註明下出處

前面大家說的都很好了。另,我在2014年10月份寫了這篇關於表面著色器的機制分析的文章,雖然Unity 5引入了全局光照、PBR這些新的內容,但大部分是不變的,可以去博文里看看。最近在寫關於Unity Shader的書(【我的書】Unity Shader的書),今天剛剛畫了最新版的表面著色器典型的Pass的流程,這裡可以提前公開一下:

當然,如果使用了基於物理的光照上面還會涉及GI的部分。如果後面還有人要引用的話,請註明出處。。。


補充下 @Vinjn張靜的答案,但是surf()是只展開在ps()里的。。。

不過根據你需要的Input里的數據,它會自動生成對應的vs()代碼;而且裡面有一些宏切換各種variant~

unity里可以直接看到展開的代碼(及compiled code),你完全可以改一改自己的代碼,然後看生成的代碼是啥樣子~話說unity5自帶frame debugger之後,關於整個pipeline終於不是完全黑箱了。

然後Lighting函數什麼的,其實你可以直接在built in shaders裡面找到對應的代碼(下載見Unity - Download Archive),例如Lambert的代碼就在Lighting.cginc里

inline fixed4 LightingLambert (SurfaceOutput s, fixed3 lightDir, fixed atten)
{
fixed diff = max (0, dot (s.Normal, lightDir));

fixed4 c;
c.rgb = s.Albedo * _LightColor0.rgb * (diff * atten * 2);
c.a = s.Alpha;
return c;
}

其實surf主要是給TA的一個低成本工具來寫shader,類似材質編輯器的感覺=w=

補充:再貼張字體小點的圖,截取了一個簡短的frag variant舉個栗子

紅色箭頭是初始化SurfaceOutput,下面一截是準備各種輸入變數(這裡還有各種空間轉換的坑,以後有機會再說,總之不同Input是不太一樣的);

然後在綠色箭頭裡就是調用你寫的surf函數,作用是根據剛才準備好的surfIN數據,計算一個SuraceOutput出來;

藍色箭頭那邊就是計算光照什麼的(lightmap, readtime, ...)是根據你指定的Lighting函數選的,如果你是自定義的Lighting模型就變成調用你的函數。

樓上大大把這個過程想的太複雜了……像unity里自定義Lighting函數的參數列表其實就固定的幾種,它就是代碼片段拼了一下……


舉個簡單例子:新的Standard shader, 官方默認的是vertfragment Shader

像素階段對應流程是: surf()-&>LightingStandard_GI()-&>LightingStandard()

surf 函數處於 pixel shader 的起始階段

如果轉換為 surface shader, 默認光照名稱是Standard

大部分需求只是修改surf函數就能滿足了。


嗯 比較贊同樓上那位妹子的


推薦閱讀:

「渲染地球」引擎實現的原理可能是什麼?
《Manifold Garden》中的模型描邊是怎麼實現的?
如何進一步學習 shader (CG) 的知識?
在頂級遊戲開發的過程中需要怎樣的編程實力?
unity和ue4以後那個發展好?

TAG:Unity遊戲引擎 | 計算機圖形學 |