(十九)unity shader之——————基於物理的渲染技術(PBS):中篇(Unity 5中的Standard Shader的實現和使用)

一、unity 5中的standard shader

在unity5中新創建一個模型或是新創建一個材質時,默認使用的着色器都是一個名爲standard 的着色器。這個standard shader使用的就是之前所講的基於物理的渲染。

unity支持兩種流行的基於物理的工作流程:金屬工作流和高光反射工作流。其中,金屬工作流是默認的工作流程,對應的shader爲standard shader。而如果想要使用高光反射工作流,需要在材質的shader下拉框選擇Standard(Specular setup)。需要注意的是,使用不同的工作流可以實現相同的效果,只是它們使用的參數不同而已。金屬工作流也不意味着它只能模擬金屬類型的材質,名字源於它定義了材質表面的金屬值(是金屬類型的還是非金屬類型的)。高光反射工作流的名字源於它可以直接指定表面的高光反射顏色(有很強的高光反射還是很弱的)等,而在金屬工作流中這個顏色需要由漫反射顏色和金屬值衍生出來。在實際遊戲製作過程中,我們可以選擇自己更偏好的工作流來製作場景,也可以混合使用。

下面的內容中,我們使用standard shader來統稱standard和standard(Specular setup)着色器。unity提供的standard shader允許我們使用這一種shader來爲場景中所有物體進行着色,而不需要考慮它們是否是金屬材質還是塑料材質,從而大大減少我們不斷調整材質參數所花費的時間。

1.1它們是如何實現的

standard和standard(Specular setup)的源碼可以在網上下載,這些shader依賴於一些頭文件,這些相關頭文件名稱大多類似於UnityStandardXXX.cginc,其中定義了和PBS相關的各個函數、結構體和宏等,如下表:

