1. 程式人生 > >想用好虛幻4引擎做遊戲,你需要避免這些擾人的坑

想用好虛幻4引擎做遊戲,你需要避免這些擾人的坑

http://wemedia.ifeng.com/7884171/wemedia.shtml

對玩家而言,UE4帶來的作品都是不折不扣的視覺盛宴。

在手遊品質越發上揚的如今,已經有不少廠商開始使用一些效能更好的引擎,去嘗試遊戲製作了。而虛幻4引擎(以下簡稱UE4)就是其中之一,在這款引擎中已經誕生了諸如《鐵拳7》《地獄之刃》《帕拉貢》等一系列大作。對玩家而言,這些作品都是不折不扣的視覺盛宴。

日本網站4Gamer曾刊登一篇文章,分享了使用UE4在影象渲染中會遇到的難點以及解決方法,分享人是該引擎的開發商Epic Games日本分公司的高階工程師篠山範明。

在研討會上,篠山首先展示了虛幻4的整體流程圖,隨後他對和流程圖上各功能板塊有關的技術進行了介紹。

繪製物體緩衝的“Base Pass”時要注意的問題

我們先來把整個流程分為不同的單元,在各個單元裡進行深入解讀。


首先從上圖左起第二個板塊“Base Pass”開始。Base Pass等同於產生了物體緩衝(G-Buffer)。

眾所周知,UE4的繪製引擎採用了延遲渲染(Deferred Rendering)。所謂延遲渲染,是指將一個場景的幾何體(3D模型、多邊形)的光照、陰影、質感擱置到一旁,先著手於繪畫,然後在後半段再對光照、陰影、質感進行處理的處理方式。

即給人一種把原本的多邊形先繪製出來的印象,實際上不僅要繪製多邊形,前者的引數還需要配合後面光照和陰影的處理。其輸出目標,在成為複數緩衝時具有普遍性,但是這裡的緩衝我們稱之為“物理緩衝”。

為何要做這麼麻煩的事情?其實延遲渲染有兩個優點。第一個優點就是能將十分複雜的光照、陰影以每次一畫素的方式進行處理。第二個優點是並不明確限定用於光照的動態光源數,所以可以進行豐富的光照渲染。

現代遊戲影象在繪製複雜的場景時需要大多的光源,而在移動複雜著色器的時候,最終其結果不是在畫面上輸出,而是通過畫素著色器,處理畫素的編寫和撤銷,導致耗時嚴重、效能下降。為了全力迴避這一點而開發出的就是延遲渲染。


物體緩衝是指使用後照明和後處理特效的中間過渡環節

根據以上提到的這些,使用Base Pass輸出物體緩衝需要注意的兩點。

第一點簡而言之即“不繪製沒進入視線的物件”。

這種“投影剔除”(Frustum Culling),一般是通過CPU端來處理;為了整體覆蓋被稱為“包圍球”(Bounding sphere)的各個3D物件,物件是否在視野內的判定標準,是通過預先設定的包圍球來實行的。


舉一個包圍球被錯誤(或是正確)設定的案子。這種情況下如果繪製沒有被正確地剔除,會導致最終明明沒有繪製,但作為GPU的繪製物件卻投入了渲染管道(rendering pipeline),造成GPU產生多餘的負擔。


什麼程度的剔除會成功,可以通過Stat初始檢視(Stat InitViews)指令的“視錐體裁剪基元(Frustum Culled Primitives)”進行確認。

另外,實行強制凍結渲染(FreezeRendering)指令後移動鏡頭,就能夠確認視野外繪製了什麼。如果那個時點上,視野外本應剔除的3D物件被描繪了出來,就會顯示出不能被剔除的理由。在這種情況下需要特別注意確認包圍球的設定。

強制凍結渲染能夠看到此時點攝像機提出的結果

Base Pass要注意的第二點,是“不計算多餘的畫素”。

在影象處理的流程中,使用畫素著色器實際處理前,會有執行深度測試(Z 測試)的“Pre Z 測試”這一步驟。從這裡著手處理的畫素,會因為被某個東西所遮擋而無法繪製出來,這時可以進行撤銷處理。


但是,像半透明物件這種會伴隨α測試的繪製、視差遮蔽對映這種畫素著色器處理後會重新編寫深度值的情況,就不進行Pre Z測試,而通過處理實行分路迂迴。

執行α測試的隱藏(Masked)材料


畫素深度撤銷(Pixel Depth Offset)可以變更畫素深度(Pixel Depth)

但是,就算是不透明的物件,設定迴避Pre Z測試之後,便無法撤銷本來能夠撤銷的Pre Z 測試。這種情況是因為雖然用了畫素著色器處理,但是(之後的)Z測試失敗,這一操作被撤銷了,雖然繪製結果沒有變得奇怪,不過效能會跌落到最壞結果。

