首頁 遊戲資訊 SIGGRAPH 粗讀——...

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

前言

SIGGRAPH 的分享和GDC的分享,是玩家和普通開發者得以窺見遊戲開發技術脈絡的重要方式——本身這種分享也是有一定的開源精神的,很多分享者不僅分享了自己的做法,還分享了自己為何選擇該方案以及實際遇到的困難,這一方面是相當誠實與寶貴的。

例如這期要談的頭發渲染,在實時渲染領域本身是一個持續發展、不斷改進調整的復雜渲染技術。在SIGGRAPH 歷程中頭發渲染就多次被提到過,Unreal、Frostbite等引擎及頑皮狗都分享過當時他們各自採用的改進方案,整體是一個不斷疊代優化的脈絡。

鑒於個人水平以及話題規模綜合考慮,這次聊頭發渲染我覺得最好的方式就是讀文檔,那麼這篇2016年的 《The Process of Creating Volumetric-based Materials in Uncharted 4》就有其承前啟後的意義,本身這個遊戲也是大家耳熟能詳的畫面很優秀的作品。

*註:本文的讀原文部分的翻譯全是我個人完成的,個人認為盡力做到了通順又精準。如需轉載需要詢問我。

1 頭發渲染概述

這部分我會簡單的概括一下頭發渲染的發展歷程和近況。

真實的頭發可以理解成是有鱗片表面的半透明類圓柱結構。(這里的半透明是Translucent)

在實時渲染領域,頭發渲染始終還無法做到真實世界頭發的大部分物理特性。這種無法做到主要是基於兩個方面的限制:一個是歸納其數學模型的難度,一個是數量上的難度。

目前認為最早的比較有效的歸納是Kajiya-Kay模型,它將頭發模擬成不透明的類圓柱結構,並且歸納了符合視覺結論的各向異性高光的效果(常表現為沿頭部一圈的高光效果)。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

後來提出的 Marschner模型開始將頭發考慮成透光的類圓柱結構,採用了更符合光傳播的歸納方式:對於任意入射光線,其後續光路被拆分成R、TT、TRT三部分,R是指表面直接反射(直接高光)、TT指從內部折射後從離開發絲(表現為背光時的投光)、TRT指從內部折射又發射後從入射表面離開發絲(表現為第二重較弱的高光)。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

以上兩個歸納模型都曾直接應用於離線渲染中,以實際一根一根建模的方式來渲染頭發。Marschner 模型在視覺上已經比較接近真實頭發了,但是數量級上無法滿足實時渲染的需求。因此實時渲染長期都需要用各種trick來近似達到接近的效果。

目前主流的實時頭發渲染方案還是Kajiya-Kay模型或基於預積分LUT的Marschner模型。LUT-Look Up Table是一種常見的多維預計算查找方式,引擎中預積分結果存儲成一張紋理圖,用紋理坐標來查詢。

主流的頭發建模方案基於半透明面片的(也稱為Hair Cards),如果看過之前半透明渲染的文章,會察覺此方案需要處理透明片元排序問題。

這里介紹一種已經被提煉得比較泛用的流程,通過4個Pass——預繪制深度(Z-PrePass)、繪制不透明多邊形、繪制半透明背面多邊形、繪制半透明正面多邊形。(細節的AlphaTest參數和ZWrite參數之類這里不列出了)

對於發梢的柔化,一般有抗鋸齒和AlphaBlend兩種常見方式,也可以進行結合。

2 方案選擇與基礎視覺

這一章開始就基本是讀原文,再輔以我自己的歸納或者補充了(打星號的部分)。原文是網上能下載到的一篇PPT文檔,一共70頁4部分,講解頭發渲染的是其中最長的第三部分。

其它幾個部分這里粗略介紹下:

——第一部分主要是吐槽立項時採用了過高的技術方案,實際落到PS4上發現太超前了帶不動,需要各種減量優化。

——第二部分講解了布料的方案,包含其細節紋理的方案及一個近似的次表面散射的方案。

——最後一部分講解了項目中積累的材質庫,例如毛發、眼睛、布料和皮膚的打濕效果等。這部分沒有拆解而只有一些舉例。

文檔中好多頁都是這個老哥痛苦的表情:

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

*以下部分開始翻譯一些關鍵的Page(圖片備注翻譯的是每張圖的題頭):

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

頭發絲是非常細的,同時它們是半透明的。

每一個頭發絲有著不同的法線,並且頭發之間會投下自陰影。要實現這種有體積感的頭發外觀,我們需要一個一個地解決遇到的問題。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

PlayStation4盡管在可容納的多邊形數量上有重大提升,但它仍然很難支持實時渲染數以百萬計的頭發絲。

