1. 程式人生 > >遊戲中的角色渲染技術之面板篇

遊戲中的角色渲染技術之面板篇

前言:遊戲中的角色渲染技術隨著近幾年來硬體機能的增長已經被大範圍地應用在了各類AAA大作中,本文會取一些遊戲為例,分類概述遊戲中的角色渲染技術。由於整個角色渲染的話題會比較長,這個主題會用兩篇來闡述,第一篇主要討論有關角色面板的渲染,第二篇會著重討論角色毛髮和其他的渲染。

壹 · 次表面散射

當光線從一種介質射向另外一種介質時,根據其行進路線,可以被分為兩個部分:一部分光線在介質交界處發生了反射, 並未進入另外一種介質,另外一部分光線則進入了另一種介質。反射部分的光照的輻射亮度(radiance)和入射光照的輻射照度(irradiance)的比例是一個和入射角度、出射角度相關的函式,這個函式就被稱之為雙向反射分佈函式(BRDF)。相應的,穿越介質的那部分光照的輻射亮度和輻射照度的比例就被稱之為雙向透射分佈函式(BTDF)。這兩部分出射光的輻射亮度總和和入射光的輻射照度的比例就被叫做雙向散射分佈函式(BSDF),即BSDF = BRDF + BTDF。



如果我們把光線行進的路線分為反射和透射,反射用R表示,透射用T表示,那麼光線從一個點到另外一個點之間行進的路線就可以用R和T表示,比如BRDF描述的路徑就是R,BTDF描述的路徑就是TT,除此之外可能還會出現TRT,TRRRT等光照路線,由此我們可以想見,在光線入射點的附近應該有許多的出射光線。實際渲染中,如果光線出射點的位置和入射點相距不足一個畫素,我們就認為入射點和出射點位置相同,這時候當前畫素的光照只受其自身影響;如果入射點和出射點相距超過一個畫素,則表示某個畫素的光照結果不僅僅受當前畫素影響,同時還受附近其他畫素的光照影響,這就是我們常說的次表面散射效果了。


《Real-Time Rendering》一書中對次表面散射的闡釋。紅色區域表示一個畫素的大小,當出射光線集中分佈在紅色區域內時,則認為次表面散射效果可以忽略,當出射光線較為均勻地分佈在綠色區域內時,則需要單獨考慮次表面散射效果。

 · 面板的實時渲染原理

面板是一個多層結構,其表面油脂層貢獻了面板光照的主要反射部分,而油脂層下面的表皮層和真皮層則貢獻了主要的次表面散射部分。


根據觀察[1],次表面散射的光線密度分佈是一個各向同性的函式,也就是說一個畫素受周邊畫素的光照影響的比例只和兩個畫素間的距離有關。這個密度分佈函式在有些地方稱為diffusion profile,用R(r)來表示。實際上所有材質都存在次表面散射現象,區別只在於其密度分佈函式R(r)的集中程度,如果該函式的絕大部分能量都集中在入射點附近(r=0),就表示附近畫素對當前畫素的光照貢獻不明顯,可以忽略,則在渲染時我們就用漫反射代替,如果該函式分佈比較均勻,附近畫素對當前畫素的光照貢獻明顯,則需要單獨計算次表面散射。據此次表面散射的計算可以分為兩個部分:

(1)對每個畫素進行一般的漫反射計算。

(2)根據diffusion profile和(1)中的漫反射結果,加權計算周圍若干個畫素對當前畫素的次表面散射貢獻。

由此可以簡單地理解為diffusion profile就是一張權重查詢表,不同的面板渲染方法,通常就是對diffusion profile的不同近似。我們根據加權計算所在的空間,將面板的渲染方法分為影象空間的方法和螢幕空間的方法兩類。


同一個diffusion profile在影象空間和螢幕空間的irradiance map效果和渲染結構對比示意圖。由於影象空間內一般畫素計算負擔較大(計算複雜度和模型個數正相關),並且針對每一個次表面散射效果的模型都需要若干張貼圖,視訊記憶體開銷也較大。而螢幕空間的計算複雜度和模型個數無關,且只需要一張螢幕大小的貼圖,因此目前主流方案均採用螢幕空間的次表面散射。

 · 影象空間的方法