Z值的預階段“Z預階段”的注意點

下一個主題是“Z預階段(Z Pre Pass) ”。UE4的渲染管道,是在Bass Pass的物體緩衝寫出來之前,在僅預處理深度值(Z值)之後,執行Z預階段。


事先預處理深度值的目的,是將最終影像和同一深度緩衝的內容結果,在透視前獲得。Z預階段之後的Base Pass則是,參考預先得出的深度值緩衝進行Z預測試,因此通過在最終的畫面裡不留下畫素痕跡(即編寫後又被消去的畫素),以迴避畫素著色器的執行。

虛幻4只將背景物件這種靜態物件計入Z預階段,而動態物件則不計入Z預階段,以上設定為預設設定。但是虛幻4可以按照客戶要求來設定專案檔案“早Z階段(Early Z-pass)”和“動態早Z階段(Movables in early Z-pass)”。


或者通過勾選“作為遮光板使用(Use as Occluder)”可以設定各物件單位是否計入到Z預階段之中。


各專案的關閉渲染選項裡面,有“作為遮光板使用”這一專案。在設定為預設為ON時,勾選為off,就能夠調整各專案單位是否參與早Z

通常預設設定應該就可以了,但是在多邊形數很多的場景中,Z預階段的執行本身會對GPU造成很大負擔,所以這種場景下,上面提到的兩個設定,通過選擇on/off來比較效能可能會比較好。

光照的前段處理“預照明”需要注意的點

虛幻4中通過血濺和彈痕這種投射材質貼圖來實行的印花式繪製,是在光照(Lighting)之前,即通過“預照明(Pre-Lighting)”部分來處理並設計的。對於後段的光照處理,可以將印花式繪製囊括在光照物件裡。

和印花繪製相同,“從所有方向插入有關假想環境光的遮蔽率”(Ambient Occlusion,環境光遮蔽)的處理,也能通過預照明來完成。


與這種預光照繪製相關的麻煩已經成了常見問題,比如“印花被描繪得照明顏色過深”、“設定天窗後,印花下面變透明瞭”等等。



從結論來說,這是“虛幻4的做法”,當然《虛幻4》也提供了迴避此類問題的對策。那就是“延遲貼花(DBuffer Decals)”功能。

所謂延遲貼花(或稱Deferred Decal),是指和將印花進行物體緩衝不同,而是使用被稱作“延遲貼花”這一印花專用特殊緩衝來進行繪製,用物體緩衝進行光照和陰影處理之後,延遲貼花的內容也反映並實行了出來。印花的繪畫結果統合到了物體緩衝之中後,也因此出現了上文提到的問題。為了迴避這個問題,要準備專用的緩衝。

但是,加上利用物體緩衝這一條件,繪製比之前的處理執行緒更多,繪製負擔變高也成為其瓶頸。

雖然導致負擔變高了,但是應該實行印花表現而匯入物體緩衝嗎?還是說應該選擇別的表現方式來回避延遲貼花,這需要開發者好好考慮後再做決定。

通過光源周圍的處理而改變效能的“照明”階段

接下來,篠山說明的是光照的階段。虛幻4的光照分為“靜態光照(Static)”、“固定光源(Stationary)”、“動態光照(Movable)”三種光照範疇。


最先提到的靜態光照,為通過全部事先計算過的光線對映來處理的靜態光照。這一事先計算,可以使用專用的光照烘培,來計算將間接光都考慮在內的光照。

其次是固定光源,固定光源的光照本身是以實際時間來執行的,但是隻有陰影計算是事先進行了處理的。

具體而言,固定光源在UE4的影象引擎內部會被當作一般的動態光源,但配置到場景裡又成了基本不動的光源。因為它不動,所以也可以在陰影貼圖中用於預先生成陰影。

雖然有點複雜,但這種做法有個好處,就是配置的固定光源一組最多可以放置4個,其內部處理是稍有限制。

固定光源預生成的陰影貼圖中,一個光源能夠分別對應αRGB中4條通道的畫素格式。也就是說,第一個光源對應α通道,第二個光源對應R通道,以此類推。所以在αRGB中最多對應四個光源,一組光源的上限也就是四個。

如果設定第五個固定光源,那麼它將被當作動態光源來處理,即便是定義為固定光源,也不會提升其效能。


固定光源在一張陰影貼圖上最多設定4個,超過第五個將被視作動態光源

而動態光源如前文所述,包括陰影生成在內,它將完全在執行時進行處理。

我們之前說過延遲渲染下動態光源的數量不受限制,再來看它實際的效能。

