很多童鞋的Unity作品完成后,發現場景卡的要死,盡管在模型階段采用了很多優化建模方法,但還是卡頓。電腦端都這么卡,發布到移動端就更不用說了。遇到這種情況,很多童鞋急得團團轉,不知如何是好。此時,就必須對場景進行深度優化,當然了,Unity深度優化的方法很多,本文小姐姐將和大家一起探討Unity3d最重要也是最有效的場景優化方法-遮擋剔除(Occlusion culling),希望對遇到場景卡頓的童鞋有所幫助。當然,對於機器配置高的童鞋,雖然暫時沒遇到卡頓,可別忘了,你的作品最終不是在你自己的機器上運行,而是要在配置不高的用戶機或移動端運行的,所以,場景的深度優化是每個人繞不過的一道坎。
那么什么是“遮擋剔除Occlusion culling”呢?通俗地講,就是將距離相機當前位置最近的一組非透明物體完全遮擋(看不到)的物體,動態地禁用渲染(禁用渲染一些物體,游戲視圖的渲染負擔就會減輕,作品跑起來就會流暢一些),這就是“遮擋剔除”。簡單地說,就是把被遮擋住的物體剔除掉。都被擋住看不見了,還不剔掉,留着這些物體,除了因增加系統渲染負擔而導致場景卡頓外,沒有任何意義。這只是對遮擋剔除的文字描述,為了讓童鞋們直觀地理解,下面我們用圖解詮釋。
在認識“遮擋剔除”之前,得先了解什么是“視錐剔除( Frustum Culling)”。大家都知道,攝相機視野的空間形態是一個四棱錐,Unity稱之為“視錐(Frustum)”,默認情況下,Unity會和人的眼睛一樣,自動將視錐以外的物體動態地禁止渲染,也就是只渲染視錐以內的物體,這就是“視錐剔除”。一般情況下我們是看不到“視錐剔除”現象的,但“視錐剔除”在運行時會自動啟用。
下圖左上角幾條線封圍的部分就是相機的視錐(俯視),因為未使用遮擋剔除,所以場景中的所有物體均可見。

下圖為添加遮擋剔除並使用其可視化預覽功能的情況。在右下角彈出的可視化預覽面板中,激活“Camera Volumes(相機體積)”,取消“Occlusion Culling(遮擋剔除)”復選框的勾選,目的是先禁用遮擋剔除,只預覽“視錐剔除”效果。說到這里,有些童鞋肯定有些暈,不是說添加了遮擋剔除,怎么有禁用了呢?因為小姐姐是想先給大家直觀地展示一下視錐剔除的效果,可一般情況下大家是看不到的,而遮擋剔除使用后,視錐剔除和遮擋剔除的效果都可以用可視化預覽,並且取消預覽面板中的“Occlusion Culling”復選,所預覽到的就只是視錐剔除的效果了,這下明白了吧?由下圖可見,視錐剔除后,只有視錐以內的物體可見,視錐以外的物體都被剔除掉了。對比上一張圖和下圖右下角游戲視圖的預覽,我們發現視錐剔除后,游戲視圖中所看到的內容沒有任何變化,這也就是說,視錐剔除不會改變游戲視圖的視覺內容,但是由於剔除了視錐以外的內容,游戲視圖相機的渲染量被大幅降低,如果不剔除,運行時游戲視圖的相機會因渲染負擔沉重而卡頓。視錐剔除是Unity3d的特性,無需特別設定,運行時會自動啟用。說到這里,一定有童鞋說,既然這是Unity3d特性,那你費什么話,啰里啰嗦說半天視錐剔除?因為搞清楚了視錐剔除的原理,對於理解遮擋剔除是非常有幫助的。

好了,接下來我們就來討論遮擋剔除。前面我們取消了預覽面板中的“Occlusion Culling”復選,現在我們勾選這一項(如下圖所示),也就是在Scene視圖中除了預覽視錐剔除的效果,也預覽遮擋剔除的效果。勾選后,由下圖可見,剛才視錐內保留的好多物體又消失了,這是因為相機處於室內,室內牆不透明,視錐內的室外物體都被室內牆擋住看不見,使用遮擋剔除后,那些物體也被剔除掉了,所以不可見了,但是對比下圖右下角游戲視圖的內容和前面沒有任何剔除以及視錐剔除后游戲視圖的內容,完全一樣,也就是說,遮擋剔除后,也不會改變游戲視圖的內容,但是在視錐剔除的基礎上,又進一步大幅度降低了相機的渲染量,運行時作品會更加流暢,因為被剔除物體的幾何、材質、光照貼圖、粒子系統等都不再被渲染了。