由於我們並沒有太多時間供深入研究曲面細分著色器(由於項目周期),我們最終選擇了使用頭發面片的方式。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

1 高精度的透明紋理

2 使用了AlphaBlend和多重抗鋸齒

3 高精度的陰影解析度

以上幾點帶來了很棒的渲染效果

(But.. 這里省略一張圖 直接帶入前面老哥的痛苦表情即可)

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

這是一張Debug螢幕截圖——紋理Overdraw(太多了)

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

*Dithered Alpha就相當於之前文章介紹過的Screen-Door Transparency,是一種用不透明渲染近似達到視覺半透明的方式

對於大部分頭發,我們關閉了alpha-blending轉而使用dithered alpha,並結合TAA(分幀抗鋸齒)。這個方案的劣勢是角色移動快或者距離鏡頭遠的時候會帶來「鬼影」(ghost effect)效果。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

為了盡量規避這個問題(鬼影),我們必須調整透明像素裁剪的閾值。

*以下是個人的一點備註:

原文沒有提到什麼場合使用了AlphaBlend的模式,我的理解是alpha值大於某個閾值視為不透明,小於某個閾值還是需要使用AlphaBlend;或者僅過場動畫使用AlphaBlend模式。調高閾值是為了盡量多的片元以不透明的方式來渲染,但是以不透明方式渲染的頭發在TAA之後會產生鬼影。

實際玩過這個遊戲的能感覺出當時這種以不透明渲染為主的頭發方案帶來的效果還不錯。不過這才剛開始,後面他們還要解決一系列問題。

3 法線方案

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

1 我們使用的是頭發片的頂點法線,而不是單個頭發絲的法線

2 日光帶來的鋒利陰影使頭發面片看起來像不透明的物體,但它們預期應該呈現半透明的效果

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

角色美術手動調整了角色的頭發面片,使用了一些trick來讓它們盡量相互穿插,以體現體積感。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

為了使頭發面片之間的過渡顯得平滑自然,我們需要統一頂點的法線。

*這里應該是指在空間變換時對頭發的法線做處理,使其在世界空間中的方向基準有一致性。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

大部分時候,我們不能使用高精度的法線紋理貼圖(圖中展示了它們採用的頭發絲繪制編輯工具和Ramp紋理結構)。

4 陰影方案

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

*前2句翻過了,略過。Deep Shadow Maps是一個性能開銷較大的多層陰影方案。

解決方案:

減少陰影?這會使頭發看起來很平(削弱體積質感)。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

*原PPT中這里是一段視頻,展示了不同燈光亮度下的預渲染結果

這里展示了一個基於預計算的自陰影渲染結果,我們使用了57盞燈光,存儲了各種不同情況的陰影信息。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

我們需要找到一個好的折衷方案,來使預計算的陰影在大部分光照場合表現得真實。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

我們把燈光劃分成了5個組,這會減少我們把不同方向的燈光烘焙成陰影時遇到的問題,同時會會使烘焙出的陰影看起來更軟(一次烘焙燈光更多的原因)。

(這里又插入了老哥的痛苦表情,由於工期的原因他們也放棄了這個方案)

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

我們最終決定組合所有燈光並把陰影烘焙到一張紋理上,放棄了方向性的陰影烘焙信息。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

左圖:1K細節紋理,很多頭發面片共用其中一部分紋理

右圖:512尺寸紋理的預計算陰影紋理,存儲在uvSet2

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

我們曾考慮用烘焙陰影完全替代頭發的實時陰影。

雖然結果在編輯工具中看起來很好,但我們也要考慮運行時其它物體投影到頭發的情況,那會帶來「暗中發亮」的視覺錯誤。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

減少頭發上的實時硬陰影:

如果簡單的模糊實時硬陰影會浪費太多GPU性能。

我們之前提到使頭發更厚一些以減少overdraw和鬼影效果,但我們不需要對實時陰影也同樣處理。重置透明裁剪閾值是最簡單和高性能的減少實時陰影的方案。

*閾值等於1指只要不是完全不透明,都裁剪掉

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

右側是減少後的效果

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

為了使最終的陰影pass更有真實感,我們對烘焙陰影進行了一些調整:

1 為烘焙陰影添加LightWrap(LightWrap 是一種相對簡化的次表面散射光照方案)

2 使用光照探針的強度值及頭發顏色值對烘焙陰影進行調整(光照探針是一種基於球諧光照的布光和渲染元件)

*圖中的這些公式為頭發陰影這個問題提供了高性價比的效果,這些trick的組合可以認為是他們在頭發這件事上的「卡馬克時刻」。

