2017年2月1日 星期三

Unity的批次處理與GPU Instancing技術介紹

作者:馬瑞曾 原文
潤稿:Kelvin Lo

我們都希望能在場景中放它一百萬個物體,可惜的是渲染和管理大量的遊戲物件代價是消耗大量的CPU和GPU效能,因為有太多Draw Call需要處理,所以我們必須找到其他的解決方案。

本文我們將討論兩種優化技術,它能幫你減少Unity的Draw Call數量從而提高整體效能,那就是批次處理和GPU Instancing。

批次處理


開發者會遇到最常見的問題之一就是效能不足,因為CPU和GPU的處理能力的問題。一些遊戲可以在PC上順暢執行,但在手機上不行。遊戲執行是否流暢Draw Call數量的影響很大。有幾個常見的解決方法是批次處理,包括Static Batching和Dynamic Batching。

Static Batching靜態批次處理可以讓引擎降低任何網格的Draw Call,要讓場景中的物體使用Static Batching,只要將它標記為靜態物件並在Mesh Renderer中共用相同的材質即可


因為Static Batching不會在CPU上做頂點轉換,所以它通常比Dynamic Batching更有效果。但它會用較多的記憶體,如果你的場景有相同物體的多個實例物件,Unity會將它們組合成一個大網格,這可能會增加記憶體使用。Unity會將盡可能多的網格結合到一個靜態網格中,並將它作為一個Draw Call送出申請。這種方法的缺點是靜態物件在整個執行週期都不能移動。

Dynamic Batching啟用時,Unity會自動試著把物件編到同一Draw Call處理。要使物體可以被動態批次處理,它們必須要共用相同的材質,還要遵守一些限制:

  • 頂點數量:Dynamic Batching場景中物件的每個頂點都有一定的消耗,因此動態批次處理只適用於少於900個頂點的網格物件。舉例來說,如果你的Shader使用頂點位置,法線和一個UV,那麼你可以動態批次處理多達300個頂點。而如果你的Shader使用頂點位置,法線,UV0,UV1和切線,那麼只能動態處理180個頂點。這部分我們未來有可能會改寬限制。
  • 反射資訊:如果物件的Transform有反射資訊,例如A物件的大小是(1f, 1f, 1f),而B物件的大小則是(-1f, -1f, -1f),則無法做批次處理。
  • 材質:如果物體使用不同的實例材質,即使它們本質上相同,也不會被批次處理。但Shadow Caster Rendering是個例外。
  • 渲染器:擁有光照貼圖的物件有其他渲染器參數,例如光照貼圖索引或光照貼圖的偏移與縮放。一般來說,動態光照貼圖的遊戲物件應該指向要批次處理的完全相同光照貼圖的位置。
  • 不能使用Multi-pass著色器的情況:幾乎所有的Unity著色器都支援多個燈光的Forward Rendering模式,這會需要額外的渲染次數,所以繪製"額外的每圖元燈"時不會被批次處理,傳統的Legacy Deferred(Light Pre-Pass)渲染通道不能被動態批次處理,因為它必須繪製物體兩次。
Dynamic Batching原理是將所有物體的頂點轉換為CPU上的世界空間來工作,所以它只能在渲染Draw Call的工作量小於CPU頂點轉換工作量的時候才會有優化作用。當在PS4遊戲機或者像Metal這樣新的API,Draw Call的消耗通常低得多,或許開啟Dynamic Batching也無法提高效能。瞭解以上限制後,就能正確判斷是否要用批次處理來提高遊戲效能。

GPU Instancing


提高圖形效能另一個好辦法是使用GPU Instancing。GPU Instancing的最大優勢是可以減少記憶體使用和CPU消耗。當使用GPU Instancing時,不用打開批次處理,GPU Instancing的目的是一個網格可以與一系列附加參數一起被推送到GPU。要使用GPU Instancing,物件必須使用相同的材質,且可以傳遞額外像是顏色或浮點數的參數到著色器。

Unity從5.3版本開始支援GPU Instancing。 唯一的限制是在遊戲物件上要使用相同的材質和網格。且目前只支持以下平台:

  • Windows DX11/DX12 和 SM 4.0 或更高
  • OpenGL 4.1 或更高
  • OS X and Linux:OpenGL 4.1 或更高
  • 手機:必須支援OpenGL ES 3.0 或更高/Metal
  • PlayStation 4
  • Xbox One

如果想要進一步優化,例如減少管理場景物件的消耗,也可以使用Graphics.DrawMeshInstanced方法。 只需要傳遞網格、材質和附加屬性來繪製該物體。目前的限制是一次最多1023個實例物件。在Unity 5.6已經加了Graphics.DrawMeshInstancedIndirect新方法,可以用來指定需要渲染的實例數量。

GPU Instancing做法


要建立支援GPU Instancing的材質可以在專案新增一個專用Shader:
從Project視窗點右鍵選Create->Shader->StandardSurfaceShader(Instanced)。


然後,在材質裡的Shader選擇這個新的著色器。



雖然實例的物體共用網格和材質,但你可以用MaterialPropertyBlock API針對每一個物體單獨設定不同的著色器屬性。

要注意的是,如果一個物件被標為"Static"進行了Static Batching,那這個物件就不能用GPU Instancing技術處理,檢視介面會出現警告,提示"靜態批次處理"標誌可以在Player Settings中取消。如果物件符合Dynamic Batching條件,但同時也符合GPU Instancing條件,那麼這個物件會自動使用GPU Instancing技術。

當使用Forward Rendering渲染模式時,受多個燈光影響的物體無法有效地產生實例。只有Base Pass可以有效利用產生實例。此外,使用光照貼圖或受不同光或Reflection probe影響的物件無法產生實例。如下圖所示,您可以開啟Frame Debug視窗,查看和GPU Instancing相關的Draw Call(被標記為Draw Mesh(Instanced)"。



GPU Instancing是一個非常強大的功能。在Unity 5.6你可以用Graphics.DrawMeshInstancedIndirect繪製大量網格。我們用Mac Pro畫出約68萬個各自不同顏色的移動方塊,並保持穩定的60FPS。


可以看看Unite大會上的超強展示




總結

本文中我們描述了用於優化渲染效能的兩種技術:批次處理和GPU Instancing。我們展示了如何實踐它們並探討可能的應用。正因為有這樣的技術,我們才能夠在繪製大量物件的情況下還能保持穩定效能。


更多關於GPU Instancing的文章
http://unitytaiwan.blogspot.tw/2016/05/unite-2016-unity-54-gpu-instancing.html

沒有留言:

張貼留言

著作人