SIGGRAPH 粗讀丨看看虛幻5中的全局光照數據緩存(下)


3樓貓 發佈時間:2024-08-30 16:33:40 作者:Hakumen Language

前言

這周無疑是屬於《黑神話:悟空》的一週。在這之前,使用虛幻5的規模較大的遊戲項目似乎還是《墮落之主》《最終幻想16》等——這些遊戲當時也面臨了一定的性能上的取捨和挑戰,後者直到最近上PC還是有嚴重的性能問題。
總的來說,這幾年真正的第三方敢於用虛幻5的lumen新特性去做高畫質高精度的3A遊戲的還是不多,可見雖然這個引擎能達到的上限高,但使用門檻也是一點都不低——更不要說做擴展開發了。不過我認為,這就和這個世界上的大多數有技術含量的事情一樣,克服了門檻和實現了一定規模,就可能孵化出《黑神話:悟空》這樣的作品。而且,Epic作為引擎的提供者,肯定是樂見這樣的高質量產品越來越多的。
這篇文章是兩篇文章的下篇,上篇中介紹了這個技術方案的提出及管線架構,以及部分核心結構和採樣方式的設計。這篇會繼續介紹剩餘的技術細節。
原文檔是以PPT為主,每頁下面搭配了一行左右的解說稿。本文還是以翻譯原文PPT頁及解說稿為主,打星號的部分則是我個人的補充。Radiance 和Irradiance兩個詞也還是儘量保持英文原文。

1 空間過濾

*其實Filter翻譯成過濾感覺也是缺乏特別準確的對應,狹義的過濾感覺可能是“按一定規則排除一部分數據”,而廣義的Filter可以理解成“輸入一些東西,然後按一定規則輸出”,例如信號系統、圖像系統等。
在光照緩存空間過濾

在光照緩存空間過濾

相比於在屏幕空間過濾,我們通過光照緩存(Radiance Cache 上篇介紹過)來執行過濾——因此我們從光照緩存的圖集中讀取,同時也寫入數據。
由於屏幕空間的光照緩存是降低採樣的,這使我們能實現性能較好的大範圍空間過濾器。
探針空間3x3的核的效果相當於相同屏幕尺寸的屏幕空間中48x48的核(“核”指需要輸入多少個數據源,以及對應的計算方式)。
當過濾入射光照時,我們可以忽略法線的區別,因為入射光照不受接收者的法線影響——因此我們僅需要深度作為權重。
從相鄰格收集光照數據

從相鄰格收集光照數據

當從相鄰格收集數據,我們可以快速查找到與收集方向匹配的radiance——因為在光照緩存中,射線已經被基於位置和方向做了索引(上一篇提到的八面體結構)。
但我們仍需要一個啟發式的結構,以排除那些可能導致漏光的相鄰格的radiance(通過定義誤差權重 Error weighting)。
我們可以通過重映射相鄰射線命中的位置,計算該點和我們目標方向的夾角,以排除那些夾角過大的radiance來源。
這樣能高效地過濾遠距離的光照,同時保持(正確的)局部陰影。
圖中展示了空間過濾器的效果,我們可以在不額外增加射線的情況下,實現在平面表面上很好的降噪效果(右側牆面)。
但我們為此損失了部分毛巾摺疊部分和貼牆部分的鄰接陰影(影子變淡了)。
保持鄰接陰影

保持鄰接陰影

這是由於基於角度誤差的容差值方式(不完全適用於遠距離光源)導致的。
遠距離光源沒有視差而且(在符合角度時)從來不被排除,導致了漏光。
我們找到的解決方案是:在重映射位置前,將相鄰的命中距離收緊至(clamp 有點對齊至的意思)當前格的命中距離。
*這裡簡單解釋一些,鄰接陰影的計算實際上也是在評估單位面積接收光能,接收的更多就更亮。雖然整個採樣由於精度和近似一定程度上有偏差,但這裡做的就是經驗性地排除一些明顯是“多出來”的能量計算。
這依然能帶來很平滑的遠距離光照效果。
同時我們也能夠“找回”毛巾摺疊處以及貼牆處的鄰接陰影。
最終過濾效果比較

最終過濾效果比較

*圖中展示了最終的空間過濾效果,左側是過濾前,右側是過濾後。

2 世界空間光照緩存

*相比於屏幕空間緩存直接緩存探針採樣光源的結果,世界空間緩存最大的區別是探針自身的光照採樣方式和精度不同。
課題:遠距離光照

課題:遠距離光照

