騰訊魔方引擎專家GDC分享:我們在手機上做出了主機級別的天氣系統

遊戲葡萄 發布於:2022-04-22

《暗區突圍》天氣系統設計演講。

整理/秋秋

4月初,騰訊魔方工作室羣自研的硬核FPS手遊《暗區突圍》又一次开放了測試,截至目前,該遊戲在好遊快爆已經擁有了超155萬的預約量,每次測試都能登上其新遊期待榜TOP 2;在TapTap也有超過72萬的預約量和8.5分的評分。

對於魔方來說,《暗區突圍》應該是他們研發過的、美術品質最高的寫實題材遊戲。此前魔方總裁張晗勁也曾告訴葡萄君,在射擊這條賽道上,他們的資源投入很大,頭也夠鐵。

那么這些投入究竟效果如何?在最近的GDC大會上,魔方工作室羣引擎中心的引擎專家陳家銘和引擎程序員應若晨,分享了他們如何做出《暗區突圍》中主機級別的實時演算天氣系統。

葡萄君將其分享內容整理了出來:

各位好,我們是騰訊遊戲魔方工作室羣引擎中心的陳家銘和應若晨。今天,我們想分享關於魔方工作室即將推出的手機遊戲《暗區突圍》中的天氣系統。從下面的視頻中能看到,我們的天氣系統支持實時體積雲和大氣散射,而不是靜態的天空盒。


首先,我們會講解動態大氣散射和體積雲的相關細節,以及將它們帶到移動設備上的優化。之後我們將分享一些相關的天氣效果,和天氣系統的設計思路。

01

低开銷的動態天空渲染方案

天空的大氣散射是一個復雜的過程,涉及到很多物理計算,因此我會試着用簡單的方式來解釋。

我們的大氣由不同大小的粒子或分子所組成,天空的顏色是由這些粒子對太陽光的各種散射決定。

爲了渲染出大氣散射效果,我們一般需要在視點以光线行進計算每個採樣點所接受的散射,然後計算它們的積。然而,這方法的計算量在遊戲和實時應用上很難實現。

UE的大氣散射優化

先讓我們來看看Unreal Engine中的 SkyAtmosphere 方案,它使用了一系列查找表(Look up Table,LUT)來減少計算量。具體而言,UE 在每一幀中使用計算着色器生成4個基本的LUT,分別爲透光度LUT、多重散射LUT、天空視圖LUT和大氣透視的3D LUT。

它不但基於物理,對美術也比較友好,同時在中高端移動設備上也能有良好的性能。但是老的設備帶寬非常有限,負擔不起每幀更新這么多的LUT。所以我們對UE的 SkyAtmosphere 做了一些優化:

首先,我們舍棄了大氣透視數據,使用高度霧來代替它。舍棄掉這個數據後,所有剩余的LUT都是2D的,因此我們現在可以使用像素着色器來更新它們——這非常重要,因爲許多移動設備仍然不支持計算着色器。

因爲《暗區突圍》局內的時間變化比較慢,所以我們分幀計算每個LUT,每幀只計算很少量的像素,在低端設備上也能承受。

爲了進一步優化,我們將天空視圖LUT 以半八面體參數化,並丟棄地平线以下的內容。這不僅節省了50%的光线行進計算量,也省了查找天空視圖LUT時調用昂貴的平方根指令。

以下是原始版本和優化版本在一天中3個不同時段的比較。可以看到太陽周圍有一些偏差,但這在遊戲中不太容易看到。

而且如下圖所示,優化後的天空在渲染性能上有顯著提升。從原始的1.35毫秒到優化後0.77毫秒,半八面體投影節省了將近40%的GPU時間。

天氣亮度與大氣散射

美術有時需要將天空變暗,以獲得更戲劇性的效果,很有可能他們會直接調整陽光亮度,但這不僅在物理上不正確,而且對我們的優化也不友好,會導致畫面閃屏——天空顏色就像在一幀幀卡頓地變化。


體積雲的實現

在遊戲中,體積雲的實現,可以歸納爲這樣幾個問題:

如何對雲建模。我們需要一些方法,來定義雲層各處的雲的密度。

如何計算光在其中的傳播。特別是對於雲這樣的白色散射介質,光线可以在其中多次反彈,比普通的表面物體更難計算。

這種Raymarching方法一般很昂貴,我們如何讓它在手機上運行。

雲建模

首先是如何對雲建模:

我們使用Worley噪聲制作3D噪聲紋理,將它平鋪在天空中,就可以定義基本的雲密度。

我們會使用基礎和細節2層噪聲,最終的噪聲是通過基礎噪聲減去細節噪聲得到的。細節噪聲會平鋪更多次,這樣我們不需要特別高的分辨率,就可以獲得足夠的細節。

但是僅僅這樣,它們不足以創造覆蓋整個天空的雲景。

於是我們引入了一張2D的Weather Map。在我們的系統中,Weather Map是由Cloud Mask組成的。每個Cloud Mask都有一個材質來指定繪制內容。

例如在下圖中,你可以看到我正在拖動一個Mask。這個Mask的材質輸出一個白色的Sphere Mask——因爲白色意味着更高的覆蓋率,因此那裏的雲變得更密集。我們也可以輸出黑色,則該位置的雲將被擦除。

爲了方便控制不同天氣下的雲層,我們有兩個全局參數:全局雲覆蓋率和全局雲類型。這兩個值將作爲材質參數傳遞給Cloud Mask,並更改繪制內容。

除了Weather Map外,我們還用到一個稱爲Cloud Profile的紋理。

在現實生活中,不同類型的雲在不同的高度有不同的形狀。因爲Weather Map只描述了XY平面的信息,所以我們需要使用Cloud Profile描述每種雲在不同高度的形狀。

我們使用了UE4中內置的曲线工具,借此可以在編輯器中輕松地創建Cloud Profile貼圖。上圖是這個工具的樣子,我們能看到每個參數都是一條曲线,其中x是歸一化高度,Y是它的值。

雲光照

雲的光照有5個部分:單次散射、多重散射、全局光、大氣透視、自發光。

因爲性能限制,我們沒有計算大氣透視,遊戲中使用的其實是一個簡化的計算,因此不會在這裏討論。此外自發光也比較簡單,這裏不作討論。

單次散射

對於單次散射,我們通過太陽光、陰影和相位函數相乘來計算能量。

在陰影採樣時,我們也不考慮細節噪聲,並且在每一步上使用更高LOD。對於相位函數,我們用一個經典的方法,通過混合兩個HG函數作爲最終相位函數。

天空環境光比較簡單,只是根據採樣點高度計算顏色。雲越高,天空環境光越亮。這其實是UE4中的方法,我們發現它很好用。

下面是开關天空環境光的效果對比。我們發現,如果沒有環境光,雲體有很大部分陰影的時候,很難分辨形狀;而开啓環境光後,雲在陰影部分添加了漂亮的藍調,造型就清晰多了。

上:無天空環境光 下:开啓天空環境光

我們還有一個地面環境光,用於來模擬地面反射的光线。這是以同樣的方式計算的,但是把高度參數反轉,這樣越低的雲層受光越強。

地面環境光的顏色是通過將地面視爲純色的Lambert表面,計算主光源的反射得到的。

這裏我們可以看到沒有地面環境光時,雲的底部比較暗。而打开地面環境後雲的底部變亮,更接近我們在日常生活中的感覺。

上:無地面環境光 下:开啓地面環境光

多重散射

多重散射指的是光子在雲中彈射了不止一次。模擬多重散射對於正確的外觀非常重要——雲看起來這么白,就是因爲當光束擊中雲粒子時,大部分會被彈开並繼續進入雲內,而不是被吸收。

特別是在雲層深處,多重散射對外觀的貢獻會變得更加明顯。這讓雲有一些反直覺的效果,比如這張照片中,雲的內部比外部更亮,就是因爲其內部的多重散射更加明顯。

爲了模擬這種效果,首先,我們參考了在寒霜引擎中的方法來計算一個大致的多重散射的強度。這個方法可以以較少的开銷完成一個近似的多重散射的計算。

這是沒有多重散射的效果,因爲光线無法到達雲層深處,雲看起來更像煙。

這是在有多重散射的情況下,雲層的整體亮度更準確,更像現實生活中的雲。

接下來我們參考地平线的方案,添加了暗邊的效果。注意下圖中圓圈的部分,在沒有添加暗邊效果之前,我們很難辨別這些部分的形狀;

但是加上了這個效果後,這部分就有了更多的細節。

體積雲性能優化

基於Raymarching的體積雲,在它的每一步都涉及大量的計算。我們一般需要至少每條光线64步,每一步都涉及許多紋理讀取和ALU指令。在手機上,這樣的計算量難以取得較好性能,以滿足我們渲染天空的預算要求。