通過上面的靜幀圖解,大家對視錐剔除和遮擋剔除有了形象的理解,但是運行時,相機的位置和方位角是動態變化的,所以被剔除和被保留的物體也是動態變化的。前面為了簡化問題,小姐姐用了俯視視角圖解了視錐剔除和遮擋剔除,下面我們用動態GIF圖像來看看實際遮擋剔除和視錐剔除,由於這里對圖像大小有限制,所以GIF只能截取很短的片段,能說明問題就好。
下圖左側是游戲視圖,右側是Scene視圖,從Scene視圖可見,應用遮擋剔除后,當相機移動時,很多物體被視錐和因遮擋被動態地剔除了,但是游戲視圖始終是完整的,根本看不到哪些物體被剔除。這里說明一下,右側視圖當中地面深色的東西是地面的光照貼圖,不是物體,剔除后保留的物體只有相機前方很少的一部分,場景的全貌如第一幀所示,大家可以仔細觀察右側視圖中保留物體的動態變化。

由於上圖右側視圖太小,下面我們拉近並手工拖動相機觀察一下遮擋剔除和視錐剔除的綜合效果(如下圖所示)
到這里,相信大家對什么是遮擋剔除和視錐剔除有了直觀的認識和理解,需要說明的是,如果將整個場景合並為一個物體,遮擋剔除和視錐剔除沒有任何效果,為什么呢?因為視錐剔除是根據物體的最大外輪廓(或者說是物體的邊界框)是否在視錐以內來確定是否被剔除,如果合並成一個物體,相機怎么移動,物體的邊界框都在視錐以內。對於遮擋剔除來說,都合並成一個物體了,哪還有被遮擋的物體呢?所以要通過遮擋剔除和視錐剔除優化場景,嚴禁將所有物體合並為一個物體,除非場景規模非常小。需要說明的是,這只是剔除靜態物體的例子,對於動態物體的遮擋剔除,后面再說。
遮擋剔除的過程是使用一個虛擬相機構建潛在可見物體集合的層級結構數據,好讓每個攝像機在運行時使用這個數據來識別哪些物體可見,哪些物體不可見。 有了這些數據,Unity的相機將只對可見物體渲染,不僅降低了繪制調用(draw calls)的次數,並可提高游戲的性能。
遮擋剔除的數據由許多單元格(前面GIF圖像中Scene視圖中的藍色3d線框)組成,每個單元格是整個場景邊界體積的細分,具體地說就是這些單元格構成了一個二叉樹結構。遮擋剔除使用了兩個樹結構,一個用於視圖單元(靜態物體),另一個用於目標單元(動態物體象)。 所有視圖單元映射到定義可見靜態物體的索引列表,從而為靜態對象提供更准確的剔除效果。
在建模時要記住很重要的一點,即要考慮在物體大小和遮擋剔除的單元格大小之間求得最佳平衡。理想情況下,不應該有比物體小太多的單元格,同樣地,也不應該讓物體的尺度比單元格大到跨越了許多單元格。可以考慮在建模軟件中將大的物體分解成較小的多個物體來改善剔除,但有時也需要考慮將小的物體合並在一起以減少繪制調用(draw calls),並且只要它們都屬於同一個單元格,就不會影響遮擋剔除。
可以使用場景視圖的“overdraw(繪制重疊)”模式(如下圖所示),定性地來查看繪制重疊(之所以發生繪制重疊,是因為物體出現了相互遮擋)的情況,也就是查看場景中物體的相互遮擋情況。

