前言
這周繼續更新讀技術文章。在GDC的文檔庫中我有一個核心感興趣的點就是很多主機大廠如何邁進PS5(以及XBOX-X)這個世代。之前也讀到過《漫威蜘蛛俠》的續作在巨量建築LOD方面的進化,這次帶大家一起來看看新《戰神》這個系列在畫面邁入當時的“次世代”的一些調整。
原文檔的分享人是2020年才加入聖莫妮卡工作室(縮寫SMS)作為渲染主程的Stephen McAuley(有趣的是他之前一直供職於育碧蒙特利爾,主要參與Far Cry系列)。
分享人介紹
這篇分享整體是一個產品向的講座,各部分間的信息密度不太一樣。各節分別是:項目背景、遊戲中的敘事元素(及其美術方案)、美術工作流程的重整、PS5上的畫面進化、團隊自我提升、性能優化。除開本文將翻譯和分析的第三部分,其它部分更多是項目管理及產品規劃的視角——例如提到了使用JIRA作為核心的敏捷反饋項目管理工具,以及他個人和團隊與之前的一些Leader間的一些配合細節。
而至於要重整美術工作流程,也並不是因為他是空降的而需要“新官上任三把火”,而是前作的光照模型整體還是基於“各場景手調光強度”的古老方案來進行的,或許作為線性流程的有明確室內外切換的場景編輯來說勉強夠用,但確實要做更細緻(一天更多時段)更寫實(更多動態光源)的光照已經不足了。這之中他們對於光照、反射和折射幾方面都有美術製作規範和渲染管線上的調整,但我個人還是更聚焦在PS5的畫面進化這個技術向的方向。
本文整體還是以翻譯為主,最後一節會補充對於SSDO的簡單介紹。打星號的部分則是我個人對原文的補充(文中很多基礎概念就不一一解釋了,之前我有很多篇文章介紹過,比如BVH)。
1 PS5上的專屬特性概覽
不同機型畫面模式一覽
*這裡面HFR是高刷新率,VRR是可變刷新率。
PS5平臺專屬畫面特性
——性能模式:
- 曲面細分
- 屏幕空間基於方向的遮蔽
- 簡單的鄰接硬陰影
- 強化的屏幕空間反射
- PS5特定的模型和光照
——畫質模式:
- 基於機器學習的紋理超採樣(文末附鏈接)
- CubeMap射線追蹤
- 進階的鄰接硬陰影
- 強化的天空光照、景深、曲面細分、屏幕空間反射
- 更高分辨率的陰影
- 更高質量的LOD
*如果渲染向的文章看過一些,或許會感覺工業化的引擎技術就像一個從“模塊方案超市”從選配的過程——某種意義上也確實如此,例如反射就可能有“鏡像攝像機拍攝”“靜態CubeMap採樣”“屏幕空間反射(縮寫SSR)”“光線追蹤”等很多方案;陰影之前介紹過的一些方案還主要是基於靜態烘焙和少量實時光源的Shadow Map的,而要實現動態的鄰接陰影甚至都要弄出膠囊體AO這樣的近似方案了,而相對來說“鄰接硬陰影”則是質量和精度相對更高的一種陰影方案。
2 基於CubeMap的射線追蹤
- 《戰神:諸神黃昏》使用視差校正的CubeMap技術(文末附資料鏈接)
- 需要找出反射線與一個物體包圍盒的交點
- 用於調整cubemap採樣的位置(視差校正)
圖中演示了需要視差校正的一種情況
- 通過反射線與場景物體幾何體求交以進一步提升——使用硬件光線追蹤
- 與屏幕空間的射線追蹤反射技術結合——在可獲得的點採用屏幕空間(反射)數據
- 屏幕空間射線追蹤——命中位置、顏色
- 如果命中一個點:射線追蹤其BVH(一種空間加速結構、之前有文章介紹過);如果命中位置能對應到一個屏幕空間的位置,從屏幕空間緩衝區中採樣數據(屏幕內);否則從cubemap中採樣數據(屏幕外)
- 混合屏幕空間反射(SSR)和BVH檢測的結果
*之前設想過這種做法來補充屏幕空間反射追蹤不到的問題,不過實際讀到3A中這麼做這還是第一篇。
CubeMap射線追蹤關閉時
CubeMap射線追蹤開啟時
*上面2圖是關閉與開啟這項技術時的對比——可以看到關閉時的反射其實完全是“不真實”的,僅能提供一種氛圍。
分層DEBUG展示
- 藍綠色——屏幕空間命中,屏幕空間採樣
- 紫色——BVH命中,屏幕空間採樣(位置校正屏幕內)
- 黃色——BVH命中,cubemap採樣(位置校正屏幕外)
- 我們並不射線追蹤實際的遊戲幾何體
- 作為替代,我們創建了代理幾何體:從GI烘焙的過程中取GI點元數據;通過Houdini轉換程網格
圖中是他們開發的點元預覽工具——位置、表面顏色、法線
*中間有幾頁是這個工具基於點元數據生成網格的過程,有興趣可以去看看原文檔,我就不放步驟圖在這裡了。
最終生成的網格
下面摘翻一下整個網格生成的步驟:
- 使用球和中心算法來從點雲生成網格(open3d python庫有一個便利的封裝)。如果數據是非常鬆散的情況容易得到“假陽性”(錯判)的結果,這時生成的幾何體會有很多問題。
- 通過生成VDB來清理球和中心算法的結果,並光柵化至幾何體。(“VDB”, so named because it is a Volumetric, Dynamic grid that shares several characteristics with B+trees。簡單來理解就是一種人為定義體積的分層加速結構——文末會附鏈接)
- VDB光柵化結果是雙面的,通過片元位置的線路追蹤來確認正面並移除背面(算法線和目標位置的夾角)。
- 通過Houdini中的減面工具,在保持曲率的基礎上大幅消減網格數——原始目標是每一團集群中33K個三角形。這一限制在光線追蹤的性能提升後會有所提高。
這一技術的性能總覽
- 由於第一步的幾個技術都需要從屏幕空間緩衝區中讀數據,因此只讀一遍以用於採樣,避免重複採樣。
- Ray binning可以理解成一種預篩選過程,處理64X32的格子性能開銷也很小。其它步驟都是前面介紹過的步驟。
- 圖中展示的是PS5上4K分辨率的性能情況。
3 鄰接陰影的新方案
- 陰影分辨率和陰影偏移量帶來了2018版《戰神》中的很多陰影方面的主要煩惱
- 大的陰影偏移值會導致角色面部(等精細部位)缺乏陰影
- 這裡(後面)展示了一個“漏光”的例子
- 鄰接硬陰影是這類問題的解決方案
*陰影偏移量:是shadow map方案中做採樣精度時的一個容差值,因為shadow map的主要方案例如CSM其實都還是基於
比較深度緩衝,在屏幕像素、目標點位置、shadow map的深度值之間有一定精度誤差。常規來說就是通過引入一個bias來做容差,但也會帶來例如“陰影範圍錯誤地變小”之類的問題。
在2018版中,由於陰影精度不夠,鼻子上是處於“漏光”狀態的
- 通過射線追蹤解決鄰接硬陰影——朝向區域光源做射線追蹤
- 思路:在shadow map空間中做射線追蹤。類似屏幕空間的其它射線追蹤技術——沒有采用硬件光追加速。
*後面一段會詳細逐步介紹這個算法(in practice):
Let’s consider how we shade a pixel. We want to trace rays to this area light source, through this depth buffer which is represented as a set of green lines – each line segment is a depth buffer texel with a certain depth. It’s important to remember that this depth buffer really doesn’t represent a contiguous height field, we have to treat it as an isolated collection of fragments.
考慮我們是如何著色一個像素的——我們想要射線追蹤區域光源,透過圖中標綠線的深度緩衝(每一段代表一個特定深度的緩衝區中的圖素)。重要的是需要記住深度緩衝並不代表一片連續的深度區域,我們必須把其中每個片元視為是獨立的。
We’re ray tracing to see if we intersect the depth buffer. In this case, let’s imagine we trace the simplest ray, one aligned with the direction of the light. In this case you can see that the first three samples are occluded, and the fourth sample is not occluded. As we’ve changed from shadowed to non-shadowed, we detect that we’ve hit an object, stop ray marching, and take the shadowed sample.
我們進行射線追蹤來檢查是否與深度緩衝相交。這次我們假設僅發出一條簡單的射線,它與光源的方向一致。你能看到最初的3個樣本是被遮擋的,而第四個樣本是未被遮擋的。由於樣本已經從陰影中變為無陰影的,我們探測出已經命中一個物體,結束光線步進的過程,並得出陰影樣本。
This all seems rather naïve though – we might as well have just taken the original shadowed sample. However, other rays that aren’t parallel to the direction of the light are more complex.
雖然所有這些看起來都很不成熟——無妨,至少我們可以先取得最初的陰影樣本。然而,其它和光源方向不平行的射線情況會更復雜。
*這裡朝光源的深度緩衝做追蹤其實就是在光源對應的shadow map上做追蹤。
Let’s trace another ray. After a few steps we notice we are no longer shadowed. Does this mean that we’ve intersected an object? Well, clearly it doesn’t in this case.
讓我們追蹤另一條射線。經過幾個步驟後我們發現不處於陰影中(標白色的部分),但這是否意味著前面的步驟我們與一個物體相交了(從紅變白)?顯然圖中的情況就沒有相交。
We also sample on the disk at the original pixel’s depth, and discover that sample isn’t shadowed either. The fact that the two samples are in agreement (they’re both non-shadowed) shows that no boundary has been crossed.
我們也在原始像素的深度緩衝中進行採樣(即攝像機位置的深度緩衝,而陰影是光源位置的深度緩衝),並發現樣本在其中也不在陰影中(被遮擋)。兩個樣本一致(都不在陰影中)的事實顯示沒有邊界被跨越。
So we continue ray tracing until we hit the light…
所以我們繼續射線追蹤直到命中光源。
The samples on the disk vs the samples on the ray are always in agreement, showing we never crossed a boundary. This means we take the last result and say that the ray isn’t shadowed.
像素盤(屏幕空間像素的緩衝區數據)和射線中對應的採樣是始終一致的,顯示我們沒有跨越邊界。這意味著最終的結果是這條射線沒有被遮擋。
Let’s trace yet another ray, a more complex one this time. We trace until we find a disagreement between the disk and the ray samples, which means a boundary has been crossed… but has it?
讓我們追蹤另一條射線——一個更復雜的情況。我們追蹤直到發現兩個緩衝區中採樣點的遮擋狀態是不一致的,這是否意味著跨越了邊界呢?
This is an innate problem of screen space ray tracing techniques. We don’t know if those individual depth texels are connected or not (or if they’re not, how “thick” each individual texel is).
這是屏幕空間射線追蹤技術的一類天生的(會導致錯判的)問題。我們不知道那些有著獨立深度值的像素塊是否是連續的(以及如果不連續,每個獨立的像素塊有多“厚”)。
However, for our purposes, we say the depth buffer is contiguous. This prevents light leaking and worst case, will just give us the same result as regular PCF filtering.
然而,基於主要的目的我們把深度緩衝視為是連續的。這能避免漏光等最壞的情況,最差的情況也會和PCF方式下的陰影一致。(PCF簡單說是一種通過鄰接採樣shadow map來進行提高精度和做軟陰影的方案)
So in the case of this ray, we determine we’ve crossed a boundary, and take the result of the sample before that. This ray is shadowed.
所以對於圖中情況的射線,我們認定它跨越了邊界,並選取最終位置的前一個採樣點作為結果。這個射線被視為是被遮擋的。
We trace each ray a sample at a time, killing a ray whenever it intersects the depth buffer
(實際運行時)我們同時發出這些射線,並中止那些與深度緩衝相交的射線(得出採樣點的交點位置)。
*整個過程是基於光線步進ray marching方案的,因此他們也沒有采用硬件加速的光線追蹤。
我們通過以下方式進一步提高陰影質量:
- 抖動陰影採樣位置(多幀基於時間的混合)
- 在採樣shadow map時使用過濾器:並不是每個像素都是二元的輸入、輸出過程;意味著可以使用更少的射線以提升性能(可降低原始採樣精度)。
PCF陰影
鄰接硬陰影
*圖中對比了2種方案的畫質差距。可以看到手指和弓弦部分有明顯差距。
- 射線追蹤相對比較慢——可能話儘量提前開始運算
- 創建一個向下採樣的限制最大最小值的shadow map:基於陰影過濾的半徑進行擴張;類似Abadie2018的景深技術方案。
- 使用動態擴張的最大最小深度:如果全部在或不在陰影中則可以提前進行運算;把光追運算更緊密的限制在需要的區域內。
——我們的鄰接硬陰影並不是“物理原理的”(主要指參數化上)
- 這一特性是後期開發的
- 光照藝術家已經手動調整過陰影過濾的半徑
- 最終能儘量保持和之前手調的(PCF方式)顯示一致
——射線追蹤的“區域光源”被假設為:
- 陰影過濾半徑的值
- 著色像素對應的2米範圍
*這裡之所謂是打引號的“區域光源”,主要是因為他們的目的是提高陰影質量,因此雖然採樣了一定面積的陰影遮蔽情況,卻不一定是朝真實的區域光源做的採樣(有可能是在主光源或點光源的shadow map上也做類似處理)。
4 漏光的解決與SSDO簡單介紹
——總的來說,我們發現移除漏光問題是最能帶來視覺提升的方面
- 這通常出現在本身不該被照亮的物體部分被錯誤照亮的情況,表現為光好像“漏”了(實際是採樣精度或信息不足)
- 之前介紹的兩種方案都對減少漏光情況有幫助
——然而,把SSAO升級成SSDO帶來了最大的影響;添加了SSGI(屏幕空間的全局光照)也有幫助
*SSAO和HBAO之前有文章介紹過,這裡直接從SSDO開始介紹。至於SSGI,也並不如其概念上的大詞那樣是一項太複雜的技術,簡單說它能在極有限的情況下通過屏幕空間深度信息做光線步進追蹤計算出間接光照。篇幅原因就不介紹了,有興趣可以網上搜搜。
左側是SSAO,右側是SSDO
*圖中引用了Games202的課件圖。可以看到SSDO除了能得到和SSAO(或HBAO)相同的AO範圍外,它還能在屏幕空間利用深度緩衝來追蹤間接光照信息,所以能得到如右邊展示的有間接光照(光彈射一次)顏色的AO效果。
*SSDO的核心步驟可以概括如下:
- 在目標像素法線所在的半球(HBAO方式,需要GBuffer法線信息)選取一些採樣點
- 檢測採樣點是否在表面之上
- 對在表面之上的採樣點進一步做一次彈射的間接光照計算(間接光可以來自周圍物體或cubemap),之後對光照貢獻求和
SSDO間接光照計算的圖示
*具體的射線追蹤方式很類似前面“鄰接硬陰影”一節,都是一種屏幕空間的ray marching技術。
*這一方案的侷限如下:
- 只能近似計算小範圍的全局光照信息
- 對於採樣範圍內的微小結構可能會有誤判
- 不能追蹤超出屏幕空間範圍的信息
最後是一些圖片對照:
SSDO和SSGI都關閉時
SSDO開啟時
You can see better shadowing behind Freya’s sword hilt, plus where Kratos’ arm shadows Atreus.
你可以看到芙蕾雅劍柄有更好的陰影,以及奎託斯的的胳膊正確遮蔽了阿特柔斯。
*但說實話由於本身精度就很高的原因,這個例子不仔細看確實不明顯。
SSDO和SSGI都開啟時
結語
其實文中的的各處“射線追蹤”方案都是廣義的“光線追蹤”或“路徑追蹤”的組件,有了硬件上對光追的加速後,確實一定程度上能把更多的組件納入渲染特性中。但實際上即使在PS5,在《戰神:諸神黃昏》中,談到“光追”其實支持的特性還只是一小部分——更多的射線以及更多的採樣數其實都不太可能。整篇文章中提到的射線追蹤技術,除了追蹤BVH之外其它都還是屏幕空間的技術;對比Nvidia話語體系下的path tracing整體方案(更不用說AI超採樣方面的技術),其實相對是挺寒酸的。
如果不看這篇文章,我可能對於2018年的年度遊戲《戰神》的渲染技術選型相對的古舊沒有太多概念——可能它的更多精細度都表現在了模型精度上,因此更需要配合定製化的關卡場景;同時他們還有一個負擔就是舊版的PS4。確實在當時看來《神秘海域4》或《戰神》都是“真”的不能再“真”的遊戲了,但其實這種也都是巧妙藏拙的結果——最大的優化就是設計出不需要優化的應用場景,因此需要各種限制自由度。
實際上《戰神:諸神黃昏》儘管在前作基礎上畫質進化很多,但是由於隔了多年記憶不準確了,實際玩上確實沒有畫質明顯變好的感覺。而讀了這篇分享,我發現PS5上第一方的光追應用也還是捆手捆腳的,也不得不感嘆——追求極致畫面這件事或許已經越來越不能帶動3A遊戲領域向上增長了。
例如大部分遊戲的室內外光源切換,其實是經不起推敲的,但這麼多年玩家也這麼接受下來了;而進一步把這事弄得更“真”要付出的代價暫時是無從談起的,只能等將來算力進一步躍升的一天,或許就能自然容納這件事了。
不過我也不同意一種言論,就是“把影子弄那麼真有意義麼,我陰影選項都開低的,幀數最重要”;對個人來說這麼選無可厚非,但開發商追求畫質極限是遊戲產業生態很重要的組成部分。事實上無論哪個3D引擎,畫面質量的高低不就是通過表面材質著色質量、反射質量、陰影質量、體積光照質量等等一系列細節撐起來的麼,而3A遊戲天生就是為發揮當時主機的最高技能而生的,這一點無可厚非——畫面最好的遊戲顯得不好玩,那不是畫面的錯,是其它方面出了問題。
最後是一些資料鏈接:
Rendering-God-of-War-Ragnarok 的GDC地址
介紹VDB的一篇知乎文章
介紹Parallax Corrected Cubemap的一篇SIG