影象空間的方法一般會將模型在其貼圖空間內展開,具體來說一般有三步:

(1) 在頂點著色器中將模型的UV座標作為螢幕位置輸出,同時輸出模型每個頂點的世界座標位置,在畫素著色器中對每個畫素進行漫反射光照的計算(如有陰影則需要將陰影考慮在內),得到所謂的irradiance map。

(2)對irradiance map進行一次或多次卷積操作,每次的卷積核由diffusion profile來確定,並生成若干張卷積後的影象。(可能需要多個pass來完成)

(3)將卷積操作後的影象每個畫素的值作為次表面散射的結果,再結合鏡面反射(specular reflectance)計算出畫素的最終顏色。


上圖是一種稱之為dipole approximation[1]的diffusion profile,我們接下來討論的大部分渲染方法都是基於dipole approximation的理論。根據上圖我們可以看出來,這個密度分佈函式能夠很好的用若干個高斯函式的加權和來近似,因此這裡提到的第一種做法[2]就是基於多pass的多次高斯模糊,並最終加權的方法。

[2]是一個標準的影象空間的做法,關於[2]中的方法,值得一提的是關於接縫的處理和irradiance的計算。

影象空間的做法產生的一個問題就是接縫,由於UV總會有縫合的位置,因此在縫合處必定會因為高斯模糊而混入背景顏色(比如下圖是黑色):


上面的Gi表示的是第i個高斯模糊,拆分成兩項主要是用來模擬面板油脂層下面的一層非常薄的吸收層,這層將少部分光線吸收並直接反射出來,因此這部分光線並不需要參與到次表面散射的加權中。


[2]能夠實現非常不錯的效果, 但從演算法分析來看效率比較差,儲存開銷也很高。對每個模型的繪製來說,需要一個irradiance map的pass,五次高斯模糊操作(一個二維高斯模糊再拆分為兩個一維高斯模糊,相當於10個pass),再加上最終合成的pass,總計就需要1+10+1=12個pass,並且每個pass中都有相當大量的取樣操作。方法[3]在[2]的基礎上進行了演算法的改進,具體來說,[3]主要改進了兩個部分:

(1)簡化diffusion profile,卷積操作的取樣數大幅減少,從每個畫素10 * 7(每個高斯模糊pass用到7個sample)= 70個取樣降低到了12個取樣,並且這12個取樣在一個pass中完成(原來需要10個pass完成)。

(2)減少了irradiance map中無效畫素的數量,這裡用到的技巧是,如果dot(N, V) < 0,則表示該畫素位於視角背面,也就是不可見的,就不需要對其進行光照的計算,據此,[3]的方法中輸出的螢幕座標為:

(u, v, dot(N, V) * 0.5 + 0.5),

在對irradiance map進行clear操作時,可以設定depth的clear值為0.5,並將depthTest的方法設定為GEQUAL,這樣,只有朝向攝像機的畫素才會被渲染,其餘被剔除(相當於利用可程式設計管線配合深度測試實現了背面剔除的效果)。在模糊的pass中,則只對可見畫素進行取樣和加權。這樣就進一步減少了取樣數。

方法[3]的效能較之於[2]有大幅度提高,因此被頑皮狗運用在了他們《神祕海域2》的過場動畫中[4]。但根據我實現後效果對比來看,[3]的效果較之於[2]還是有比較明顯的差異,因此只能說是犧牲了效果實現了遊戲中要求的實時性。


螢幕空間的方法類似於影象空間的方法,只是計算irradiance時輸出的位置不是UV座標而是模型的投影座標,此外還需要將螢幕空間中屬於面板的材質的畫素用stencil buffer標記出來,然後對標記出的面板材質進行若干次卷積操作,卷積核的權重由diffusion profile確定,卷積核的大小則需要根據當前畫素的深度(d(x,y))及其導數(dFdx(d(x,y))和dFdy(d(x,y)))來確定。


方法[5]在diffusion profile的近似方面和方法[2]沒有本質上的區別,唯一的區別僅在於前者在螢幕空間,後者在影象空間。由於取樣空間發生了變化,因此螢幕空間的方法需要根據當前畫素的深度及其導數資訊,對周邊畫素的取樣UV進行橫向和縱向的縮放,具體的縮放係數計算可以閱讀原文。Unreal Engine3和CryEngine3就是採用了上述方案。