設定兩組對比場景,一是將100個照射範圍狹小的動態光源放置到同一場景內,二是將8個照射範圍較大的光源放置到同一場景內,對比兩者的負荷大小。


得出的結果是後者的負荷更高。這裡的負載大也可以看作是計算量大。而計算量的大小,取決於場景內各個畫素受到多少個光源的照射。所以比起處理八個畫素各自被一個光源照射,處理一個畫素被八個光源照射的負荷要更高。


負荷率大小:藍>綠>紅

UE4可以通過光效複雜性(Light Complexity)功能來檢視光源的處理負荷,所以在設計的時候,可以不依賴延遲渲染的特性,而通過精確計算光源負荷來構建場景。


通過光效複雜性功能,就可以檢視已設定的光源的負荷率

光照反射存在最優解嗎?

不僅限於UE4,在所有即時遊戲的美術中,需要不少設計技巧的就是光照反射(Reflection)。

如果反射的材料是鏡面反射更強的金屬類,那麼周圍的場景就應該被映入其中,如果以材料的角度來看,就相當於受到了周圍所有光源的照射。光照反射要應對的就是周圍所有的光源,所以它是在表現材料材質的時候,關於真實程度的關鍵要素。

在UE4當中,有三種反射生成方法來處理會引起映象、映照等情況的材料,如下圖所示。


第一種是採用反射探頭來生成靜態反射。

在場景的任意位置都可以預先設定座標,然後可以進行全方位的透視,最後會生成立方環境貼圖(CubeMap)材質。而這裡的預生成座標點,被稱作反射探頭(Reflection Probe)。


靜態反射就好比是反射探頭處拍下的360°照片

關鍵的問題在於:在反射探頭處獲得的全方位場景,應該影響到場景內多大的範圍。

依然採用對比分析,首先在一個場景裡配置一個靜態反射,將其設定為影響整個場景;再配置200個靜態反射,設定為只能影響非常狹小的範圍,可以看到兩者的繪製負荷差距不大。


但是在設定了200個靜態反射的例子中,如果將各自的影響範圍調大,符合就會急速增大。

其原因與動態光源的例子相同。如果將多個靜態光源的影響範圍擴大,那麼在繪製一個畫素的時候,就必須參考計算多個靜態光源的引數,負荷自然就增大了。


稍微調大200個靜態反射的影響範圍,負荷就立刻增大

其實,已經配置了相鄰的反射探頭時,再將兩者的影響範圍重疊的意義並不大。反射探頭的設定以及其影響範圍的設定如果不合情合理,那麼就會增加很多無效的計算負荷。

如下圖例項的配置就更為合理:


紅圈:讓整個場景都處於光照反射的影響範圍內。

覆蓋各個房間的藍圈:主要的靜態反射,在這裡可以定義大致的統籌性引數。

繪製物件細節的綠圈:僅配置在能產生光照反射的特殊物件上。

第二種是螢幕空間反射(Screen Space Reflection,簡稱SSR)。

螢幕空間反射會根據透檢視的結果,在畫面座標系中進行區域性的光線跟蹤,在執行時刻(Runtime)上實時生成計算結果。

這種處理方式的好處是,即便僅擷取場景中任意一瞬間的畫面,也會精確地反映出動態角色以及動態光源影響下的光照反射。相對的缺點,就是對於畫面外場景的影響,會被完全無視掉。

使用SSR最典型的報錯,就是暴露了這個缺點所造成的。彌補它的有效方法,就是結合靜態反射來做處理。


如右圖,巖壁左側被畫面隔斷的地方,應該在水面中被反射,但是因為巖壁被隔斷在畫面之外,所以無法被正確反射

第三種是平面反射(Planar Reflection)。

這是將場景準確的映入平面的光照反射處理手段。如下圖例項,以水平面為反射面,從視點E來看右側的岸邊,那麼相當於在水下的視點E'來透視上下顛倒的岸邊的場景。所以被反射的場景需要進行兩次繪製,光照反射處理的負荷必然會高出不少。


又因為處理負荷比較高,所以平面反射常限定在水面等平面上使用,同時畫面的品質非常高。由於這些特性,比較推薦在過場動畫中使用平面反射的處理。比如《地獄之刃》在GDC 2016上放出的宣傳片,其中就使用了平面反射的處理技術。

關於半透明的各種問題

不僅限於UE4,半透明物件的繪製在實時圖形繪製領域都算是很難處理的問題。UE4在繪製半透明物件時也會遇到不少棘手的問題。

離我們最近的問題,可以說就是粒子效果,可能是因為日本的畫師都特別喜歡粒子效果,所以對它比較關心吧。

