前言
提到渲染材質
,很多人能想到的可能最常被提到的“PBR材質”或是“SSS材質”之類的縮寫,或是“毛髮”、“衣料”、“水體”等更接近現實中物體的概念。
其實仔細想想就會發現,前兩者更接近一種經驗模型,通過近似歸納的算法來提供儘量寫實的效果;後兩者的實現則是一套或多套不同複雜度的組合方案,基於不同引擎、不同開發者、不同平臺都會有多元的方案,但幾乎都無法通過一次簡單的繪製來完成。
但不管是哪種方案,在目前大多數的情況下,在實時渲染的引擎中還是以提煉好的功能模塊為主——這意味著一旦需要一個新的複合材質,就需要一定程度的自定義開發,而且往往有比較多的限制(後面文中也會介紹到)。
本次會介紹的是虛幻5引擎中2023年提出的一套被稱為基底(Substrate)組合的方案,這是一套藝術家易用性為主的複雜材質方案。
本文還是以翻譯原文PPT頁及解說稿為主,打星號的部分則是我個人的補充。由於篇幅原因還是會拆成上下兩篇,上篇介紹Introduction、Slab、Operators這幾部分。
1 引入動機——Introduction / Motivations
The current material model in UE is based around the notion of Shading Models. A shading model target a specific set of visual features, proper to a given appearance. For instance, we have a shading mode for general dielectric/conductor, for ClearCoat, for Subsurface scattering, for Cloth, ..
當前UE中的材質模型是基於著色模型(Shading Models)的概念建立的。一個shading model是以一組視覺特性為目標,為實現一種特定的外觀。例如對於絕緣體、導體、清漆、次表面散射、布料等。
每個shading model能覆蓋一定範圍的外觀譜系(spectrum)。
多數現存的shading model使用的是藝術家的實現方案,同時保持了高性能。例如《要塞英雄》最高能以120Hz運行。
另外,所有這些shading models在我們的不同渲染路徑之間保持視覺一致(visually consistent),無論是光柵化路徑,還是光線追蹤方式的Lumen或path tracer路徑。
然而,儘管內置了大量特定的shading model,這種方式還是阻止了藝術家自由的進行創作。(*原文用了silo一詞,直譯過來的一個意思是發射井)
例如,一個藝術家希望將SSS表面與金屬表面混合。
在shading model的方案中,由於我們對一個像素只能提供一個shading model,這種混合就會導致硬邊界或不連續。
在合適的混合後,過渡外觀會變得更平滑。(使用了Substrate方式來演示)
另一個例子,如果一個藝術家想創造一個基於次表面的材質,但包裹在塑料殼或被汗水環繞呢。
同樣的,僅通過shading model的方式,無法同時兼顧SSS模型和覆蓋層(Coat)模型的特性。
另一個更進階的例子。想象藝術家想構建一個進階的布料材質。這種布料有著兩種絲線的集合(例如,有著各向異性高光),相互垂直以模仿某種編織圖案。並且布料還會被一層塑料層覆蓋。
再次的,這種需求也無法通過shading model方式實現。
這個例子是我們現有shading model中的一個限制,更多是與我們的實現方式有關。
UE default shading model uses the notion of metalness to make the distinction between conductor and metallic. When transiting between the two types, an undesired halo can appear. This is a known limitation in PBR, but something we couldn’t address before dropping the notion of metalness in Substrate.
UE的默認shading model使用了基於金屬性(metalness)這一概念來區分導體和金屬。當在兩種類型之間過渡時,會出現一個預期外的光暈。這是PBR中一個已知的侷限,不過在引入Substrate之前我們無法解決。
所有這些例子都突出了這項事實——如果一個藝術家想得到特定的進階外觀效果,都需要對現有shading model的一些修改,甚至創建全新的shading model。
每次都為這種例子添加新shading model,可能會導致參數排列組合式的爆發。
同時,添加新的shading model也需要工程師花時間來實現並確保其如預期一樣生效。
長期來看,這樣做也增加了整體的著色開銷。一個更復雜的shading model在實現時,通常也會增加分支邏輯和參數註冊上的壓力,最終拖慢整體著色的性能。
為了應對這種性能倒退,人們可以調整項目設置,按需啟用這些進階shading model。但這又帶來維護和測試上的問題。
此外,UE是基於延遲渲染架構,這限制了可存儲的輸入的數量。(*下篇介紹數據存儲中會提到)
在所有這些限制下,我們感覺很難引入並支持進階材質。
這時Substrate方案出現在了視野裡。
Substrate是一套複合框架,基於內建好的材料模塊藝術家可以集成或創建複雜的外觀。
然而,這套框架需要能保持和舊系統一樣的性能,也要能保持和舊方案在不同渲染路徑上的視覺一致性。
此外,這套框架需要是動態規模的(scalable)。對於一種複雜材質,我們不希望為高端設備和低端設備重新指定不同的變體。
在工業界存在很多材質的標準和規範。MaterialX似乎是在工業界有著最廣泛應用的框架——然而,這些規範都有一些不符合我們需求的限制。
要麼它們依賴具體的拓撲定義(Autodesk Std Surface, Adobe Surface, OpenPBR),使我們無法創建更多進階的表面模型;要麼它們依賴一堆BSDF(*原文是a soup of),這阻礙了高性能地進行實現(有著太多分支和循環),也很難基於簡單的規則來動態控制材質複雜度。
*BSDF(Bidirectional Scattering Distribution Function) ,雙向散射分佈函數。和BRDF差一個字母,但這個複雜度往往高很多。
這就是我們開始開發Substrate的原因。如前所述,Substrate是一個框架,它基於以下3個概念構成:
- Slab是我們描述具體材料(matter 也可以理解成材質特性)的方式。
- 操作符Operators是我們對其做組合操作的方式。
- 樹結構Tree是材質的拓撲結構描述方式。例如,不同的材料之間的相互關係和組織方式。
為了演示,讓我們舉一個例子。這裡是一個由兩種材質特性構成的材質:
一個基礎層,由金屬和各向異性纖維構成,這被表達為一個Slab。
另一層由有一定吸收率的絕緣體構成,這被表達為另一個Slab。
兩個Slabs之間通過上下層方式來組織,通過一個垂直層操作符(vertical layering operator)連接。
*這裡是演講人的全部章節安排。這裡我的上篇中覆蓋了:Slab、Operators這兩部分。
2 Slab定義材料——Slab Defining Matter
*Slab直譯是厚板、 板坯等,後續以作者拆解的多層結構作為理解方式,本身保持英文不翻譯了。
在這個架構中,slab被用來表達一類材料。這也是我們與大多數已有shading model統一化的方式。
你可以把一片材料想象成一個表面(interface)和有著一定厚度的一層介質(medium)組合而成。
所有屬性都基於物理屬性,這幫助我們不通過太多複雜的再參數化(convoluted reparametrization)就能描述一個材料。通過使用參數單元也能幫助我們保持一致性——它幫助我們思考如何控制光照與這些屬性的交互(計算)方式。
一個反例就是gltf,或是其它有著漫反射透射(diffuse transmission)或是顏色吸收(color absorption)等等特性的規範,你控制的是完全不符合整體直覺的一些單一的影響項。
*gltf是一套3D模型的行業規範,包含了材質部分。
表面層如你能想象的一樣簡單——它定義了兩層介質之間的分割層,以一個微表面場的方式被建模。這部分的工業標準是簡單而實用的。
這層表面通過一些不同的屬性進行參數化,下面我們會介紹其中的一部分。
*這裡的參數有的描述起來可能很複雜,但只要理解最終影響的都是某一像素最終計算的光照值即可。
為描述反射率,我們使用了一個F0參數(這裡指一個表面的法向入射的反射率)——它是便利且歸一化了的,不像一些複雜的折射率(如IOR)參數。(*這裡說反射率也包含了折射吸收,稍微瞭解的應該知道這本來就是光傳播在介質同時發生的一種材質特性,如圖)
為了描述更進階的導體材料,我們使用了一個F90參數,它影響色度(chromaticity 對應HSV顏色空間)而不是強度,不減少透射時的能量。這個參數是其中最不“物理”的一個參數,後續我們可能用Adobe的F82來替代它。這個特性是一個可選項,意味著你只有當用到它時才會額外消耗性能。
對於微表面描述,我們使用一個簡單的基於GGX的NDF。再次的,工業標準在這裡是足夠好的,我們不用自己從頭造輪子。(NDF是Normal Distribution Function法線分佈函數。GGX、NDF在Games101中有介紹,另外參數如圖所示)
作為默認,我們的原始高光粗糙度是與漫反射粗糙度結合的。
對於各向異性(anisotropy),我們使用了Kulla&Conty參數標準。
We adapt our tangent basis representation based on usage. If a surface is isotropic we will store a simple normal, but if the surface is anisotropic we will store a full tangent basis with two octahedral encoding (quantized onto 32bits).
基於自身使用情況,我們調整了基於切線的表達方式。 如果表面是各向同性(isotropic)的,我們僅存儲一個簡單的法線,而對於各項異性表面我們需要存儲基於兩個八面體編碼(two octahedral encoding 離散化裝入32bits數據)的切線數據。
備註:後續我們會發現,一個材質可能由多個Slab構成,但都共享內存中同一份切線數據。更多的說明會在後續講存儲的章節介紹。
可能有點神秘的是(原文用了esoteric這個詞),我們有一個第二級的GGX波瓣(lobe),它以較低的權重混合進了第一級的波瓣中。
這樣做的主要意圖是為表面描述添加更長的“高光尾段”(‘specular tail’ 參考圖中分佈函數)。一個基礎的GGX NDF有其自身的尾段分佈,不過對於特定的製成材料,它的高光波瓣尾段可能更長。通過混合兩種特定波瓣,我們可以重現這一朦朧的外觀效果,使我們同時可以看到銳利的反射和朦朧模糊的部分。
我們也開放出了一個輔助節點(材質藍圖中的)用於以特定方式混合兩個波瓣。
一個Slab可以描述其表面層的粗糙。經過這一粗糙層光以水平方式散射(圖中右側的波瓣分佈),而不像高光波瓣那樣(垂直分佈)。
這些粗糙層總是在高光波瓣的上層,並且有著完全的參數化表達(roughness, albedo, coverage *圖中以Fuzz開頭的部分)。
*這裡舉例的就是衣料表面的絨毛——Fuzz。
正如所提到的,我們使用一個微表面場來為表面建模。這些微表面有著特定的尺寸,因此當我們靠的足夠近時,我們就會看到它們。這時就會出現“閃亮”(glints)的效果。
你可以有選擇地接入glint特性,並通過一個密度控制器來控制它們的尺寸,在表面UV上參數化。為了達到實時渲染的要求,我們使用了Chermain的方案——使用預計算的LUT來定義不同LOD下的glint行為。(*LUT——LookUpTable)
對於給定的glint密度,當你的視野遠離表面時,像素的覆蓋範圍內逐漸會包含越來越多的glint。在達到特定距離後,它就會通過一個單一的GGX波瓣來覆蓋。
這主要用於車漆、雪和其它需要閃亮效果的地方。
作為前述的反射率參數的補充,一個Slab可以補充提供一個SpecularLUT。這個LUT定義了一個統一化的“染色”效果,參數化基於視線和光照角度。
這一LUT可以通過顏色坡度或紋理的方式來控制——參數化也是基於視線和光照角度。
*這裡解釋下,這是一個二維的LUT,即一個軸是編碼好的視線角度,另一個軸是光照角度,座標對應的唯一點是Tint結果值。
這在表現乳白色(opalescence)外觀或一些複雜的車漆模擬時都會使用到。
當表面有粗糙度時,光線會被散射(折射)。這能創造出一個粗糙傳播的外觀效果。在這種情況下,粗糙的外觀是由表面造成,而不是介質散射(例如次表面傳播)造成。
為模擬這一效果,我們從表面的反射率參數中恢復出IOR折射率。
對於我們的光柵化路徑,我們預計算了以粗糙度和深度(厚度)為維度的LUT,以計算光的分佈。之後通過一個後處理pass來計算相應的模糊效果。
對於光追路徑,我們可以簡單地依賴一個射線檢測來計算這個效果。
*這裡的粗糙度散射和之前絨毛的粗糙度不一樣,而且這裡更多關注的是折射問題。
目前為止我們沒有涉及的一個方面是管理不同slab之間的能量傳播。
When an incident light ray hits the interface, it will hit a given microfacet. At that point, it might be reflected, transmitted, or hit another microfacet as it get reflected
當一個間接光照的射線命中表面時,它會命中給定的微表面。在特定點上,它可能被折射、穿過或命中另一個微表面而被反射。
在後一種情況下,我們需要模擬這裡的多次“彈射”以避免損失能量。這在高粗糙度的表面會非常明顯,尤其是對導體而言。為模擬這一能量傳遞過程,我們採用了Turquin的方案。(對比圖見圖中紅框部分,用經驗性的方式補充了一部分應該反射的能量)
第二個重要的方面是,我們需要確保傳遞了正確數量的能量到下層介質中。例如,所有未被表面層折射或吸收的光能,需要傳遞到介質中去。對於這一點我們使用了表面定向的預計算查找表——也如Turquin的方案中所述。
*篇幅原理這裡就不展開解釋Turquin的方案了,有興趣可以去搜搜原文。文末附了論文鏈接,另外我記得在Games202中也有相關講述。
結束了表面層的介紹,讓我們深入介質層的部分。
Our medium is based on a volumetric formulation. The intent was to have a unique parameterization which can represent the continuum between diffuse to fully transparent surface and abstract any artistic parametrization.
我們的介質層是基於一個體積化表達式(volumetric formulation)。這樣設計的意圖是能有一種獨特的參數化方式,以連續體(continuum)的方式描述從漫反射到全透明表面的材質,以及抽象出任何藝術表達用的參數。
因此,一個介質由以下部分來描述:
- 無效區段(Mean free path),例如一個光子命中介質的粒子前自由通過的距離。後面縮寫成MFP。
- 反照率(Albedo 也翻譯成漫反射係數或反射率),例如用以總括多次彈射後的介質反射率。它是散射和吸收兩個係數的結合。(*Albedo are just the dual of scattering & absorption coefficients)
- 各項異性的相函數(Phase function),用以描述命中介質粒子後的散射方向。
- 厚度(Thickness),用以描述介質的深度。
通過這樣的參數化方式我們可以覆蓋完整的外觀譜系——從全向漫反射到全透射。
簡單來說,你也可以把它看成:
- Diffuse albedo控制外觀的“乳白色”程度
- Mean free path控制表面的透射程度
從方案實現的視角來看,你也可以把這個外觀空間劃分為4部分:
- 不透明漫反射(opaque diffuse)部分,視線無法透過材質表面,光也無法從像素覆蓋區 中漏出
- 光學厚(optical thick)的部分,視線無法透過材質表面,但像素覆蓋區中有些光能漏出
- 光學薄(optical thin)的部分,視線能透過材質表面,同時仍有一些光散射發生
- 透明(translucent)部分,作為前者的一種特殊情況,沒有任何內部散射
下面讓我們看看如何實現這幾部分。
對於漫反射的部分,我們使用了一個反光(retroreflective)模型——基於Chan的方案。
This approximates a diffuse microfacet model, who the roughness is coupled with the primary specular.
這種方案提出了一種漫反射微表面模型的近似,將粗糙度與基礎高光相結合。
其中粗糙度控制反光的量,例如光在掠射角(幾乎90度的夾角)方向反彈的量,這在高粗糙度時會尤其明顯。
如圖表中所示,這種“管理方式”(原文是regime,直譯是體制)是在像素覆蓋區相對MFP時足夠大時採用的。
當像素覆蓋區和MFP相比較小時,我們就進入了optical thick的模式。
此時我們應用常規的SSS技術:
- 對於光柵化路徑,我們使用基於Xie的方案的後處理pass
- 對於光追路徑,我們使用隨機遊走算法(random walk algo)
需要注意,在我們的實現中,如果一個材質由多個slab構成,這一計算只應用於最底層的可見slab。對於光柵化路徑,我們可以退回到例如wrap lighting等方案以提升性能。
*wrap lighting是次表面散射近似方案中的一種簡化方法,它的核心思想是做顏色移位來模擬吸收與散射。
Optically thin的情況是實時渲染中很不常見的,因此也很難處理。它拼合了一部分視線能透過的半透明,以及一些大範圍的散射(朦朧)效果。
在我們的情況中,我們聚焦於它在疊在另一個slab上層的情況。
這個技術方案包含兩部分:光線散射回彈,以及光線透射通過表面。
- 對於透射的部分我們使用一個簡單的Beer Lambert模型來計算吸收。
- 對於散射的部分,我們依據預計算的LUT——參數化來自單次散射的反照率(從漫反射率估計),重新縮放後的MFP(相對於1M的厚slab),以及視線、光照角度。這一LUT通過離線計算這些條件組合的情況來得出(MFP/albedo/view/light)。
這一方案目前只解決了第一步,也就是能量的部分,但沒有處理光的“擴散”以顯示成朦朧表面的部分。(*原文這裡提到以後再加這個特性)
最終這種特定情況下的optically-thin表面,雖然有了視覺“透過”的效果,但沒有任何散射效果。
對於光柵化路徑,我們使用雙來源混合(dual source blending)的方式來處理透射的部分。沒有任何神奇的部分,都是工業標準方式。(*這裡就是常說的Alpha blend)
The important part here is that we decoupled the notion of transmission from the notion of coverage. This split the convoluted ‘opacity’ term into two explicit notions. More on that later.
重要的部分是我們將光傳播(transmission)的概念從光覆蓋(coverage)的概念中解耦出來了。通過這麼做把晦澀難懂的“不透明度”(opacity)概念拆分成了兩個確定的概念,後面我們會提到。
從中我們可以看出,表面材質元素在視覺上的規模(scale)決定了我們在材質用應用的策略。(*基於MFP和Pixel Footprint像素覆蓋區的比較)
我需要強調,採用的單位是很重要的。使兩者都採用合適的世界空間單位,能有助於如何渲染一個材質的外觀。
最後,介質的厚度也需要列入考慮。它定義了光在其中傳播的距離,這會影響“光的衰減“結果。
當它是單一層,或多層材質中的最底層介質,我們需要考慮這兩種情況:
- 如果表面被視為是“厚”的,它的厚度是被幾何體的範圍來定義的。在光柵化路徑中,我們可以使用一個常量、一個SDF或一個shadow map來估計厚度值。對於光線追蹤路徑,它能夠通過射線相交計算得出。
- 如果表面是“薄”的,例如幾何體是一個平面(植物、衣物等),它的厚度需要通過另外的方式定義。此時我們在材質層級來定義它的厚度,在根節點上以Surface Thickness參數輸入。
For other layer, we also define the thickness through the graph. This is only necessary when a slab is ‘coating’/’layered on top of’ another slab. In such case, the thickness of the top slab is provided by the Vertical operator.
對於其它層,我們也能通過材質圖來定義厚度。這隻在slab是其它層上的覆蓋層(coat)時是必須的。此時,上層slab的厚度是通過Vertical運算符來提供的。
*小結一下,這裡位置slab方案主要提煉了目前為止的主要的透射材質方案,併為之設計了組合的可能性。其中在介質層有其獨創的設計,而在表面層方面更多的是把現有的工業化方案進行組合。
3 運算符——Operators
我們有3種主要的運算符:
- 水平混合(Horizontal mixing)——類似之前兩種材質之間的過渡和混合
- 垂直層疊(Vertical layering)——想象兩個材質之前的覆蓋層關係
- 覆蓋權重(Coverage weighting)——可以把它想象成alpha blending,使一種材質看起來更“透明”
但首先,我們需要適當地定義一些術語(terminology)來確保談的是同一件事。
假設我們有一個粗糙金屬球體作為基礎。當光照命中表面時,它直接基於它的粗糙金屬性反射。
現在,假設我們把它覆蓋在一層綠色的絕緣體材質下。
在圖中最左側,材質的厚度很大,因此當光線命中表面時,它只通過絕緣體層反射。所有光傳播都被絕緣體層吸收了。此時,絕緣體層的傳播係數(transmittance)是0。
如果我們降低絕緣體層的厚度,僅有一部分光會被它吸收,光線就會達到下方的粗糙金屬表面。此時,絕緣體層的傳播係數就大於0。
如果我們進一步降低厚度,更少的光會被吸收,則傳播係數會進一步增加。
在前三個例子中,絕緣體層始終存在——從任何角度的光都會命中它。因此絕緣體層的覆蓋率(coverage)是1;另一方面,在最後一個例子中,絕緣體層不存在,它的覆蓋率就是0。
依此推演,0.5的覆蓋率意味著光有50%的幾率命中絕緣體層和粗糙金屬表面,以及另50%幾率直接命中金屬表面。
因此覆蓋率定義了slab的存在率,而傳播係數定義了一個slab的“1-衰減係數”。
組合在一起,兩者共同定義了一個slab的通過率(throughput)。
水平混合運算混合兩種材質slab。
假設有一個金屬材質,我們將其“水平混合”至一個SSS材質。這意味著我們將分別計算兩種材質,並混合兩者的計算結果。混合係數源自slab覆蓋率的定義。第一個slab將有著等於mix值的覆蓋率,同時第二個有著“1-mix”值的覆蓋率。
而垂直層疊操作,則允許一個材質slab覆蓋在另一個之上。
假設我們有一個各向異性的纖維材質。我們想為它覆蓋一層絕緣體材質,這樣它的傳播係數就會影響纖維材質的外觀,產生一層微紅的染色效果(如圖)。
多層效果可以合併,例如我們再將另一層粉塵材質覆蓋再其上,它又會影響傳播係數以及底部層的通過率。
最後,則是覆蓋權重運算。
我們從一層有著一定吸收率的絕緣體開始。光傳播通過這層表面,被部分吸收,產生圖中所示的微黃的傳播係數。
通過覆蓋率的權重調整,slab表面“存在”的像素數量被修改了:
- 如果覆蓋率是1,我們能看到完整的表面。
- 如果覆蓋率是0,則完全看不到。
- 在兩者之間的覆蓋率參數,我們能看到表面的一部分。
*類似天氣預報的降雨概率,也是一種覆蓋率。
很重要的一點是,這種組合操作能明確區分出調整厚度和調整覆蓋率的外觀區別。
以圖中的橙色絕緣體為例:
通過垂直層疊操作,我們可以調整頂層的厚度來影響底層的光傳播係數及頂層的通過率,但始終能看到這一層的高光反射。
而通過覆蓋率操作,當我們調整頂層的覆蓋率,上層的光傳播係數保持不變,但它的通過率改變了(由於覆蓋率的變化)。(*以圖中為例,覆蓋的分佈是一種離散的情況)
結語
以前常有“五彩斑斕的黑”這個調侃,確實從靜態的繪製來說某種意義上是無法實現這種效果的。但實際上自然界人們確實能從昆蟲的甲殼上觀察到這種視覺現象,而實際上雖然“五彩斑斕”“一閃一閃”和“黑”在同一時間確實不是同時存在的——這和觀察角度、光照等都有關係,但在一個動態時間的尺度上,這種材質確實是存在的。
通過這次介紹的複合材質系統,這種材質確實能做到了,而這是之前通過普通的shader model無法實現的。
下篇會介紹這篇分享的後面部分,覆蓋一些更偏向架構和數據設計的部分。
最後是資料鏈接:
Authoring Materials That Matters - Substrate in Unreal Engine 5 的PPTX
Practical multiple scattering compensation for microfacet models這篇論文的一個下載地址
Real-time subsurface scattering with single pass variance-guided adaptive importance sampling 論文的一個下載地址
Material Advances in Call of Duty: WWII 在動視的文章地址