螢幕空間的方法的好處是演算法的時間複雜度可以說是一個固定開銷,和面板材質的模型個數無關,缺點是這個固定開銷比較高。例如方法[5]提到的,需要在螢幕大小的貼圖上進行12次高斯模糊,實際上仍然是不小的開銷(在待渲染面板材質較少的情況下甚至可能比影象空間的方法效率更差),因此方法[6]在方法[5]的基礎上提出了螢幕空間框架下的另外一個diffusion profile的簡化思路。從工程的角度來說,方法[6]將diffusion profile的近似用兩個一維的卷積操作來表示,每個一維卷積根據最終效果可以用11-25個取樣點不等,而卷積核的大小則由當前畫素的深度以及使用者定義的面板柔和程度(sssWidth)決定。從原理的角度來說,不同於方法[2]和[5]將diffusion profile用若干個高斯函式的加權和來表示,方法[6]把diffusion profile看做一個矩陣,利用奇異值分解(SVD)的方法將其分解為一個行向量和一個列向量,並且保證了分解後的表示方法基本沒有能量損失(只是我的直觀理解,歡迎數學好的同學給我解釋一下詳細的數學推導過程)。目前來看,方法[6]是兼顧了效能和效果的最佳方案。

伍 · 預積分的方法[7]

影象空間的方法和螢幕空間的方法很大程度上都是通過周邊畫素對當前畫素的光照貢獻來實現次表面散射的效果,從原理上區別不大,方法之間的區別通常只是在於如何去近似diffusion profile,在效能和效果上有一個較好的trade off。而Pre-Integrated Skin Shading的方法則不同於上述方法,是一個從結果反推實現的方案。我們在觀察次表面散射效果可以發現:

(1)次表面散射的效果主要發生在曲率較大的位置(或者說光照情況變化陡峭的位置),而在比較平坦的位置則不容易顯現出次表面散射的效果(比如鼻樑處的次表面散射就比額頭處的次表面散射效果要強)

(2)在有凹凸細節的部位也容易出現次表面散射,這一點其實和(1)說的是一回事,只是(1)中的較大麴率是由幾何形狀產生的,而(2)中的凹凸細節則一般是通過法線貼圖來補充。

結合以上兩個觀察,[7]的思路是把次表面散射的效果預計算成一張二維查詢表(具體的預計算方法可以檢視這篇文章[8]),查詢表的引數分別是dot(N, L)和曲率,因為這兩者結合就能夠反映出光照隨著曲率的變化。


上圖右邊就是曲率顯示出來的效果,可以看出類似額頭這樣的位置曲率是比較小的,而鼻子等位置的曲率就比較大。

上述方法解決了(1)的問題,但對於(2)提到的一些凹凸起伏的細節,由於它不是由幾何造型產生的,因此無法用上述曲率計算的方法確認其是否有明顯的次表面散射效果,因此作者進一步結合了bent normal的方法來實現這些細節處的次表面散射效果。

Bent Normal其實不是專門用來處理次表面散射專有的方法,可以應用於很多預計算複雜光照的情況,簡單的說就是把包含AO,陰影,次表面散射之類的複雜光照資訊pre-bake到法線裡面,然後計算光照時使用pre-bake得到的法線,結合正常的光照計算方法,就能得到比較複雜的光照效果。


畫面效果至今也是我覺得相當巔峰的《教團1886》在他們的渲染角色渲染部分[9]也使用了pre-integrated的方法,但是具體方法不是很清楚,希望瞭解的朋友可以告訴我。

陸 · 高光的計算

在[10]中提到了一種用於面板高光渲染的名為Kelemen/Szirmay-Kalos specular BRDF的模型,並將其中一些引數預計算成了查詢表用於加速計算該BRDF,該模型計算面板油脂層的高光效果從視覺上要遠好於簡單的Blin-Phong BRDF模型。具體公式可參見GPU Gems3的相關文章。

柒 · 透射(transmittance)的計算

