《明日之后》客戶端技術復盤:如何構建末日世界的光與影?

《明日之后》作為一個3D視角的生存類手游,具備大世界、晝夜、天氣、大量可采集植被、可建造房屋等多種高級特性,玩家在游戲里能明顯感受到真實物理世界的光照和陰影變化。但這些效果的實現是比較復雜的,傳統制作方案并不適用與這款游戲。在光照條件不固定,場景布局不固定的問題下,如何讓游戲場景更接近真實物理世界?

1月20日,在網易游戲學院舉辦的2019網易游戲開發者峰會上,《明日之后》主程復盤了這款產品在客戶端渲染和性能優化方面的技術實現。

以下為分享實錄:

大家好,今天要給大家帶來的是明日之后客戶端技術分享。

明日之后是一個開放大世界的游戲,在渲染方面是比較復雜的。首先它是一個大世界,擁有3D視角,有晝夜天氣以及大量植被,環境是可以被改變的。這些在程序員看來,就會面臨光照條件不固定,場景布局不固定,面數高,DP多,Overdraw之類的問題。

光與影

帶著這些問題,看一下我們是如何制作渲染方案的。

先來回顧一下傳統制作方案:運用場景光照離線烘培,角色光照實時計算的方法,使場景更加真實。但這個方法并不太適合我們的游戲,因為單純基于烘焙的方法雖然高效,但難以實現晝夜光照的交換。

所以我們當時就有幾點思考:
第一,日升月落-主光源應當分離;
第二,實時GI對于手游依然遙不可及-依然需要烘焙;
第三,重新梳理光照中的各個分量,找出變與不變量。

單純的烘焙效果是無法達到《明日之后》光照變化所需的效果的。所以我們將主光源分離出來,實時計算;將烘焙貼圖的RGB存儲間接光與烘焙點光;烘焙貼圖的Alpha存儲AO。

烘焙貼圖的RGB在白天其實是看不見的,因為我們在白天時會將烘焙的RGB分量調到0。而在黑夜,烘焙貼圖的RGB分量權值會調高。這樣就可以做到白天沒有燈光,而夜晚有燈光以及間接光的效果。

關于烘培間接光,是不存儲太陽光產生的直接光照、陰影的,它只存儲間接光與烘焙點光,根據時間調節直接光與間接光的權重,主要起到豐富夜間光照的作用,由于不需要存儲陰影,尺寸可以小很多。

但AO就不同了,AO在白天和黑夜基本是一樣的。因為AO可以讓物體更“實”,有AO甚至可以接受沒有陰影,烘焙時天光只產生AO,不產生光照,另外AO存儲在烘焙貼圖的Alpha通道。

以上基本就解決了場景烘焙基調的問題了,也就是場景靜態光照的問題。

但美術發現,在這樣一套光照下行走,人物不受光照影響,顯得光照不夠豐富,畫面相對平面化。

所以我們就運用了模擬GI技術,它是一種叫Ambient Cube的技術。它會在場景里擺很多采樣點,每個采樣點記錄6個方向的光照信息,利用烘焙器離線生成(網易自研Cloud GI)簡單高效,大幅增強了夜間光照真實感與豐富度,它也可替換為SH來提升質量。

接下來就是動態點光源的問題,類似于游戲里的火把。但Ambient Cube只能實現靜態點光,Deferred框架不適合移動平臺。這時我們就回到點光源特性的問題,點光源有距離衰減的特性。

我們運用了Tiled Point Light這一技術,將畫面切分為多個tile,利用上一幀的深度計算tile的world position,然后計算出tile貢獻最大的2個點光,使得每個頂點/像素僅需計算2個點光。Tiled Point Light使得我們開銷降低(iphone 5s也可使用),大大豐富了場景的光照效果。

接下來就有了這樣的光照匯總

主光源實時計算
烘焙貼圖的RGB存儲間接光與烘焙點光
烘焙貼圖的Alpha存儲AO
Ambient Cube實現夜間和室內GI
Tile-based Point Light實現動態點光源
全場景陰影實時計算

提到陰影,陰影對3D視角的游戲來說幾乎是不可避開的坑,Shadow Mapping在未來幾年內應該還是主流方法,3D視角的陰影比2.5D視角難做很多,場景參與投影更是個大坑。

Shadow Mapping的優點是它具有非常簡潔的原理,相對開銷低(但也有很多坑)它具有海量的變種算法,在未來幾年內應該還是主流方法,全場景陰影可能會越來越主流。如果用一句話來概括就是:如果在燈光視角看不到物體A,那么物體A就在陰影中。

但這也經常會遇到問題,其中一個比較嚴重的問題就是大場景的問題:3D視角游戲常常平視場景,陰影透視走樣嚴重;使用單張超大Shadow MAP,遠處精度嚴重浪費。

所以我們引入了Cascade Shadow Maps,使用多級Shadow Map,拆分遠近物體,盡量充分利用精度,這也是LOD思想的一種應用。

