在2020虛幻引擎技術開放日中,來自完美世界《幻塔》遊戲開發工程師丁許朋,爲大家帶來了《幻塔》遊戲的開發技術分享。

以下是演講實錄:

丁許朋:大家好,丁許朋,我現在完美世界幻塔工作室任客戶端引擎工程師。我今天結合我們遊戲《幻塔》來給大家做一些技術分享。我們遊戲是基於虛幻引擎4的MMORPG類型的遊戲。

那麼我們可以看出,我們遊戲主要有兩個特點:一個是開放世界,第二個就是高自由度。

那對於在移動平臺做這種開放世界類的遊戲,我們會面臨着很多的技術問題。

第一個技術問題就是大世界,就我們的地圖目前有2.5K×2.5K,當然了對於現在的遊戲來說2.5Kx2.5K並不算大,但是當你的地圖達到一定程度的時候,我們面臨的技術問題其實是一樣的。

當然Unreal本身對大地圖提供了比較好的支持,比如說Unreal提供了流式關卡,流式關卡的意思就是我把大地圖切成小的地圖,然後按需加載。

在加載策略方面Unreal也提供了兩種:

第1種:觸發式的地圖加載,觸發式的地圖加載適合於,比如說你這個世界並不太連續,兩個世界之間有明顯的交界,我這個時間我可以在交接處放一個觸發盒子,比如說我走到這個地方,這個觸發盒子的時候去加載另一塊地圖。

第2種:世界合成器,就是World Composition,它裏邊可以分Layer,就是在這Layer裏邊我可以放不同的Level,這樣的話我可以根據Layer定義不同的加載策略,這樣的話會更靈活。

這是Unreal做的比較好的地方,當然Unreal也有做的不太好的地方,比如說Unreal的地形系統,那我們來看一下Unreal的地形系統。

在Unreal中 它的地形系統叫做Landscape,在右邊這張圖就是它的地形系統的創建界面,比如說我們可以就是調一些參數就可以創建出來,就在點擊這裏的Create 就可以創建出來一個地形,或者說在這個標籤裏從外部導入一個高度圖來生成地形。

那麼跟地形相關的第一個概念就是Quad,那Quad就是Unreal就是地形系統的基本單位,它的大小呢跟Scale相關,那麼這個紅框圈出的地方就是Scale。

我們看它的單位這裏是顯示的是100,因爲Unreal裏邊它的單位是釐米,所以說這100是一米,如果你不調這個參數的話,它的一個Quad就是相當於是一米見方的一個塊。

第二個跟地形相關的概念就是Section,這Section它是Quad的容器,也是地形LOD的基本單位,比如說地形LOD它計算的規則是就是高階的,低階LOD的擴展會減一半。

比如說第0級LOD是31×31,那麼第1級的LOD就是15×15個Quad,那目前我們可以看出來,每個Section就可以有7×7、5×15、31×31,一直到255×255,一共這6種可選。

那麼第三個跟Landscape相關的概念是Component,那Component是Setting的容器,它是UE4中地形繪製和地形裁剪的基本單位,目前它也是隻有兩種配置:一個是1×1;一個是2×2。

就相當於是說一個Component只有1個Section,或者有4個Section。

那麼UE4這是個地形系統的優點,它是地形是統一管理,相當於是我一個Landscape的Actor就管理了所有的Landscape相關的Component。那麼它地形、地塊之間的數據耦合度比較小,它是以Component爲單位就是執行LOD計算,因爲它Component是比較獨立的,所以說它的計算可以並行起來,多線程來處理。

地形系統的缺點是什麼?因爲它一個Component一個Actor管理了所有的Component,那這個Actor初始化的時候,所有的Component都已經初始化了,這樣會導致三個問題:

第1個問題:內存佔用會比較大,地形相關的內存佔用會比較大。