我接觸到的很多美術都把次表面散射和透射混為一談,實際上這兩者是面板渲染中兩個完全不同的技術點。按照我們之前的光路分析,次表面散射反應的是TRT,TRRRT這類光路(奇數次反射),也就是入射光和出射光在介質的同一側,而透射(或者我們常說的透光)則是反應TT,TRRT這類光路(偶數次反射),也就是入射光和出射光分佈在介質的兩側。


在渲染技術上,這兩者也完全不同,透射的實現思路相對比較直觀,也容易理解,可以分為兩步:

(1)計算光照在進入半透明介質時的強度

(2)計算光線在介質中經過的路徑長度

(3)根據路徑長度和BTDF來計算出射光照的強度,這裡BTDF可以簡化為一個只和光線路徑長度相關的函式

方法[10]就是一個基於螢幕空間的透射計算方法,它除了需要延遲渲染常用的GBuffer(用到的資訊包含depth, normal和albedo,主要用來近似入射的光照強度)外,還需要計算透射用到的光源視角的深度圖(一般是主光源的深度圖)以及相關的光源攝像機矩陣。這個深度圖和矩陣用來計算光源在從當前畫素位置射出時經過的光線路徑長度。因為BTDF隨著路徑的衰減比較快(指數平方的衰減),因此只有在較薄的結構(比如人的耳朵或者手掌邊緣)上才能看到明顯的透射效果,而當半透明物體背面被其他物體擋住的時候,體現在深度圖上還是光線路徑較長,因此也不會產生錯誤的透射效果。方法[11]用出射點法線的反方向近似背面入射點的法線方向進行入射光強度的計算,具體的計算公式可見原文。

捌 · 總結

面板的實時渲染在近十年來一直算是一個比較熱門的話題,每年也有不少相關的演算法和改進的提出,從目前的遊戲中呈現的效果來看,面板可以說已經達到了非常逼真的程度。從這裡歷數的各個方法出發可以看出面板渲染隨著其他渲染技術的演化,例如從影象空間的方法到螢幕空間的方法的演化很大程度上要歸功於延遲渲染管線的普及以及硬體效能的提高。儘管螢幕空間的方法較為主流,但上面提到的方法在特定的環境下也都仍然有應用範圍,另外這些方法在核心原理上並無太大區別,可以根據具體的情景和需求做選擇。

引用

[1] Henrik Wann Jensen, Stephen R. Marschner, Marc Levoy, Pat Hanrahan. A Practical Model for Subsurface Light Transport

[2] d'Eon, Eugene. NVIDIA Demo Team Secrets–Advanced Skin Rendering

http://developer.download.nvidia.com/presentations/2007/gdc/Advanced_Skin.pdf

[3] John Hable, George Borshukov, Jim Hejl. Fast Skin Shading

[4] John Hable. Uncharted 2: Character Lighting and Shading

http://advances.realtimerendering.com/s2010/Hable-Uncharted2(SIGGRAPH%202010%20Advanced%20RealTime%20Rendering%20Course).pdf

[5] Jorge Jimenez, Veronica Sundstedt, Diego Gutierrez. Screen-Space Perceptual Rendering of Human Skin

[6] Jorge Jimenez, Károly Zsolnai, etc. Separable Subsurface Scattering

http://iryoku.com/separable-sss/

[7] Eric Penner. Pre-integrated Skin Shading

[8] Eric Penner, George Borshukov. GPU Pro 2, Pre-Integrated Skin Shading

[9] David Neubelt, Matt Pettineo. Crafting a Next-Gen Material Pipeline for The Order: 1886

http://blog.selfshadow.com/publications/s2013-shading-course/#course_content

[10] Eugene d'Eon, David Luebke. GPU Gems 3, Chapter 14. Advanced Techniques for Realistic Real-Time Skin Rendering

https://developer.nvidia.com/gpugems/GPUGems3/gpugems3_ch14.html

[11] Jorge Jimenez, David Whelan, etc. Real-Time Realistic Skin Translucency

http://iryoku.com/translucency/downloads/Real-Time-Realistic-Skin-Translucency.pdf

[12] John Isidoro, Chris Oat, Jason Mitchell. Next Generation Skin Rendering

http://game-tech.com/Talks/SkinRendering.pdf