*這里再解釋一下為什麼頭發可以使用烘焙陰影,因為頭發間的相對自遮擋關系在發型確定時可以相對確定,一定程度上確實不會穿幫。當然,如果是還考慮了各方向光照的烘焙陰影會有更正確的效果,只是他們做了trade off放棄了。

5 散射方案

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

我們決定採用一種GPU性能開銷很低的方案來時玩家有一種頭發有光散射的視覺印象(trick)。

這一方案有2個關鍵要素:

1 頭發絲間的散射

2 背光的散射

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

近似於布料的「Cheap SSS」,我們發現我們遊戲中的大部分頭發顏色是棕色、褐色、黑色甚至白色,添加微紅的顏色可以使頭發看起來更柔順和有真實感。

*本文中沒介紹他們布料的散射方案。SSS就是次表面散射,那是另一個trick。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

在這個課題上,我們把頭發視為球體。我們總結出背光散射主要基於光方向、攝像機角度和表面法線。

散射的量會基於頭發顏色、形狀、長度和光強度不同而變化。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

*代碼部分掠過,翻譯一下變量說明

通過調整scatterPower可以使scatterFresnel(菲涅爾 折射相關)的形狀和范圍看起來更可信。

在我們的遊戲中,scatterPower 是11時代表短發,9代表長發或蓬鬆的頭發。

lightScale代表光散射的范圍。對於大部分棕色頭發,我們選擇了一個更高的數值。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

正如之前提到的,我們不得不讓頭發絲盡量粗一些,以減少排序問題和overdraw。與此同時,為散射光添加一些變化是一個低開銷的提高發絲質量的方案。

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

1 Kajiya-Kay ——在這個模型的基礎上我們沒有進行改進

2 高光的變化——我們在所有頭發材質上共享了同一個變化遮罩層。原因:

1)高光變化僅僅用來給玩家提供一種更像頭發絲的感覺

2)視覺上不像其它變化遮罩那麼容易穿幫

3)我們的頭發面片的UV布局是統一化的

*這個效果我個人理解主要是在Kajiya-Kay高光的基礎上增加了一些擾動感

SIGGRAPH 粗讀——看看頑皮狗怎麼做頭發

左側是主要角色Nadine的質量,右側是多玩家模式下的質量。

*可以認為是採用了LOD形式或不同預設的畫質分級。

結語

原文檔的很多部分還是略微有一些缺少上下文的感覺,如果能有講解的原視頻會更容易理解,可惜我沒有找到相關的視頻。

如果有點遊戲開發經驗,可能會感覺他們當時考慮的一部分問題到現在變成特別初級的事情了;一方面是技術也在工業化領域不斷發展,另一方面是硬體性能在之後也有了質變(盡管還不到10年)。但考慮到年代感,能讀到一點他們當時真實的想法,並稍微揭開頑皮狗這樣的公司的神秘感,我覺得也是很好的視角。

在最新的實時渲染領域,已經可以模擬發絲(Strand-based 基於線的)進行頭發渲染了,但是數量上仍然有一定限制,因為除了渲染開銷外,物理模擬上的性能消耗也是不可忽視的。(玩過《死亡擱淺》的想必對strand這個詞不會陌生)

更科學的歸納模型還會考慮頭發的髓質及其影響,這在人頭發的占比不大,但是在動物毛發中的占比很大,因此動物毛發不能僅僅採用Marschner模型。這一點Games101的閆老師提出了他的模型並被正式採用到了工業領域,有興趣的可以去看Games101的相關內容(這一技術被用到了動畫影片《新獅子王》的渲染中,效果很好)。

無論是容納更多數量,還是歸納出更精進的模型,在頑皮狗完成《秘境探險4》製作並進行技術分享的2016年之後,這個領域又不斷有了長足的進步;當初用一堆trick堆出來的特別「真實」的渲染質感,又在不斷被後人挑戰。

我覺得遊戲行業從業人員本身是有其反功利樂於分享的一面的,而研究圖形技術及其應用的分享又是各類分享中的重頭戲。不同的公司在其不斷提升畫面的路上,都或多或少的各自迎來了屬於自己的「卡馬克時刻」,他們將其分享出來,帶來行業共同的發展,還能反哺到電影、動畫、教育等很多其它領域。

只要這份分享精神還在,這個行業就始終充滿希望。

最後是一些資料連結:

ATI早期的頭發渲染文檔

知乎上騰訊P13大神寫的介紹文章

SIG2016的本文下載地址

SIG2019由Frostbite提出的發絲方案

斯坦福Deep Shadow Maps的文檔

來源:機核