我們的方案是,使用半八面體映射,把Raymarching的結果緩存在一個512 x 512的2D紋理中。由於天氣以及太陽方向在我們的遊戲中變化較慢,因此我們可以將緩存分多幀更新;

另外,我們需要兩張RT,一張用於渲染天空,一張用於分幀的Raymarching,並且在Raymarching完全結束時交換兩張RT。

這么做的優點是什么?

我們可以把緩存在任何動態反射效果中重復使用。例如《暗區突圍》使用了平面反射,天空在水中的倒影不需要再做額外的Raymarching。

雲的運動在八面體空間中相對較慢,可以與重投影技術很好地結合。

同時,爲了有效地限制計算量,以避免移動端的GPU過熱。我們採用了以下策略:

1.棋盤渲染技術。這個技術有助於節省每幀需要計算的 50% 的光线。

下面的圖片展示了棋盤格式更新的效果。

2.切片。我們通過將RT分成4-16個切片,我們每幀僅更新一個切片,來實現分幀更新。

然而,切片也會導致雲在移動時看起來較爲卡頓。爲了解決這個問題,我們可以插值緩存的採樣方向。


例如,雲分4幀從A移動到B點,假設我們在當前幀中查看B點,我們現在比上次更新周期過了1幀。由於雲的移動量是已知的,因此我們可以向後追溯並找到點C,從視點到C的方向將是重新投影的方向。

下面的圖片展示了插值後的效果。


3.時序升採樣。如前所述,不加優化的話,我們在Raymarching時可能需要64步才能有較好的效果。所以我們想稍微優化一下,節省GPU 預算給其他效果。因此我們採用類似於 TAA 的技術,在每一幀中,我們對每條光线的起始點應用一個全局偏移。並且將結果和之前幀的結果混合,來得到最終結果。

我們還使用了重投影,來減少雲在快速移動時產生的“鬼影”問題。

無重投影,可以看到雲層移動時重疊的“鬼影”

重投影开啓,“鬼影”情況大大減少

4.壓縮最小化內存存儲和帶寬。我們還使用了一些方法,來壓縮體積雲的存儲大小。

通常我們使用半浮點型來存儲射线行進結果,單張512 x 512的RT需要2MB,這對於主流設備來說是可以接受的,但是在舊設備上還是有優化空間,因此我們想嘗試支持使用RGBA8的格式。

最終,我們將散射的結果除以相位函數,並且預計算一個曝光值來降低整體亮度,以及Gamma壓縮等方法,成功將貼圖壓縮進了RGBA8的格式裏。

5.可擴展性和性能。下面是在iPhone 11上測試的不同設置的一些性能。

可以看到在優化前,iPhone11需要大概10ms來完成一幀的更新。而在开啓切片、時序升採樣等優化後,我們可以將开銷控制在1ms以內。

03

暗區突圍中的動態天氣效果

下面讓我們來看看《暗區突圍》項目中的一些動態天氣效果。

雲影

因爲我們將體積雲的結果保存在RT中,所以我們可以通過一些簡單的計算,把結果投射在地面上,做出雲影效果。

它的實際效果如下面的視頻。


這是我們在編輯器中錄制的一個短片。它包含雨滴粒子和溼潤效果,雨本身是用粒子渲染的。

溼潤效果通過調整材料參數來達成,參考了Waylon的方法。遮擋貼圖則使用了CSM Scrolling技術,來支持快速更新遮擋貼圖。

然而這些細節仍然需要持續的打磨和優化。比如遮擋採樣开銷過高,沒有太陽光和AO的情況下,場景顯得太平等等,還需要更多的優化。


閃電

接下來是閃電,我想分享一些在實時渲染中制作真實閃電的關鍵點。

下面的動畫來自YouTube上一個名爲the slomo guys的頻道,它清楚地顯示了一次完整的打雷的過程。

現實生活中的閃電,有三個階段:

Lightning Leader。這是一條像蜘蛛網一樣的路徑,從雲端延伸到地面。實際上肉眼是幾乎看不見這個現象的,因爲它太快了。但這很酷,所以我們決定實現它。

Return Stroke。當Lightning Leader到達地面時,就產生了我們所說的“閃電通道”。巨大的電流從中穿過,把空氣加熱到非常高的溫度,出現閃電和雷聲。

Re-Strike。在Return Stroke之後,可能會有若幹次Re-Strike。Re-Strike沿着同樣的通道發生,通常比Return Stroke小,平均發生3到4次,產生閃爍的效果。