第2個問題:它會增加渲染線程的裁剪開銷,因爲它這個Component在註冊的時候,它這個渲染代理就是SceneProxy都已經創建好了,就相當於是渲染線程在做剔除的時候,有可能你這個地形就是離玩家非常遠,它根本是不需要考慮,但是說它現在也需要考慮要不要把它裁掉。

第3個問題:當你視距比較大的時候,地形相關的Draw Call會非常高,增加了CPU的消耗。

這裏給大家舉個例子,比如說我這一個Component有4個section,那每個Section裏面是63×63的Quad組成的,當這個Component離玩家很遠的時候,它會退化成2×2的Quad,如果你這個Scale沒有改,你相當於是你還是1米×1米,相當於是這一個Component只覆蓋了2米×2米的大小,這樣會導致你這個視距裏面有非常多的Component。

在Unreal中一個Component會包含比如說剛纔我們講的它是2個4×4,2×2 就是4個Section,如果這每個Section它的差別不太大,它會把這個4個Section合併成1個Draw Call,就相當於是1個Component。要麼是有1個Draw Call,要麼是有4個Draw Call,這樣會導致你視距很大的時候,你這個地形相關的Draw Call會非常高。

那麼針對這個問題,我們目前有三種解決方案:

第1種:我們可以調整地形的創建參數,讓地形生成的Component數儘可能的少。比如說我可以把Quad調大,然後可以把Section裏邊的Quad數量調多,讓它生成的Component的比較少。當然,這樣會帶來另一個問題,就是你這個地形Cutting(切割)的力度會變粗。

第2種:可以用虛擬紋理,就是說地形制作上使用虛擬紋理,虛幻引擎官方會在4.26版本的引擎上支持這個功能。相當於是一個Draw Call,就可以把所有的地形畫繪製完成。但是虛擬紋理它本身也是有消耗的,所以說它具體在Mobile(移動)平臺表現怎麼樣,還需要進一步的測試。

第3種:對地形進行切割,切割本身也分兩種。第一種,用Houdini軟件來進行切割。第二種,自己寫了個工具來進行切割。

剛纔我們所講到的是優化地形的Draw Call,對於地形的面數,我們目前的做法是用了LODBias。

例如在繪製地形的時候,這個地形是第零級,但我們偏移一級來畫,即用第一級來畫;那第一級地形,就用第二級來畫,以此類推。這樣的話很好地控制地形的面數。

我們看一下這張圖:

在這張圖中我們可以看到我這個視距是非常遠,我們可以看到地圖中的大部分的物件。我們在優化之前,我們那個地形的Draw Call有220多個,優化之後我們地形的Draw Call現在只有37個。

對於面數我們使用地形偏移,我們目前地形偏移了一級,偏移的話在這個視角下優化了大概15萬面,這個優化其實是相當可觀的。

那麼在進入第二個主題之前,我們來看一下就是移動平臺的渲染管線。

移動平臺由於硬件會有兩個限制,一個是頂點數受限,一個是Draw Call受限。

我們看一下這是移動平臺的渲染管線,其實它這裏邊會有一個Tiling的過程,Tiling過程就相當於它VS先走一遍,然後決定這個三角形在哪個Tile裏邊,個時間它會把它臨時數據放到這個主存中,如果你的頂點太多,它這邊就會形成一個熱點。

第二個Draw Call受限,Draw Call提交的時候,CPU要準備大量的數據,而且提交Draw Call的時候會把這個Shader生成,編譯成對應平臺的編碼。

所以說,我們在移動平臺主要是這兩個限制,我們在做開放世界遊戲時,面對超大視距在角色這個位置的時候,我們能夠看到的地圖非常遠。

這樣會導致兩個問題:

第1個:要畫的東西非常多

第2個:頂點數也會非常多

而由於移動平臺這兩塊都是受限的,相當於雖然你看到了這麼多東西,但是硬件其實是繪製不完的。