如果僅僅通過屏幕空間探針來追蹤光照緩存,我們會面臨一個問題就是在遠距離光照上噪聲過大。
較小的光亮物的採樣噪聲隨著距離增加而變大——與此同時,長距離不連貫的採樣又非常慢,是性能上無法承受的。
Distant lighting is changing very slowly, both in time and in space, which is an opportunity to cache across frames, and an opportunity to reuse those distant rays for neighboring Screen Probes.
遠距離光源在時間和空間上通常都變化得很緩慢,因此是可能被跨幀緩存(而不失真)的——也有可能重用相鄰屏幕探針的遠距離射線。
*僅看這裡原文的措辭不太清晰,可以結合後面實際方案設計來看。
解決方案:對遠距離光照分開採樣

解決方案:對遠距離光照分開採樣

我們採用的解決方案是,對遠距離光照使用一套單獨的採樣方案。
我們使用的世界空間光照緩存(World Space Radiance Cache)參照了James McLaren的‘The Technology of the Tomorrow Children’方案。(*這裡是2015的一篇SIG講座分享,The Tomorrow Children: Lighting and Mining with Voxels
這套世界空間光照緩存方案能給我們帶來穩定的誤差——這使它能易於被隱藏(通過過濾)。
*粗略去看了下那篇分享,大致上遠距離光照探針採樣就是採用的以前介紹過的Voxel Cone Tracing(錐體體素採樣),那麼對一定錐體範圍內的多光源就可以基於插值計算方式來採樣。雖然計算細節不能百分百確定,但肯定是採用的一種覆蓋範圍更廣但精度相對更低的採樣方式,並用分幀的方式降低重新計算時的壓力。
管線集成

管線集成

我們在需要從世界空間探針插值的屏幕探針位置周圍放置探針。
然後我們通過世界空間探針來追蹤射線以計算入射光照,並對光照做插值以解決屏幕探針無法很好處理遠距離光照的問題。
連接射線

連接射線

We’re effectively connecting two rays, actually multiple World Probe rays because of interpolation, but we can just look at one World Probe ray and consider how they connect.
我們能高效地連接兩條射線——這實際上是多條世界空間探針射線(由於需要計算插值),不過我們可以僅僅考察一條世界空間探針射線是如何與屏幕探針射線進行連接的。
避免“自光照”問題

避免“自光照”問題

在進行世界空間探針追蹤時,首先我們需要加入一個偏移值(offset)以確保避開插值的覆蓋空間。
Any positions within the interpolation footprint are going to use this World Probe ray for distant lighting and must not pick up their own lighting.
任何位於插值覆蓋空間的位置都將使用世界空間探針射線對遠距離光照進行採樣(而不是採樣到它們自身的光照)。
*這裡主要是解釋如何避免採樣到光照物自身的光照能量,會產生“自光照”的原因也是由於這種世界空間的採樣方式是相對低精度的。
連接射線

連接射線

When we trace the Screen Probe ray, and connect it with the World Probe ray, we need to make sure the Screen Probe ray covers the interpolation footprint, and also the distance that the World Probe ray skipped.
當採樣屏幕探針射線,並於世界空間探針射線連接時,我們需要確保屏幕探針射線位於插值覆蓋區內,並基於距離閾值得出世界空間射線需要跳過的距離(*即避開插值覆蓋空間的半徑,從一定距離開始才採樣遠距離光照數據)。
課題:漏光

課題:漏光

不過在前述的內容都實現後,我們會遇到“漏光”問題。
這是由於兩條射線是從不同原點發出的,儘管平行但它們是錯開的。
圖中的這條世界空間射線是應該被剔除的,但因為這種視差(parallax)的情況而保留了,就出現了漏光情況。
*這裡parallax翻譯成視差其實也不完全合適,整體來說是一種“雖然平行但是觀測結果又不一致”的更廣泛的概念。
解決方案:簡單球面視差

解決方案:簡單球面視差

We can solve this by using the World Space Probe ray whose origin best matches up with our Screen Probe ray, instead of the World Probe ray whose direction matches.
我們可以通過調整世界空間探針射線原點的位置,以更好匹配屏幕探針射線並解決漏光問題——而不是使用同一球面同方向的射線。
這可能帶來一些方向上的誤差,但消除了射線間的錯開問題,避免了導致漏光的根源。
回顧前述,可以看到前面很多情況會有的漏光問題都被新的簡單球面視差方案解決了。
稀疏覆蓋

稀疏覆蓋

我們的世界空間光照緩存是稀疏的(sparse)。由於我們僅在需要作為屏幕探針插值補充的一些位置放置探針,因此我們將它們保存到以攝像機為中心的3D裁剪紋理(後文保持用clipmap這個詞)中。
這些clipmap被間接存儲到探針圖集中(storing an indirection into the probe atlas)。
Clipmap在圖集中的分佈受屏幕尺寸(可視範圍)約束,這一點的重要性在於:我們既不希望放置太多探針,也不希望放置太少。
圖集

圖集

我們的八面體探針圖集仍是存儲Radiance和TraceDistance,就像之前屏幕空間一樣——除了這次我們的每個探針採用了更高的分辨率(32X32),因為世界空間探針相對屏幕空間有著更少的數量。
放置和緩存

放置和緩存

為改進放置策略,首先我們標記出後續要從clipmap中間結構中插值的位置。
對每一個標記的位置——各自都是一個世界空間探針,我們既可以重用上一幀的追蹤結果,或者對於沒有任何可重用的情況,我們可以在圖集中為之分配一個新的探針,並追蹤新的射線。
我們也重新追蹤了一些(命中緩存的)重用的射線的子集,以便傳播式地更新光照的變化。
*這裡子集和傳播式(propagate)都指向了一種不一次性全部更新的分時策略。
課題:較高的變量開銷

課題:較高的變量開銷

目前為止描述的這套算法的問題是它會帶來一些故障。
我們有著過高的變量開銷,因而任何時候攝像機快速移動、或是繞過角落時,我們都需要顯示大量未緩存的為止並追蹤大量射線。(*一種有效的基於緩存的分時策略一般來說不能導致性能問題,否則就是不可用的)
不過我們可以為這種開銷制定一個上限值——通過為全分辨率下的追蹤數量指定一個預算上限的方式。
基於無法命中緩存而需要的額外追蹤數,如果超出預算,仍會運行但需要在較低的分辨率下執行;基於更新光照信息而產生的額外追蹤數,如果超出預算,則會被完全跳過。
這為我們提供了可控的最大開銷,使整個方案得以實時運行。
重要性採樣

重要性採樣

就像屏幕探針,我們也可以(對世界空間探針)做重要性採樣,只是這次我們沒有對入射光線很好的估計方式了。我們仍然能基於BRDF做採樣。
我們從屏幕探針來估算BRDF,之後將探針隨機分配(dice up)到待追蹤的tile(trace tile)中——我們需要追蹤更高分辨率,因而無法負擔排序並執行單獨的射線,因而追蹤以tile為最小單位執行。
我們基於和BRDF相對應的分辨率來生成trace tile,並在其上執行結構化的重要性採樣。
我們只在靠近攝像機的位置對trace tile進行超採樣,並且這種超採樣能帶來(每個世界空間探針)高效的4000個追蹤數。
這為我們帶來極為穩定的遠距離光照——尤其是當與每幀1像素1射線的屏幕空間降噪(Screen Space Denoiser)方案相比。
探針之間的空間過濾

探針之間的空間過濾

在追蹤後我們仍可對光照緩存的探針之間做空間過濾。(*基本思路參照前面空間過濾一節)
這次的問題是我們無法預設探針之間的可見性狀態——相鄰的探針可能被放置在牆壁的另一側。
避免漏光

避免漏光

Ideally we would like to re-trace the neighbor ray path through our probe’s depths, the depths that we stored off from ray tracing.
理想情況下我們希望通過存儲的(可能把射線追蹤隔開的)探針深度來判斷,並重追蹤相鄰的射線路徑。
This turns out to be too expensive, but we can solve most leaking by doing a single occlusion test to a position along the neighbor ray’s path.
這種方式開銷過大了,不過大部分時候我們可以通過對相鄰射線上位置的遮擋檢測來解決漏光問題。
這種遮擋檢測近乎是“免費”的,因為我們沒有追蹤任何新的射線——僅僅是重用了探針追蹤過程中得到的深度值(進行比對)。
圖中展示了世界空間緩存的結果。對於近處的2米採用的是屏幕空間光照緩存,更遠處則都是採用的世界空間光照緩存。
分時穩定性改進

分時穩定性改進

這一方案的最大好處是降低了基於屏幕空間分時採樣(射線精度高但樣本不足)的噪聲情況,提高了採樣穩定性。
我們也使用世界空間光照緩存來指導屏幕探針的重要性採樣(上篇中也提到了)。
它也被用於前向渲染中的頭髮、間接光照等模塊,並用於改進多次彈射算法的質量。

3 全分辨率下的集成步驟

*瞭解過渲染管線的對其中一些模塊應該不陌生,這裡的新事物主要是光照緩存和其它部分的集成方式。這裡主要是降低精度採樣後會面臨的一些精度損失問題與彌補方式。
(管線中)剩餘的流程是Bent Normal、插值、集成和分時過濾。
*這裡Bent Normal相對來說是圖形計算上的比較新的事物。UE中給出的說明如下:Distance Field AO produces a bent normal which is the direction of least occlusion.
*簡單來說,它的方向指向法線半球內遮蔽最小的方向模長代表受遮蔽的程度
最終集成

最終集成

回到積分公式,現在我們已經計算出了入射光照(基於屏幕空間光照緩存,低分辨率下的),需要在全分辨率下與其它幾何項做整合。
蒙特卡洛積分的噪聲

蒙特卡洛積分的噪聲

一種減少採樣噪聲的方式是對BRDF做重要性採樣來獲得射線方向,之後採樣我們的光照緩存、八面體圖集以獲得radiance。
由於這些採樣實際上是不連續的,並且我們也無法承受太高的採樣數,因此得到的初始結果是有噪聲的。
我們可以對光照緩存使用mipmap——這也被稱為可過濾的重要性採樣(Filtered Importance Sampling),不過這會導致“自照亮”問題。這會(錯誤的)把背面半球的光照計算到正面半球的光照數據中。
將光照探針轉換為三階的球諧函數

將光照探針轉換為三階的球諧函數

相應地對於更高質量我們使用球諧函數(Spherical Harmonics)作為漫反射的集成方案。
We convert the octahedral radiance into the Spherical Harmonic at the lower resolution of the Radiance Cache, and then the full resolution pixels load the SH coefficients coherently.
我們將低分辨率的光照緩存中的八面體radiance轉化成球諧函數的形式,並在全分辨率像素中連貫地讀取球諧係數(用於光照計算)。
這樣我們可以高效以及高質量的做SH漫反射集成了。
*之前也介紹過,球諧函數(SH)是“傳統的”光照探針的做法,階數越高則保留的高頻信號越多。高頻意味著光照在空間的激烈變化(例如圖像的高頻部分就是明暗分界處等),低階球諧函數無法很好描述這種變化,但性能上非常省——最終只用一組係數就可以定義出整個球面探針採樣的結果。
粗糙表面的高光

粗糙表面的高光

光線追蹤的反射對於高粗糙度的表面來說是開銷非常大的(各個點可能反射角度都不同,需要很大采樣數),並且它們需要追蹤額外的射線。
Thinking about the GGX lobe, at high roughness it becomes wide and converges on diffuse.
想象GGX波瓣(lobe),在高粗糙度下它會變得很寬並(近似)覆蓋漫反射的範圍。
*這裡主要是說用一定的方式來近似粗糙高光(反射),可以避免用昂貴的光追方式來做反射。GGX是一種微表面模型( microfacet models),是以函數模型的方式來以一定係數描述微表面接收光照後的出射角度範圍與能量的;波瓣則是以圖形來描述這種分佈的一種方式。更多信息可以去看看Games101,有講微表面模型的部分。
*之所以不用討論光滑表面高光(反射),因為這時基本只用按鏡面反射算一次就行了。
粗糙表面的高光——重用屏幕探針

粗糙表面的高光——重用屏幕探針

相比於追蹤額外的射線,我們可以重用屏幕空間光照緩存。
我們基於GGX波瓣函數來生成重要性採樣的方向,並可以直接從屏幕空間光照緩存中採樣(用於高光計算)。
這就自動利用上了之前所有的採樣和過濾中的工作成果(其實就是設計這個緩存結構時都考慮到了。原文用到了leverage一詞,直譯是槓桿作用、以槓桿的方式完成)。
降精度的採樣丟失鄰接陰影

降精度的採樣丟失鄰接陰影

*下一個要處理的問題就是丟失鄰接陰影信息。
全分辨率下的Bent Normal

全分辨率下的Bent Normal

我們可以利用全分辨率的Bent Normal來減輕鄰接陰影的問題,因為它自帶方向性的遮擋程度信息(前面介紹過)。
我們通過屏幕空間射線追蹤的方式計算Bent Normal,其中的追蹤距離等同於屏幕探針之間的距離。
*這裡的思想類似於計算屏幕空間的AO。
(鄰接陰影計算)與屏幕空間光照緩存的集成

(鄰接陰影計算)與屏幕空間光照緩存的集成

之後我們把全分辨率的的Bent Normal與屏幕空間光照緩存結合——使用“水平方向的間接光照”(‘Horizon Based Indirect Lighting’)估計方式。(*這裡很類似HBAO的思想)
This treats the Screen Probe GI as far-field Irradiance, and the Bent Normal represents the amount of near-field Irradiance, with the multi-bounce approximation from the paper giving the near-field Irradiance.
這個方案把屏幕探針的全局光照視為遠域(far-field)的Irradiance,然後Bent Normal代表近域(near-field)的Irradiance——計算是考慮上了圖中的論文給出的近域內多次彈射情況。
*結果比較,右側是補充了鄰接陰影的結果。
對於全分辨率的細節小物體,陰影也能正確表現了。
分幀過濾器

分幀過濾器

對於分幀過濾器,由於我們不是每像素都設置探針(每幀都採樣),因此需要抖動探針位置。(*上一篇中介紹過)
這種抖動需要一種穩定的分幀過濾方式來隱蔽,因此我們採用深度排除(depth rejection)而不是相鄰對齊(neighborhood clamp)的方式。
深度排除儘管能提供穩定的結果,但仍然在光源變化時性能很慢——這會表現為運動物體及周圍物體的間接光照的更新可能不夠及時。
在追蹤高速移動物體時切換至快速更新模式

在追蹤高速移動物體時切換至快速更新模式

*最終的方案是在達到速度閾值時,降低分幀過濾權重,提高空間過濾權重。
*最後有一節性能總覽這裡略去了,有興趣的可以去看看原文。

結語

上一篇我也提到過,目前渲染高清化的這些技術中,突破性能瓶頸的主要思路就是基於時間的超採樣和基於AI推測的超採樣,但兩者都對於畫面突變的情況適應力較差;而導致需要超採樣的其中一個原因,就是單幀的光線追蹤開銷還是太高了。我很好奇下一個世代的渲染技術要如何突破這方面的瓶頸,但至少目前我還想象不到(但至少不能全指望AI效率的提升)。
另外,不透明渲染還和這套整體“高清化”的架構無關,是另一套優化方式和另外的敘事。實時上不透明渲染要達到更高質量只會是越來越耗的——例如水體、粒子特效、透射材質等(之前一個主題介紹的雲就是如此)。
最後談一點題外話,關於《黑神話:悟空》的引擎運用上的小問題:最明顯的是部分場景的運行效率不正常的低,且往往不是處於極值狀態的一些場景;遊戲中還有一類常見的物理系統碰撞盒遇到極限值導致卡死的問題,例如第三章某個BOSS後出去的斜坡,直接走不跳的話大概率就會遇到。
而根據身邊統計學,這遊戲在PS5上的運行視覺效果相對也差一些。可能由於沒法太精確的定製畫質,因此整體的環境視覺質量是不如PC端的;很多人會拿這個遊戲的整體PS5觀感和《戰神:諸神黃昏》比較,那麼通用商業引擎的整體運行效率肯定也是比不過第一方定製化開發的(開頭提到的《最終幻想16》等更是這個情況)——況且場景美術資源的精度也不是一個量級的。
依稀記得在《黑神話:悟空》的早期一些訪談中,主創曾談到他們想實現肌肉破壞流血、以及毛髮染血後粘合變形的效果——當時主創似乎認為這種特性可以歸納為一種“怪物肌肉材質”,是可以模塊化開發出來的,但一定程度瞭解3D遊戲開發的應該能知道,這是需要高度定製的而且很複雜的一個組合需求;它大概率不會是人們對物理世界的瞭解那樣是一套統一法則的實現。我很高興最終在多年的磨合中游戲科學團隊能實現引擎運用上的“取長補短”,實現了項目管理和開發效率上的成功,而並沒有像我瞭解過的其他一些單機開發項目一樣陷入“細節地獄”。
整體來說,這款遊戲無疑是取得了空前的成功,但輿論也不能上頭了覺得這是“技術上”的空前成功——目前來說只能說這是遊戲藝術上的空前成功(遊科自己應該是很清楚這一點的,他們總的來說是比較低調和謙虛的)。說到底引擎技術基本還全是別人幾十年的積累得到的,我們距離當作工具來用明白都還有一定距離。
即使世界處於反全球化的浪潮,有些文化領域(例如遊戲)其實已經深度交融了。在世界的其他玩家玩《黑神話:悟空》的體驗中,我能感受到有些人不是為了熱度,而是真的被這個遊戲的真誠打動了。我認為世界在一定程度上還應該是求同存異的,我們能有《黑神話:悟空》這樣偉大的遊戲,也應該感激作為引擎的虛幻5。

最後是資料鏈接:
Radiance Caching for Real-Time Global Illumination 的PPT文件
The Tomorrow Children: Lighting and Mining with Voxels 的MP4視頻地址

© 2022 3樓貓 下載APP 站點地圖 廣告合作:asmrly666@gmail.com