爲了創造閃電,我們需要對它建模。這裏我們使用分形算法,來創建閃電通道。

算法本身很簡單,就是不斷地分裂线段,每次分裂時偏移中間的節點。

在分裂一段節點時,隨機地創建當前節點到末端的新线段,並且把它旋轉一定角度,即可得到分支的效果。

然後,我們將結果轉換爲四邊形組成的網格,同時用頂點顏色存儲一些信息,來模擬從雲層到地面的生長動畫。

這是它最終動起來的效果。我減慢了Lightning Leader的速度,這樣我們就能看得更清楚了。


場景是通過直接增強主光源亮度來模擬閃電的光照的,強度則基於相機和閃電之間的距離計算。燈光方向不會變化,因此物體的影子並不正確。但是閃電發生得很快,所以也不是特別明顯。

爲了照亮雲層,我們計算雲和閃電的距離。雲的位置是通過和雲層求交來計算的,因此結果不是百分之百準確,但對於移動端來說已經足夠了。

04

創作工具

接下來的部分,我想分享一些在我們的系統中關於用戶體驗和軟件工程的東西。

天氣創作

首先是我們如何在我們的系統中定義一個天氣。

一般的想法是,我們可以有一個天氣參數的結構體,裏面包含了太陽強度、雲量、霧密度等。這樣不同的天氣就是一組不同的值。

但是在我們的實現中,我們希望用戶可以擁有最大的靈活度,所以我們避免了這樣的硬編碼。

在我們的系統中,用戶可以使用UE的Level Sequence工具,直接將任何屬性添加到系統中。系統將基於一天中的時間或太陽角度,來採樣該Sequence,這樣我們就可以讓天氣參數跟着時間變化。

並且,我們允許多個Sequence同時生效,多個Sequence之間有疊加順序,較高Sequence中的值將覆蓋較低Sequence中的值。同時每一層都有一個不透明度,通過控制不透明度值,可以控制Sequence的強度,就像Photoshop中的圖層一樣。

這樣一來,用戶可以將不同的天氣元素參數,組織在不同的Sequence中。並且通過不同的不透明度組合出最終的天氣。

舉例來說,一個晴朗的天氣的預設,它保存的不透明度可能是,基礎層是1,雲是0,霧是0.2等等。

我們再做一個叫做多雲的預設,它的雲、霧兩個Sequence的不透明度較高;再做一個叫做雨天的預設,它的雨層會有一定不透明度。

Gameplay邏輯最終看到的就是這些預設,如果Gameplay要求改變天氣,系統就只是在這些不透明度值之間過渡。

天體系統

接下來是天體系統,它幫助我們定位太陽、月亮或天空中任何東西。

這個系統允許你爲場景定義時間和地理屬性,包括一天中的時間、一年中的時間、時區、緯度和經度等。有了這些設定後,該系統可以根據天體屬性,自動將它定位到正確的位置。

圖像參考捕獲

這個功能允許你把攝像機放在關卡的任何地方,只需輕輕一點,系統就會遍歷時間和天氣,渲染成圖像並進行保存。同時它還支持立方體貼圖和HDR格式。這樣,美術可以更方便地迭代天氣效果。

建議

最後是一些實現類似的天氣系統插件的建議。

因爲每個項目都有自己非常具體的需求。例如,並不是每個項目都需要體積雲或者基於物理的大氣。而且有些項目可能對材質有一些特殊的要求等等。

因此最好不要嘗試做一個开箱即用的解決方案,否則類似的需求使得你必須爲每個項目創建分支。在虛幻引擎中,材質和藍圖很難管理,因爲它們是二進制的,維護多個分支會很麻煩。

我們最終使用了一個方案,盡可能的去解耦所有的功能,包括把所有的功能特性都分成Actor、Component或Material Function。用戶可以在藍圖編輯器或材質編輯器中組裝這些功能。

這樣做,雖然讓用戶在早期帶來了多一點的設置工作,但將來會少很多麻煩。

我們分享了在我們的天氣系統中,如何渲染真實的天空,以及一些基本的天氣效果,但這不是終點。我們也在致力於打造動漫風格的天氣系統。並研究如何將把更極端的天氣條件,如暴風雪,沙塵暴移植到移動端實現。

最後,我們要感謝魔方和魔方引擎中心爲我們提供的支持與幫助。

 

追加內容

本文作者可以追加內容哦 !

2024/05/04 - 外匯經紀商評分