這就需要裁剪策略,提前把對視覺不重要的裁掉,Unreal也提供了好幾種裁剪方案:

第一種:距離剔除,即Distance Culling。根據你攝像機的遠近,來把它剔除掉。在每個X軸上設置一個剔除距離,對玩家交互不強的且視覺不重要的,就剔除掉。

第二種:視錐剔除,即Frustum Culling。把不在攝像機視野範圍之內的剔除掉。

第三種:預計算可見性,即Precomputed Visibility。先預先離線計算好,走到這個位置的時候,查詢這個計算好的表,哪些東西是可見的,哪些是不可見的。預計算可見性只適合靜態場景,主要的應用場景是室內。

第四種:動態遮擋查詢,即Dynamic Occlusion。針對於動態遮擋查詢Unreal提供了三種方案。

第1種:基於硬件的遮擋查詢,即Hardware Occlusion Queries。被查詢物會根據它的包圍盒提交一個Draw Call,來硬件做Depth Buffer,查一下它有沒有被遮擋住。這種方案它的缺點就是Draw Call會比較高。

第2種:HIZ的遮擋查詢,即Hierarchical Z-Buffer Occlusion。這種方法不需要提交Draw Call,它首先把Depth Buffer做mip,之後根據這個被查詢物的這個包圍盒再映射到屏幕空間,計算物體最長的邊,根據最長的邊來算一個它在哪個mip裏做,然後在那個mip裏做遮擋查詢。

這兩種都是基於硬件的方法,如果這個裁剪是CPU在做的話,它需要回讀到CPU,回讀的話會延遲一幀或者幾幀,這樣就導致了一個問題,因爲延遲了所以裁剪是不準的。Unreal還提供了一種基於軟件的遮擋查詢。

第3種:軟件遮擋查詢,即Software Occlusion Queries。被查詢物先用CPU進行軟光柵化,作爲軟光柵化來生成這個Depth Buffer,然後再來做遮擋查詢,這樣的好處就是它不需要回讀了。它全是用CPU來計算的,它具體性能表現怎麼樣,還需要一定的測試。

那麼對於開放世界的遊戲,另一個不得不提的主題就是植被系統了。如果你這個開放世界很大,但是植被很少的話,那你這個世界的細節不豐富。以前的植被系統都是實例化繪製的,就是Instance Static Mesh即ISM。

ISM的裁剪是它的主要問題,比如1000個物體,人物視野只看到了1個,但是這1000個物體都是要提交的。

所以說Unreal針對這個問題做了個Hierarchical的Instanced Static Mesh即HISM。它的實現是就是根據樹形結構來做了Cluster來分組,可以把裁剪粒度更精細,還可以做Cluster LOD,同時我還實現了距離剔除。

這些都是針對CPU的粗粒度剔除,對於GPU優化方面,Unreal提供了Early Z Pass。植被系統中樹、草都是Mask材質,Mask Material需要做Alpha test,所以Unreal目前的做法是把Mask材質先在Early Z Pass中深度畫一遍,利用這段時間已經完成了Alpha test,在Early Z Pass把Mask材質的Pixel過濾掉,這樣就優化了Mask材質。

其他減少Draw Call的方法:

第1個:動態批次合併,即Dynamic Instancing,這是4.2版本加入的功能,對於我們這個遊戲來說,開啓了態批次合併之後,優化了150個Draw Call左右。

第2個:Hierarchical LOD,即HLOD。例如一個大的關卡,可以生成一個代理,一個Draw Call把它畫出來。HLOD也有他的問題,它會導致你的包體跟內存變大。

對於開放世界遊戲,還有一個問題就是陰影,我們看到的東西很多,如果陰影繪製的距離很近,這樣就導致了場景很平,沒有層次感。Unreal在PC平臺上提供了很多應用方案。

第1種:聯級陰影,即Cascade shadow map。

第2種:距離場陰影,即Distance field shadow。