我們還是以前面的場景為例,將Scene視圖由Shader改為overdraw模式,如下圖所示,這是使用未遮擋剔除的場景。overdraw模式用來查看場景中物體的遮擋情況,由下面中間圖可見,矩形框所標注的區域亮度很高,說明這個區域物體重疊嚴重,也就是被遮擋的物體很多。另外,可通過游戲視圖的狀態信息面板,定量地查看游戲場景的狀態,如下面右圖右上角所示。從狀態信息可見沒有遮擋剔除時,批次數量(Batches)為296(這個值越高,越卡頓,2017版之前用Drawcall來表示),三角面約42萬,頂點數約62萬。
下圖是使用遮擋剔除后的情況。由下面中間圖可見,矩形框所標注的區域亮度明顯降低,說明這個區域物體重疊已經消除,也就是被遮擋的物體已經被剔除。由下面右圖可見,批次數量降至62,三角面降至約15萬,頂點數降至約21萬。這里說明一下,批次數量(batches)的值是游戲是否運行流暢的重要標志,場景優化的目標就是降低批次數量、三角形數量、頂點數量,這些值越低,作品運行越流暢,否則會很卡頓。
由上面的圖解再一次說明,遮擋剔除對於優化場景性能非常重要,也非常有效。
遮擋剔除對模型的要求
要使用遮擋剔除,模型必須在建模軟件中被分解成合理尺寸的大小,以適應unity中遮擋剔除的單元格細分尺寸。在建模軟件中盡可能把模型分解成比較小的幾何體,且被比較大的物體(例如牆壁,建築物等)遮擋的布局對於unity中的遮擋剔除優化是很有幫助的。
遮擋剔除的一般設置
在檢視面板中,將要參與遮擋剔除計算的靜態物體標記為Occluder Static(靜態遮擋物)或Occludee Static(靜態被遮擋物),如下圖所示。Occluder是遮擋物,也就是遮擋其他物體的物體,Occludee是被遮擋物,也就是被其他物體遮擋的物體。有的物體需要同時勾選Occluder Static和Occludee Static,也就是這個物體既遮擋別的物體,也會被別的物體遮擋。有的物體只需勾選Occluder Static,例如比較大的物體,它是永遠不會被其他物體遮擋。而有的物體只需勾選Occludee Static,例如比較小的物體,因為它遮擋其他物體的可能性很小,單獨勾選Occluder Static或Occludee Static,可減少遮擋剔除的計算量,即可加快遮擋剔除的烘焙。如果將場景中比較大且不可能被其他物體遮擋的物體設置Occludee Static,或者將比較小且不可能遮擋別的物體的物體設為Occluder Static,除了浪費烘焙時間和增大遮擋剔除數據文件外,毫無意義,所以根據物體的實際情況,合理地對其進行遮擋剔除設置,對加快遮擋剔除烘焙和優化遮擋剔除數據非常重要。另外補充一點,很多人在烘焙場景時,按照官方的要求,將要烘焙的靜態物體直接設置為“Static(靜態)“,也就是將檢視面板右上角的“Static”復選框直接勾選,可當展開“Static”后面的下拉菜單的三角符時,發現后面有一堆這靜態那靜態的選項,如果直接勾選Static,就等於把這個下拉菜單里的所有選項(除Noting外)都勾選了,小姐姐認為,在烘焙場景時,不要直接勾選“Static”,而只需勾選“Lightmap Static”,全部勾上會給烘焙增加多余的計算負擔和時間,做什么計算,就勾什么選項,全部勾上,進行遮擋剔除計算時不分青紅皂白,會浪費很多時間,原因前面已經說過了,這里再次提醒一下。
這里特別說明一下,當使用LOD Group優化時,只有LOD0可作為遮擋物。
設置物體的Occluder Static和Occludee Static,也可以在遮擋剔除面板中完成,即單擊打開菜單Window > Rendering > Occlusion Culling,在彈出的面板(如下圖所示)中選擇“Object”,並且在場景中選擇“網格渲染器(也就是場景中的物體)”,遮擋剔除面板下方即會出現Occluder Static和Occludee Static兩個復選框,在這里勾選,和前面設置物體的Occluder Static和Occludee Static屬性異曲同工。遮擋剔除面板各按鈕的功能見下圖。
如果您覺得小姐姐的文章有價值,肯請您能給小姐姐的視頻教程一些支持,視頻教程中包含遮擋剔除優化在實際商業項目中靈活應用。