這是侑虎科技第507篇文章,感謝作者FrankZhou供稿。歡迎轉發分享,未經作者授權請勿轉載。如果您有任何獨到的見解或者發現也歡迎聯繫我們,一起探討。(QQ羣:793972859)
作者主頁:https://www.zhihu.com/people/pkhere,作者也是U Sparkle活動參與者,UWA歡迎更多開發朋友加入U Sparkle開發者計劃,這個舞臺有你更精彩!
遮擋剔除是當一個物體被其他物體遮擋住而不在攝像機的可視範圍內時,不對其進行渲染。在3D圖形計算中並不是一個自動進行的過程,因爲在絕大多數情況下離相機最遠的物體首先被渲染,靠近攝像機的物體後渲染,並覆蓋先前渲染的物體(這種重複渲染又叫做"OverDraw"), 它不同於視錐剪裁。
視錐剪裁只是不渲染攝像機視角範圍外的物體,而對於那些被其他物體遮擋,但是依然在鏡頭範圍內的物體,則不會被視錐剔除。當然當你使用遮擋剔除時,視錐裁剪還是會生效的。我們在遊戲中主流的Occlusion Culling 方案基本上是以下幾種方式:
CPU端:
預計算的原始的PVS
Umbra的dPVS
SoftWare Occlsion
GPU端:
GPU-Driven
Hierarchical Z-Buffering
其他硬件層面:
Early-Z Culling
1. 預計算原始的PVS(UE4自帶)
首先劃分好格子,然後離線計算每個格子內的所有物體的可見性狀態,這裏的劃格子是用Cell來描述的,格子一般是10M*10M,對於格子內的每個物體都會生成OCID,通過存一個BitArray記錄可見性,這樣做的好處是內存的佔用空間會儘可能小,我們用Unity實現了一遍。
優點:
運行時消耗極低,能有效的降低OverDraw的開銷,比較適合開闊的場景,個人認爲非常適合目前的手遊場景,而且實現比較容易。
缺點:
可見性計算算法一般偏於保守,無法處理動態模型的剔除,可見性烘培的時間較長,需要額外的佔用內存,調試工具需要自己開發。
幾個注意點:
-
Cell劃分和佈置:看了下UE4的算法,看平面上Cell儘可能進行等分,高度上的面片是動態計算,在實際操作時候,可以平面上也可以按照密度自適應合併Cell大小,採取用四叉樹進行管理劃分,高度上也可以分層然後考慮是否進行合併。
-
可視計算烘培:Cell內的可見性物體,根據自己的算法如何來操作。一般做法都比較保守,具體可以參照UE4的實現,蒙特卡洛隨機選取,細節在這裏就不贅述了。
-
Streaming:如果是大世界,需要考慮到內存的佔用和IO的換入換出,看過UE4它是隨着關卡的加載全部加載的。對於Unity中的實現,因爲大世界一般採用Streaming方案,所以可以隨着Chunk加載卸載去管理,Obect ID就得按Chunk去給,Visible BitArray也一樣,保證只有局部的可見性數據,內存比較好控制。
-
所有參數記得按照實際項目進行調優。
UE4中的具體過程參考,這種做法在UE中默認是關閉的,大家可以參考:
http://t.cn/EVj6wtB
2. Umbra的dPVS(Unity自帶)
這種方式是Unity目前通過Umbra內建的OC方案,雖然也叫PVS,看了Umbra創始人Timo Aila的畢業論文,發現和純離線的原理有很大的區別。它的離線下是不計算所有可見性的,而只是生成一個空間數據結構,也就是一個BSP描述的節點信息,用於之後的空間位置查詢。因此它在離線計算的時候速度可以提升很多,但是在線消耗也會提升,因爲它還是會產生很多問題,比如跟蹤可見物體的標記點,提取輪廓生成HOM等步驟。
優點:
引擎內建使用方便,烘培速度很快,簡單易用,調試工具方便。
缺點:
可見性計算算法同樣偏於保守,手游上實測運行時CPU消耗不穩定,有時候一幀2-3ms是常事,不支持Streaming大世界內存可觀。
Unity中的具體過程參考,大家可以參考:
https://docs.unity3d.com/Manual/OcclusionCulling.html
3. SoftWare Occlsion
直接參考Intel的文章吧:
https://software.intel.com/en-us/articles/software-occlusion-culling
4. GPU-Driven
後面的趨勢,可以全部採用Compute Shader來做OC,後面對於IB/VB進行合併處理。
優點:
可以用來結合Virtual Texturing和DrawInstanceIndirect基本可以做到花1-2個DP搞定整個場景。
缺點:
定製整個自己的Rending PipeLine,不過後面肯定是趨勢。
具體參考鮑鵬對於Siggraph15和GDC16的相關翻譯:
https://zhuanlan.zhihu.com/p/33881505
https://zhuanlan.zhihu.com/p/33881861
5. Early-Z Culling
傳統Z-Test其實是發生在PS之後的,因此僅僅依靠Z-Test並不能加快多少渲染速度。而Early-Z Culling則發生在光柵化之後,調用PS之前,這樣能提前對深度進行比較,如果測試通過則執行PS,否則跳過此片段/像素(Fragment/Pixel)。需要注意的是,在PS中不能修改深度值,否則Early-Z Culling會被禁用。這個方式是基於硬件的,因此一旦在硬件不支持這個特性的顯卡上使用此技術,反而會導致效率下降,目前Geforce 6系列上是支持這個特性的。
總結:
如果是手遊不是大世界,且原先CPU的負載就不高,那麼第二種方案是可以接受的;如果是大世界,且對於CPU耗時比較扣,那麼第一種方案適合;對於Unity就簡單粗暴一點,先照着UE順一遍很快就能出來。如果自己能定製管線,那麼CPU的OC方案就可以不用,直接用GPU-Driven。對於優化這件事,在通用引擎上需要自己更細粒度的定製化方案。
文末,再次感謝FrankZhou的分享,如果您有任何獨到的見解或者發現也歡迎聯繫我們,一起探討。(QQ羣:793972859)
也歡迎大家來積極參與U Sparkle開發者計劃,簡稱“US”,代表你和我,代表UWA和開發者在一起!