UE4在繪製半透明粒子效果時,不會更新深度值(Depth)。


將特效配置在場景中


但深度值沒有變化

這種情況下會產生的問題就是,將深度值設為關鍵值時,後期處理會變得很奇怪。

如下圖所示,聚焦到中間的火焰時,利用景深來讓影象增加朦朧感,但火焰的粒子效果在地平線上方的都被模糊了,下方則沒有變模糊,這樣的影象就非常奇怪。


這就是因為火焰的粒子效果沒有深度值,所以Z快取(Z buffer)裡只存在地面的製圖深度值。再加上地平線上的背景裡鏡頭非常的遙遠,所以背景和火焰都被判斷為離鏡頭非常遙遠,就被做了模糊處理。模糊處理器是無法判斷有沒有火焰粒子效果的,於是就成了這副樣子。

為了解決這類問題,就要使用到獨立透明度(Separate Translucency)功能。

使用時,半透明粒子效果在繪製時仍然不會更新深度值,但它被分離到別的快取中,與景深等後期處理分開。

換句話說,就是將半透明物件與不透明物件的繪製分開,在後期處理時優先應用於不透明物件。


在通常的後期處理中,粒子效果的繪製與普通場景的繪製是在同一快取裡處理的


而利用獨立透明度功能,就可以將粒子特效分離到另一個快取裡,並將其與後期處理分隔開


對比兩種處理效果的區別

利用這個功能的確能夠避免半透明粒子效果出現一些奇怪的模糊情況,但是卻沒法給它新增適當的模糊處理。所以即便能夠避免問題,但解決不了實際的困難。

所以即便是UE4,也會存在很多限制,而引擎的開發方還在試圖解決這些問題。

而在半透明繪製方面,還存在一個很棘手的問題,也就是繪製的負荷過高。首先可以思考一個問題:讓畫面整體的顏色都改變的後期處理,與近讓畫面一部分出現冒煙的粒子效果,哪邊的處理負荷更高?


簡單的看,可能因為畫面整體的畫素數很多,所以可能是前者更高,但其實答案恰恰相反。

乍一看,半透明粒子效果的煙霧僅佔畫面的一小部分,實際上它是經過多次重疊繪製而成的。檢視粒子的驅動線的框架,原因就一目瞭然了。在畫面的同一部分,煙霧的粒子改變大小並反覆繪製了多次。


我們可以用著色複雜性(Shader Complexity)功能的排錯檢視,來檢視。

在這個檢視中,重複繪製的區域會用紅色表示,可以藉此判斷產生問題的部分。越紅則表示改區域中,粒子繪製的負荷越高,需要調整。


降低粒子繪製負荷最有效且最簡單的手段,就是用獨立透明度功能,將粒子繪製分離到別的快取重,用低解析度進行繪製。

可以將反覆繪製、重合的半透明粒子用低解析度繪製,再調整好合適的大小,與主透檢視合成就可以了。如果將繪製半透明粒子的快取降低為縱橫各半的解析度,那麼簡單算下來繪製負荷就能變為原先的1/4,要是再將其減半,就能變為1/16。


在低解析度的快取裡繪製,然後擴大併合成,就意味著粒子的輪廓會變得更加模糊,而原本就是半透明的粒子即便更模糊一些,產生的影響也不會有多少。但解析度調得過低,也會暴露出解析度不高的問題,所以選擇快取解析度的時候要相當慎重,與負荷變化放在一起尋找最佳的配置。

以上三張圖就是在半透明物件的緩衝解析度在100%、50%、10%的繪製狀態下,繪製負荷的變化

粒子繪製的符合降低方法還有一種,就是在粒子動畫中,完全透明區域更多的時候可以用到的粒子剪影(Particle CutOut)功能。

通常的粒子是由兩個多邊形構成的,而粒子剪影的原理,就是為了避開完全透明的區域,由多個多邊形自動分割來進行繪製。

雖然這樣一來多邊形的數量會增加,但由於能夠避免畫素著色器的無意義執行,所以在大量的粒子進行繪製時,這或許會減輕負荷。


粒子剪影會自動分割多邊形


利用著色複雜性功能的排錯試圖,可以看到左側的四邊形粒子,與右側的多邊形粒子,在繪製時產生的重複繪製區域更少

UE4中順序被固定的後期處理

後期處理就好比相片修飾一樣,需要對最後的繪製結果進行加工處理。

相片修飾通常是針對2D的照片,在3D遊戲內的影象方面,由於需要利用透檢視中附帶的多種資訊,對影象進行三維的加工,所以兩者差距非常大。


不進行後期處理(左)與進行後期處理(右)的區別

UE4中預設了諸如色調、景深、光