PSSM是一種比較流行的做法,但《明日之后》使用的則是以相機為中心建立多級嵌套的Shadow Map。它使外層Shadow Map可以緩存,隔幀更新,減少DP,相機在小范圍內移動時,可以完全不更新外層,DP開銷接近0,實踐上每幀均攤DP只有十來個。但它也有缺點,遠處的陰影質量不如PSSM,難以實現超遠距離的陰影。

接下來是植被問題:場景中有大量植被,大量Overdraw,陰影也存在被重復計算的問題。所以我們用Screen Space Shadow,使繪制場景時不計算陰影,最后一個Pass利用深度重構像素的世界坐標,并計算陰影,減少陰影的Overdraw。這樣能減少GPU Time,從16.8ms減少到14.3ms。陰影也不是承在方向光上,而是承在最終顏色上,會讓被光面變得更黑。

關于渲染這一塊還想分享的是雨雪渲染。雨雪實際上是使用椎體包圍相機,椎體行播放多層紋理動畫來實現的。我們將近處和遠處的雨都分開三個通道存在貼圖里,做一個層次變化,在椎體上播放。

關于濕身效果非常有趣,它是通過調節PBR的金屬度/粗糙度來模擬濕身效果,在PBR框架下無額外開銷,所以不用白不用。

關于方案就講到這,接下來講講優化方面是怎么做的。

優化

首先是渲染效率的問題,3D大場景的DP數非常恐怖,移動設備CPU/GPU都很弱,對DP數量非常敏感。我們的思路是:剔除不必要的DP,合并零碎的DP,優化單DP效率。

首先我們做了遮擋剔除,在視錐剔除的基礎上,進一步剔除被遮擋的物體。最適合手游的遮擋剔除方案是PVS(Potentially visible set),它將相機可達空間切分為多個Cell,光線追蹤計算每個Cell可看見哪些模型,使用bitset等數據結構保存可見性信息。運行時,我們就根據相機位置找到對應的Cell可見性信息,用于剔除不可見模型。

接下來進行合批。相同材質的物體會被合批,以一個DP繪制出來,合批發生在加載線程,生成額外頂點數據。有相同材質的要求,就意味著物體之間需要共享貼圖,才能用一個指令繪制出來。

這樣會遇到很多問題:
美術需要把多個模型的貼圖合并到一張(隨緣合);
每個模型的UV都需要重新調節一下(很麻煩);
后續增加模型.修改模型.刪除模型,都需要調整UV;
維護成本高;
只要修改一個模型貼圖,就會產生大貼圖的Patch體積;
跨場景共享模型會造成內存浪費。

針對這些問題,我們制定了改進方案:離線僅預計算合批策略;貼圖合并改為加載時進行。針對場景結構設計一種貪心算法,自動搜索哪些模型應當被合批,計算合批信息,僅需保存貼圖的合并信息,合并操作留到運行時再做。

運行時創建被合批貼圖時,在內存里將多張壓縮貼圖合并一張Atlas,ASTC/ETC2/PVR都是Block-based的壓縮算法,按Block拷貝即可合并,合并的時間開銷很小,和I/O相比可以忽略不計。引擎加載模型時,查詢貼圖是否被合批,如果被合批,則根據貼圖的合批信息調整自己的UV即可。

最終的合批效果比人肉做可能還會好一些,美術也不需要花費大量時間去做合批,修改貼圖不再會導致巨大的Patch。

除此之外,游戲中需要大量植被,植被雖做了LOD,但面數依舊很高。我們的思路是將它們渲染到Render Target,再以Billboard面片方式批量繪制。

為什么不離線做呢?
第一個原因是因為不想增加包體;
其次一些動態的信息可能需要渲染到RT里;
最后的成果是新手場景面數從20萬減少到17萬,新手場景GPU Time從14.3ms減少到12.3ms,并且因為生效距離遠,所以不太容易看出瑕疵。

預算機制

最后講一塊,預算機制。

啟發點首先是因為計算能力是有限的,我們需要對任務建立重要度分級。所以我們需要建立資源消耗和計算能力的閉環,從以前的”來幾個,處理幾個”,轉變為”能處理幾個,處理幾個”的負反饋機制。然后當消耗達到預算上限時,延遲/放棄低優先級任務,或者換出低優先級資源。

這里我舉兩個例子。
第一:CPU預算,CPU資源是有限的,為了達到30fps,每幀預算只有33ms,那么我們可以使用預算管理異步回調。

第二:內存預算,NeoX中的紋理預算系統。我們可以通過嚴格控制紋理的內存的使用量,根據紋理對于場景的貢獻度打分.排序,將分值低的紋理切換到低分辨率的Mipmap來實現對紋理內存的嚴格管控。

結語:
光影表現對新游戲而言非常重要,實時化是個大趨勢,手游的技術路線并不完全與端游的老路一致,沒有銀彈,希望大家勇于探索新技術。

本文來自網易游戲學院,本文觀點不代表GameLook立場,轉載請聯系原作者。

關注微信
网球优等生