2021年11月22日-24日,由騰訊遊戲學堂舉辦的第五屆騰訊遊戲開發者大會(Tencent Game Developers Conference,簡稱TGDC)在線上舉行。本屆大會以“Five by Five”為主題,邀請了海內外40多位行業嘉賓,從主論壇、產品、技術、藝術、獨立遊戲、市場及遊戲社會價值7大專場共同探討遊戲產業趨勢和多元價值,以開發者視角與需求為出發點,助力遊戲行業良性發展,探索遊戲的更多可能性。
一、概述
我分享的是《天涯明月刀》端遊自研引擎的體積雲和體積霧的渲染系統開發和最佳化。首先給大家介紹一下背景以及目標:天刀在業內一直以動態的雲海體積雲著稱,算是最早一批應用體渲染的大型3D網遊,主要思想就是借鑑體繪製,利用體渲染進行光線步進,整體效果非常真實,並且效能也十分高效,近年來各類3A大作在這方面的改進層出不窮,提出了各種各樣的新技術。
自然界中的雲和霧千變萬化,令人心曠神怡賞心悅目,這些都是星球研究所拍攝的國內各個地方的自然景觀,新技術以及美術效果的各種需求使我們有了更高的追求和目標。
下圖是整體系統,我們需要渲染相機中所看到的各種參與性介質,比如霧、雲、大氣等,在距離相機0-200m的範圍我們使用Froxel,就是圖中黃色的體素網格進行渲染,超出200m的範圍使用RayMarch進行渲染。
這段影片演示了系統的部分效果,我們支援全動態的時間天氣變化,美術可以任意設定雲霧的形態,玩家可以隨意穿越進入體積雲和體積霧。我們是如何對這些體積介質建模的呢?
二、建模
首先是體積雲,為了方便講解,我們先從一個簡單的例子入手,圖示的一塊雲是從10000米的高度往下看,這塊雲體大概覆蓋了5km*5km的範圍,它是根據左上角的2D俯視角的Cloudmap生成的,在高度上沒有變化,我們需要引入一張高度LUT貼圖來控制他在高度上的變化。
左上藍紅色的豎條是當前的高度LUT,雲層在高度上便有了變化。
最後我們加入displacementMap以及PerlinWorley3D貼圖來刻蝕雲體的邊緣以及細節。兩張貼圖如上圖左下角所示,最終雲體模型會變得更有層次,細節豐富。
這個影片演示了Cloudmap變化對雲體形狀的影響,Cloudmap如下圖紅色箭頭所示。
這段影片演示了高度LUT貼圖對雲體形態的影響,LUT貼圖如紅色箭頭指示,實時遊戲中LUT的變化會插值進行過渡,我們還可以使用各種程式化引數來改變雲層的形態.
接著是體積霧的建模,FogMap跟CloudMap的作用一樣。它是張2D貼圖,覆蓋可玩區域,R通道在Houdini中以地形高度圖為基礎微調,G通道在Houdini中根據噪聲生成,對特定區域進行手工編輯,B通道是濃度,實時程式化生成。
這段影片演示了FogMap3個通道變化對地圖中體積霧的影響,R通道控制起霧的高度,G通道控制起霧的衰減,最後B通道控制起霧的濃度。
天空大氣的原理: 我們在外太空會看到地球上有一層大氣光環,在晴天我們看到的天空是藍色的,在黃昏時看到的天空是橙紅色的,這都是因為地球上的大氣所造成的現象。
類地行星上存在著稀薄的氣體以及其他細小顆粒,它們構成了一種特殊介質,會與光線有互動,形成各種現象。如果空氣顆粒比較細小,短波長的藍光會受它的影響進而散射,這就是Rayleigh散射,散射出去的藍光使天空變藍。如果是比較大的顆粒,光線會向四面八方散射,而它在傳播主方向上散射的光比較多,這就是Mie散射,我們平常所見的霧霾就是Mie散射的一個例子。
下面是Rayleigh散射變強的對比,在傍晚太陽光線在空氣中傳播距離比較長,導致藍光大部分散射開來,天空呈橘紅色,
這是Mie散射變強的對比,空氣中大顆粒粒子比較多,會造成霧霾現象。
體素介質的建模:我們有時需要一些特殊的參與性介質,它的精度要求比較高,可能需要一些特殊的光照,它的運動軌跡可以由美術來設定。
我們在Houdini中根據力場生成帶擾動的風場,來驅動這些介質,用兩套實時計算的偏移場來混合介質的位移。
三、渲染
渲染分成4個部分,首先是Froxel的渲染。Froxel是Frustum+voxel的縮寫,從字面上就可以知道,它是將相機的視錐分割為一個一個的3D體素,我們之前說了,使用froxel渲染的是200m以內的視錐。
視錐分割成了體素後,第一步我們會對每個體素注入介質的屬性,同時我們對每個體素生成shadow mask來儲存它們的可見性資訊。第二步計算每個體素的光照結果,第三步將每個體素的光照結果沿相機視線方向進行離散化積分,得到最終的光照結果。
這個影片展示了近處200mFroxel的渲染結果,太陽光是從左上角打下來,場景中有多個區域性動態光源,這裡的體積霧的建模會根據風力隨機擾動屬性值。
接下來是RayMarch的介質渲染,如圖所示,我們會在Froxel後發射光線來繼續渲染之後的參與性介質。首先我們需要計算光線的起點和終點,然後擾動起點位置來防止一些ray march的pattern,紅色箭頭指向的小圖中,每個紅點代表raymarch的取樣位置,可以看到由於起點擾動,每個取樣點並不是規則的齊步步進。
一開始我們會使用比較長的步長來快速步進沒有介質的區域,當我們取樣到介質時,如圖所示,我們會多次試探來確定一個比較好的步長,我們在取樣到介質之後,會每次減半步長來回退,如果回退還在介質中則繼續減半步長回退,直至離開介質,然後再次步進介質中,如此迴圈直至回退數達到設定的最大值。
在介質中我們會逐漸加大步進步長,當離開介質後,我們會重新使用更大的步長來步進。
下面我們詳細討論下光照模型,我們計算相機透過介質看到的最終顏色,首先要知道背景顏色,就是圖中xs這一點的光照顏色,考慮到空氣中有參與性介質,背景顏色會根據穿過介質的透射率(圖中Tr項)做融合,最終的顏色除了背景色還有介質本身受光的顏色,我們需要在視線方向上做積分來計算介質本身的顏色。如圖所示,我們有多個綠色的離散取樣點,需要計算這些取樣點的光照,然後再計算取樣點各自的透射率,最終進行離散的積分。每個取樣點的光照比較複雜,它首先有BSDF項(f),我們一般用一個介質相關的Phase Function來表達它,其次是Visibility項,他來描述取樣點和光源直接是否有遮擋。
那麼我們來進一步看下實際遊戲中visibility項如何得到。它由三部分組成,第一部分是我們熟悉的CSM,第二部分是介質的shadowmap,它的深度是ray march得到,會根據ray march的透射率擬合一個深度,再使用esm和mipmap來進行陰影的柔和和反走樣。第三部分是地形的shadowmap,可以根據光照方向和地形高度圖離線處理獲得,我們為什麼要加入一個單獨計算的shadow mask volume呢?
原因是陰影的計算由於欠取樣會有很多瑕疵。如圖中紅圈所示,它對於最終畫面效果影響很大,所以我們考慮使用temporal的時序累積來提升陰影取樣效率,以及穩定陰影數值。
這個影片是我們加了temporal之後的效果,可以看到即使是動態的變化,畫面也會非常穩定。
對於光源的光照計算,我們會使用Zbin的資料結構來快取多個光源的可見性資訊,進行光源的剔除,然後計算當前取樣點的光照顏色,以及讀取當前取樣點的Visibility可見性資訊,為了畫面的穩定,我們會使用temporal來累積時序上的光照結果,近似提升取樣率。
但是有一個比較嚴重的問題,不同於Visibility的低頻資訊,光照是一個高頻資訊,對於全動態的光源,時序上的累積會造成兩個BUG,Ghosting現象和拖尾現象,Ghosting現象很好理解,當光源移動時,對於之前體素的光照累積會造成光照的拖影,這對於手電筒,探照燈等動態光源會有很明顯的瑕疵。
第二個問題是相機變化時的拖尾現象,在參與性介質各向異性屬性特別強時會出現,如圖所示,當相機轉換視角時,光照變化非常劇烈,此時再進行時序累積,會造成拖影。
但是如果我們因此不進行光照時序積累,會使體素光照欠取樣產生噪點與畫面抖動。
所以我們需要採取更精準的方法來處理全動態區域性光源,對於一條穿過光源的視線可以和光源原點構成一個平面三角形,針對其中一個三角形,順時針改變視線方向來進行離線預計算,一共使用96種相機方向,如圖所示,這樣每個平面三角形會生成一個3D紋理,然後我們根據角度將整個光源分為15個平面三角形,又由於對稱性,只需要記錄8個。
在光照的時候,我們根據視線與光源的交點計算3D紋理座標,進而進行取樣預積分的光照進行合成。
這個影片展示了使用直接計算光照和預積分計算光照的對比,可以看到前半段的直接計算由於欠取樣會有很多瑕疵,後半段的預積分可以完美解決問題。
最後一部分是重建合成最終的光照,為了效能考慮,我們會在1/16解析度上進行光照計算,每四幀順時針旋轉發射光線的畫素點。
如圖所示:第一幀發射最左上角的畫素,第二幀發射右上角的畫素,以此類推,在重建1/4解析度尺寸的光照時,如圖所示,綠色的畫素點是當前計算的畫素點,紅色的區域是我們需要重建的畫素點,假設圖示的白色畫素需要重建,我們考慮它周圍3x3的綠色有效畫素進行加權插值,當然我們要考慮深度的不連續性,防止前景和背景錯誤融合,如圖示的白色畫素需要進行重建,首先我們會選擇出周圍的3x3畫素,然後根據當前畫素的深度去進一步過濾深度相似的畫素,比較深度會引入一個問題,就是我們之前說每四幀順時針旋轉畫素,會造成重建時3x3的畫素深度都是前景或者背景,如圖所示,這9個取樣點全是背景沒有前景,所以我們需要使用特殊pattern來獲取最小和最大的深度範圍。
這張圖是重建前的1/16解析度的光照結果
這是重建完成1/4解析度的光照結果。直接將1/4解析度放大為全解析度會帶來各種問題。
在前景背景動態變化的地帶,比如樹葉間隙會造成重建不穩定,畫面閃爍.
所以我們使用一個帶深度權重的濾波核,對其進行雙邊濾波,並且使用噪點來擾動取樣位置,並依賴後期的TAA來進行時序上的降噪,穩定畫面。
四、效能統計
我們已經在天刀端游上線了第一版的大氣系統,當時使用的測試條件如圖所示。
測試多次效能取平均,最終的結果如圖所示:整體耗時在1.4毫秒左右,相較於它對於整體畫面的提升,遊戲幀率的下降幾乎可以忽略,總體效能以及價效比非常高。
最後,總結一下我們的大氣系統,它是實時全動態的渲染系統,可以大幅度提升畫面的質量與真實感,在整個時段和天氣系統中可以統一的整合進配置,方便美術製作、調整引數設定以及針對不同場景天氣進行管理,它的效能十分高效,對遊戲的幀率損失微乎其微。
未來的改進方向,我們希望精簡引數更容易達到比較好的畫面效果,以及更自動化離線製作的管線。
Bonus
此外,我們還持全動態的時段以及天氣,建模的體積雲和體積霧可以根據當前的引數來程式化混合,任意變換各種形態以及光照效果。
這個影片簡單的展示了從中午到傍晚,從晴天到下雨起霧的無縫動態過渡切換的過程,暴雨天氣下雨水打到物體上會自動程式化生成水霧,
我們複用了下雨潮溼效果的OcclusionMap,以及ESM深度比較來自動化實現,影片展示了雨霧開和關的效果對比
半透霧效是一個比較棘手的問題,如紅圈所示,水流這種半透物體的受霧特別容易穿幫,在濃霧中,我們希望玻璃這種類似材質可以完全隱蔽在霧中。
一個折中的解決方法是在RayMarch的pass,單獨輸出一個Fogmap,對它進行mipmap chain的生成以及高斯模糊,為了效能考慮這張fogmap只有32x32畫素,alpha通道儲存介質的透射率深度,我們最終霧效合成的程式碼如下,他並不是基於物理只是一種近似。
美術在遊戲中需要手動控制室外室外不同的介質濃度,美術會製作房子的簡模作為包圍盒,用於GI等系統。在體積霧中我們複用這些包圍盒,程式會自動的把它們體素化進Froxel,注入介質的時候根據結果選擇屬性。
Q & A
Q:體積雲和體積霧的光照有什麼區別?
A:主要有以下幾點區別:第一點它們支援的動態光源型別不同,體積雲支援太陽光、閃電,體積物支援太陽光、區域性光源以及GI,另外體積雲的可見性計算只有雲層的體積陰影,但是會有多個Phase Function來模擬多層閃射。
Q:比表陰影覆蓋距離?
A:地表陰影根據地表高度圖處理的覆蓋所有可玩區域,雲層陰影的覆蓋距離可以配置,邊界會使用淡出效果。
Q:Froxel的劃分以及rayMarch的距離?
A:8×8畫素是一個體素,深度上會有64層,ray march會根據地球大氣圈的高度計算一個終點,對於布徑有一個最大距離限制,如果透射率大於等於一,會提前退出。
Q:天空盒的處理?
A:一般天空基本只有程式生成的大氣閃射以及體積雲霧,有時美術會需求使用天空盒貼圖作為背景,我們會使用透射率以及美術可調的引數,讓美術手動選擇做Blender。