Unreal官方建議,在近處用聯級陰影,在遠處用距離場陰影。

第3種:膠囊體陰影,即Capsule shadow。利用物理資產Physical Asset來生成陰影,這種實現有一個好處就是它支持間接光照。例如一個場景裏全是烘焙的這種Lighting,聯級陰影是生成不了這種陰影的,但是膠囊體陰影可以對這種間接光也產生陰影,但是你要做的非常細的話,你需要很多膠囊體,這是它的一個缺點。

目前在移動平臺上,UE4的陰影方案主要是兩種:

第1種:調製陰影,即Module Shadow。

第2種:級聯陰影,即Cascade shadow map

先來看調製陰影,先看這張圖:

調製陰影它其實它主要是跟場景做了簡單的乘積,它跟其他陰影混合的時候,是明顯的疊加效果,所以說這種基本滿足不了現在遊戲的品質要求。所以UE4在移動平臺上只能選擇級聯陰影。級聯陰影最初是爲了解決陰影貼圖的精度不夠而導致的透視堆疊。

根據這個圖說明,把視錐分爲三級,再根據太陽方向,把小視錐包圍住,生成一個正交投影的矩陣,然後每一個級生成一個ShadowMap,這樣也更符合人類的這個視覺特點。

在UE4裏級聯陰影的設置界面

設置界面包括了設置級數的、每一級結束了怎麼切換等等。

對於級聯陰影,它主要的問題是什麼?

主要的問題是因爲,級聯陰影每一級都是一個視錐,它需要單獨做跟場景做裁剪,就是CPU內的裁剪消耗比較高;另一個是它級數很多,需要繪製的陰影的物件會很多,導致你的場景的Draw Call會比較高。

在移動平臺上,由於帶寬受限,陰影貼圖做成1024這種分辨率是一個比較合適的選擇,這樣就需要更多的級數去提高它的精度,但是由於之前講的幾點限制,所以在移動平臺做不了這麼多級數。

針對級聯陰影的問題,我們現在的做法是用4級,前2級是全動態,後面2級是隻畫靜態物體,前2級做分幀刷新,即不是每幀都提交,後面2級做低頻刷新,即隔幾幀才刷新一次。

通過這樣修改CSM,可以做到400M距離的陰影效果。

我們遊戲的第二個特點就是高自由度,在以前的遊戲中,人物與場景交互是很少的,當然隨着Unreal對物理引擎就是良好的封裝,爲我們創建這種更高自由度的交互就是提供了可能。

我們遊戲中做了踩不同的地形和不同的物件,會有不同的聲音反饋,這樣更真實。還有不同的砍樹,踩水等交互,另外場景中大部分物體都是可以被推走或者破壞掉的。

那麼這種根據不同的交互產生不同聲音是怎麼做的呢?

我們地形分了4層,在草這一層,通過匹配對應的物理材質的聲音來實現。

例如砍樹,我們是全物理模擬的,而並不是簡單的播一個動畫。

例如砍草,整個世界大部分草是可以砍掉的,而且別的玩家是可以看到的,而不是簡簡單單的一個客戶端表現。

這是踩水,角色走在水裏邊會產生這種漣漪效果。

遊戲中大部分物件是可以被破壞掉,或者挪走等等。

爲了製作出高自由度的遊戲,也因爲UE4的Movement的組件本身提供了非常多的Movement mode,所以在飛行、游泳等之上,設計擴展出了攀爬滑板等模式。

攀爬

游泳

滑板

我們遊戲立項的時候,拿到的是虛幻4引擎4.20版本,目前我們升到了4.25,那隨着版本的迭代UE4,對移動平臺的支持越來越好,比如說它添加了動態批次合併、骨骼實例化等等一些新功能。我們相信隨着UE4對移動平臺支持越來越好,以後會湧現出越來越多的基於UE4的精品遊戲,謝謝大家。

相關文章