可以打開standard.shader和standardSpecular.shader文件分析unity是如何實現基於物理的渲染的。總的來說,這兩個shader代碼基本相同——都定義了兩個subshader,第一個subshader使用的計算更加複雜,主要針對非移動平臺(通過#pragma exclude_renderers gles代碼來排除GLES平臺),並定義了前項渲染路徑和延遲渲染路徑使用的pass,以及用於投射陰影和提取元數據的pass;第二個subshader定義了4個pass,其中兩個pass 用於前項渲染路徑,一個pass用於投射陰影,一個pass用於提取元數據,該subshader主要針對移動平臺。

Standard.shader和standardSpecular.shader最大不同之處在於,它們在設置BRDF的輸入時使用了不同的函數來設置各個參數,——基於金屬工作流的standard shader使用MetallicSetup函數來設置各個參數,基於高光反射工作流的Standard(specular setup) shader使用SpecularSetup函數來設置。這倆函數均在UnityStandardCore.cginc文件中被定義。下圖給出了standard shader用於前項渲染路徑的典型實現,這是對內置文件的分析所得:

從上圖看出,兩個pass代碼大體相同,只是forward Pass進行了更多光照計算,例如全局光照、自發光等效果,這些計算只需要在物體的整個渲染過程中計算一次即可, 因此不需要在FarwardAddPass中再計算一次。

1.2如何使用standard shader

之前說到unity 5中的standard shader適用於各種材質的物體,但怎麼使用standard shader得到不同的材質效果?

爲什麼不同的材質看起來如此不同?材質和光的交互可以分爲漫反射和高光反射兩個部分,其中漫反射對應了次表面散射的結果,而高光反射對應了表面反射的結果。通過對金屬材質和非金屬材質的分析,可以得到它們的漫反射和高光反射的一些特點。

1.2.1金屬材質

(1)幾乎沒有漫反射,因爲所有被吸收的光都會被自由電子立刻轉化爲其他形式的能量。

(2)有非常強烈的高光反射。

(3)高光反射通常是由顏色的,例如金子的反光顏色爲黃色。

1.2.2非金屬材質

(1)大多數角度高光反射的強度比較弱,但在掠射角時高光反射強度反而會增強,即菲涅耳現象;

(2)高光反射的顏色比較單一。

(3)漫反射的顏色多種多樣。

但真實的材質大多混合了上面的這些特性,unity提供的工作流就是爲了更方便地讓我們針對以上特性來調整材質效果。unity有提供針對兩種工作流的校準表格,針對不同類型的材質來調整參數。

以金屬工作流使用的校準表格爲例,解釋如何使用它來指導我們調整材質。如下圖所示:

左邊球體是金屬材質,右邊是塑料材質。注意,我們需要在PlayerSetting裏的Other Setting->color space中選擇Linear纔可以得到上圖效果,這是因爲基於物理的渲染需要使用線性空間來進行相關計算。

在金屬工作流中,材質面板中的Albedo定義了物體的整體顏色,它通常就是我們視覺上認爲的物體顏色。從來亮度來看,非金屬材質的亮度範圍通常在50-243,而金屬材質亮度一般在188-255之間,可以用unity校準表格裏的推薦Albedo值,我們這裏把金屬材質Albedo設爲銀灰色,塑料材質設爲藍綠色。材質面板中的下一個屬性是Metallic,它定義了該物體表面看起來是否更像金屬或非金屬。同樣,可以使用一張紋理來採樣得到表面的Metallic值,此時紋理中的R通道值將對應了Metallic值。我們的例子中把金屬材質的Metallic值設爲1,表明該物體幾乎完全是一個金屬材質,同時塑料材質Metallic值設爲0,表明該物體幾乎沒有任何金屬特性。最後一個重要的材質屬性是Smoothness,他是Metallic的附屬值,定義了從視覺上看該表面的光滑程度。如果我們在設置Metallic屬性時使用的是一張紋理,那麼這張紋理的A通道就對應了表面的Smoothness值(此時紋理的GB通道則被忽略)。我們把金屬材質的Smoothness值設置爲相對較大的0.7,表面該金屬表面比較光滑,而把塑料材質Smoothness設爲0.4,表面該塑料比較粗糙。

高光反射工作流使用的面板和上述工作流使用的基本相同,只是使用了不同含義的Albedo屬性,並使用Specular代替了上述的Metallic屬性。在高光反射工作流中,材質的Albedo屬性定義了表面的漫反射強度。對於非金屬材質,它的值仍然是視覺上認爲的物體顏色,但對於金屬材質,Albedo的值通常非常接近黑色(金屬材質幾乎不存在次表面散射現象)。高光反射工作流Specular屬性則定義了表面的高光反射強度。非金屬材質通常使用一個灰度值範圍在0-55的深灰色作爲Specular值,表明非金屬材質的高光反射較弱。金屬材質則通常會使用視覺上認爲該金屬的顏色作爲它的Specular值。Specular屬性同樣也有一個子屬性Smoothness,它定義了從視覺上來看該表面的光滑程度,和上面的金屬工作流類似,如果使用了一張紋理來爲Specular屬性賦值,那麼紋理的RGB通道對應了Specular的值,A通道對應了Smoothness屬性值。

上述材質屬性都屬於材質面板中的Main Maps部分,除了這些屬性,還包含了其他材質屬性,例如切線空間下的法線紋理、遮擋紋理、自發光紋理等。Main Maps部分下面還有一個Secondary Maps屬性部分,這個部分屬性是用來定義額外的細節信息,這些細節通常會直接繪製在Main Maps的上面,來爲材質提供更多的微表面或細節表現。

除了上述屬性,我們還可以爲Standard shader選擇它使用的渲染模式,即材質面板上的RenderMode選項,它支持4種渲染模式:Opaque、Cutout、Fade和Transparent。其中,Opaque用於渲染最常見的不透明物體,這也是默認的渲染模式。對於像玻璃這樣的材質,可以選擇Transparent模式,在這個渲染模式下,Albedo屬性的A通道用於控制材質的透明度。而在Cutout渲染模式下,Albedo屬性中紋理的A通道會成爲一個掩碼紋理,而他的子屬性Alpha Cutoff將是透明度測試時使用的閾值。Fade模式和Transparent模式是類似的,不同的是,在Transparent模式下,當材質的透明度不斷降低時,它的反射仍然保留,而Fade模式所有渲染效果都會逐漸從屏幕上淡出。

需要注意的是,儘管Standard shader的材質面板有許多可供調節的屬性,但我們不用擔心由於沒有使用一些屬性而對性能有所影響。unity在背後已經進行了高度優化,在我們生成可執行程序時,unity會檢查哪些屬性沒有被使用到,同時也會針對目標平臺進行相應優化。

想要得到更理想的渲染結果,我們需要對不同的材質使用合適的屬性值,尤其是一些重要屬性值,例如Albedo、Metallic、Specular。當然要讓整個場景的渲染結果令人滿意,尤其包含了複雜光照的場景,僅僅有這些使用了PBS的材質是不夠的,我們需要使用unity提供的一些其他重要技術,例如HDR格式的Skybox、全局光照、反射探針、光照探針、HDR和屏幕後處理等。

二、一個更復雜的例子

下面用一個比較複雜、基於物理渲染的例子來更深入學習PBS。我們使用基於物理的Standard shader與其他unity功能相互配合得到更好的效果。

2.1設置光照環境

我們首先使用一個HDR格式的Cubemap當做天空盒,替換掉unity的默認天空盒。我們下篇文章中會詳細介紹HDR的原理和應用。現在只需要知道,使用HDR格式的Skybox可以讓場景中物體的反射更加真實,有利於我們得到更加可信的光照效果。

我們還可以設置場景的環境光:

可以選擇環境光照來源是場景使用的天空盒,還是使用漸變值,亦或是某個固定顏色。還可設置環境光強度(Intensity參數),如果想要場景中不接受任何環境光照,可以把該值設爲0。在使用standard shader前提下,如果我們關閉了場景中所有光源,並把環境光強度設置爲0,物體仍然可以接受一些光照,如下圖:

 

那麼這些光照是哪裏來的呢?答案就是反射。默認的反射源(Relection Source選項)是場景中使用的skybox。如果不想讓場景中物體接受任何默認的反射光照,可以把反射源設爲自定義(Custom)並把自定義的cubema保留爲空即可(或者直接把場景中使用的skybox設置爲空)。一般爲了得到更加逼真的渲染結果,不會這麼做。在渲染實現上,即使場景中沒有任何光源,unity在內部仍然會調用ForwardBase Pass(假設使用前向渲染路徑),並使用反射的光照信息來填充光源信息,再進行基於物理的渲染計算。需要注意這裏設置的反射源是默認的反射源,當默認反射源是skybox時,unity會由場景使用的skybox生成一個cubemap,可以通過Resolution選項來控制它每個面的分辨率。

除了standard shader外,unity還引入了一個重要的流水線——實時全局光照(Global Illumination,GI)流水線。使用GI,場景中的物體不僅可以受直接光照影響,還可以受間接光照影響。直接光照指的是那些直接把光照射到物體表面的光源,之前的章節我們都使用的直接光照來渲染整個場景的物體。但在現實生活中,物體還會受到間接光照的影響。舉個例子,一個球放在一面紅色的牆旁邊,儘管牆壁本身不發光,但球體靠近牆的一面仍會由少許的紅色,這是由於紅色牆壁把一些間接光照投射到了球體上。在unity中,間接光照指的是那些被場景中其他物體反彈的光,這些間接光照會受反彈光的表面的顏色影響,這些表面會在反彈光線時把自身表面的顏色添加到反射光的計算中,在unity5中,我們可以使用這些直接光照和間接光照來創建更加真實的視覺效果。

下面首先設置場景中使用的直接光照——一個平行光。在PBR(physically based rendering)中,想要讓渲染效果更加真實可信,我們需要保證平行光的方向和Skybox中的太陽或者其他光源的位置一致,使得物體物體產生的光照信息可以與skybox互相吻合。有時可能會使用一張耀斑紋理(Flare Texture)來模擬太陽等光源,此時我們同樣需要確保平行光的方向與耀斑紋理的位置一致。與之類似的還有平行光的顏色,我們應該儘量讓平行光的顏色和場景環境相配,比如傍晚平行光爲淺黃色,日落爲淡藍色等。我們還再Skybox的材質面板上調整天空的旋轉角度及曝光度,來調整場景的背景。

在平行光面板的烘焙選項中(Baking)我們選擇的Realtime模式,意味着場景中受平行光影響的所有物體都會進行實時光照計算,當光源或其他物體位置、旋轉燈發生變化,場景中光照結果也會隨之變化。然而實時光照往往需要較大的性能消耗,對於移動平臺這樣資源比較短缺的平臺,我們可以選擇Baked模式,此時unity會把該光源的光照效果烘焙到一張光照紋理中(lightmap),這樣就不用實時爲物體計算複雜的光照,而只需要通過紋理採樣來得到光照結果。擇烘焙模式缺點在於,如果場景物體發生了移動,但它的陰影等光照效果並不會發生變化。烘焙選項中的Mix模式則允許我們混合使用實時模式和烘焙模式,它會把場景中的靜態物體(即那些被標識爲static的物體)的光照烘焙到光照紋理中,但仍然會對動態物體產生實時光照。

unity5引入了實時間接光照的功能,在這個系統下,場景中的直接光照會在場景中各個物體之間來回反射,產生間接光照。間接光照可以讓那些沒有直接被光源照亮的物體同樣可以接受到一定的光照信息,這些光照是由它周圍的物體反射到它的表面上的。當第一條光線從光源被髮射出來後,他會與場景中一些物體相交,第一個和光線相交的物體受到的光照即爲直接光照。此後與該光線相交的物體,就會受到間接光照的影響,同時它們也會繼續反射。經過多次反射後,該光線最後完全消失。這些間接光照的強度是由GI系統計算得到的默認亮度值,光源面板中的Bounce Intensity參數可以讓我們調節這些間接光照的強度。當我們把它設爲0時,意味着一條光線會和一個物體相交,不再被繼續反射,物體只受直接光照的影響。

除了上述單個光源的間接光照強度調整,也可以對整個場景間接光照強度進行調整。在光照面板LightSetting中的Scene標籤頁下,我們可以調整General GI參數塊中的Bounce Boost參數來控制場景中反射的間接光照強度,他會和單個光源的Bounce Intensity參數來一起控制間接光照的反射強度。除此以外,把Indirect Intensity參數調大同樣可以增大間接光照的強度,需要注意的是,間接光照還有可能來自一些自發光的物體。

2.2放置反射探針

在實時渲染中,經常會使用cubemap來模擬物體的反射效果,但永遠使用同一個cubemap,當場景發生變化較大時,就很容易穿幫鏡頭,比如正在開車,車身或車窗的環境反射並沒有隨着環境的變化而變化,一種解決方法是可以在腳本中控制何時生成從當前位置觀察到的cubemap,然而unity 5爲我們提供了一種更加方便的途徑,即使用反射探針(Reflection Probes)。反射探針的工作原理和光照探針(Light Probes)類似,它允許我們在場景中特定位置上對整個場景的環境反射進行採樣,並把採樣結果存儲在每個探針上,當遊戲中包含反射效果的物體從這些探針附件進過時,unity會把這些鄰近探針存儲的反射結果傳遞給物體使用的反射紋理。如果物體周圍存在多個反射探針,unity還會在這些反射結果直接進行插值,來得到平滑漸變的反射效果。實際上,unity會在場景中放置一個默認的反射探針,這個反射探針存儲了對場景使用的skybox的反射結果,來作爲場景的環境光照。如果我們需要讓場景中的物體包含額外的反射效果,就需要放置更多的反射探針。

反射探針由三種類型:

(1)Baked,通過提前烘焙來得到該位置使用的cubemap的,在遊戲運行時反射探針存儲的cubemap並不會發生變化,需要注意,這種類型的反射探針在烘焙時同樣只會處理那些靜態物體。(即那些被標識爲Reflection Probe Static的物體)。

(2)Realtime,它需要花費更多的處理時間,因此使用時應該非常小心它們的性能,幸運的是unity允許我們從腳本中通過觸發來精確控制反射探針的更新。

(3)Custom,既可以讓我們從編譯器中烘焙它,也可以讓我們使用一個自定義的cubemap來作爲反射映射,但自定義的cubemap不會被實時更新。

需要注意的是,在放置反射探針時,選取的位置並不是任意的,應被放置那些具有明顯反射現象的物體的旁邊,或是一些牆角等容易發生遮擋的物體周圍。當放置好反射探針後,需要爲它們定義每個探針的影響區域,當反射物體進入到這個區域就對物體反射產生影響。通常情況下,反射探針的影響區域之間往往會有所重疊,此時unity會計算反射物體的包圍盒與這些重疊區域的交叉部分,並據此選擇使用的反射映射。如果當前目標平臺使用的是SM3.0及以上,unity還允許我們在這些相互重疊的反射探針之間進行混合,來實現平緩的反射過渡效果。

使用unity反射探針另一個好處是,可以模擬互相反射。傳統的cubemap方法無法模擬互相反射效果,比如兩個面對面的鏡子,理想情況下不僅會反射自己對面那面鏡子,還會反射鏡子裏反射的圖像。要實現這種效果,就需要追蹤光線的反射軌跡,這是傳統反射方法無法實現的,unity 5引入的GI系統讓這種效果變成了可能,如下圖:

在這個場景我們每個金屬球位置處放置了一個反射探針,並把每個金屬球上mesh render組件中的reflection Probes設爲爲simple,保證他們只會使用離他們最近的一個反射探針。默認情況下,反射探針只會捕捉一次反射,左邊金屬球使用的反射探針只會捕捉到右邊金屬球第一次反射過來的光線。但理想情況下,反射過來的光線會繼續被左邊金屬球反射,並對右邊金屬球造成影響。unity允許我們控制物體之間這樣來回的反射次數,通過改變Lighting Setting裏面的Reflection Bounces參數來實現。

使用反射探針往往需要更多的計算時間,這些探針實際上也是通過在它的位置上放置一個攝像機,來渲染得到一個cubemap,如果把反彈次數設置很大,或使用實時渲染,這些探針可能造成性能瓶頸。

2.3調整材質

要得到真實可信的渲染效果,需要爲場景中的物體指定合適的材質,基於物理的渲染並不意味着要一定模擬像照片真實的效果,更多好處在於可以讓我們的場景在各種光照條件下都能得到令人滿意的效果,同時不需要頻繁地調整材質參數。

在unity中,要想和全局光照、反射探針等內置功能良好地配合來得到出色的渲染結果,就需要使用unity內置的standard shader,可以通過查看官方給的Viking Village Demo對這些效果很好的材質提供一些參考,例如場景中所有物體都使用了高光反射紋理、遮擋紋理、法線紋理,一些材質還使用了細節紋理來提供更多的細節表現。

2.4線性空間

在使用基於物理的渲染方法渲染整個場景時,應該使用線性空間(Linear Space)來得到最好的渲染結果,在Player Setting->Color Space中選擇Linear選項。可以得到更加真實的效果,但他缺點在於,需要一些硬件支持來實現線性計算,但一些移動平臺對他的支持並不好,這種情況下往往只能退求其次,使用伽馬空間進行渲染和計算。

當我們在默認的伽馬空間下進行渲染計算時,由於使用了非線性輸入數據,導致很多計算都是在非線性空間下進行的,這意味着我們得到的結果並不符合真實的物理期望。除此之外,由於輸出時沒有考慮顯示器的顯示伽馬的影響,會導致渲染出來的畫面整體偏暗,總和真實世界不像。後面我們會了解伽馬校正的原理。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章