動態合批與靜態合批其本質是對將多次繪製請求,在允許的條件下進行合併處理,減少cpu對gpu繪製請求的次數,達到提高效能的目的。
兩者是否開啟都可以在Project Settings -> Player -> Other Settings 下的 Static Batching 和 Dynamic Batching
1.靜態合批是將靜態(不移動)GameObjects組合成大網格,然後進行繪製。靜態合批使用比較簡單,PlayerSettings中開啟static batching,然後對需要靜態合批物體的Static打鉤即可,unity會自動合併被標記為static的物件,前提它們共享相同的材質,並且不移動,被標記為static的物體不能在遊戲中移動,旋轉或縮放。但是靜態批處理需要額外的記憶體來儲存合併的幾何體。注意如果多個GameObject在靜態批處理之前共享相同的幾何體,則會在編輯器或執行時為每個GameObject建立幾何體的副本,這會增大記憶體的開銷。例如,在密集的森林級別將樹標記為靜態可能會產生嚴重的記憶體影響。此時就必須去權衡利弊,為了更少的記憶體佔用,可能需要避免某些GameObjects的靜態批處理,儘管這必須要犧牲一定的渲染效能。
靜態合批在大多數平臺上的限制是64k頂點和64k索引(OpenGLES上是48k索引,macOS上是32k索引)。
2.動態合批是將一些足夠小的網格,在CPU上轉換它們的頂點,將許多相似的頂點組合在一起,並一次性繪製它們。
無論靜態還是動態合批都要求使用相同的材質,動態合批有以下限制:
-
動態合批處理動態的GameObjects的每個頂點都有一定的開銷,因此動態合批處理僅應用於包含不超過900個頂點和不超過300個頂點的網格。
- 如果shader中使用Vertex Position, Normal和single UV,可以批次處理最多300個頂點,而如果shader中使用Vertex Position, Normal, UV0, UV1和Tangent,則只能使用180個頂點。
- 注意:將來可能會更改屬性計數限制。
-
如果GameObjects在Transform上包含映象,則不會對其進行動態合批處理(例如,scale 為1的GameObject A和scale為-1的GameObject B無法一起動態合批處理)。
- 為了驗證此說法,筆者親自做了測試,對於兩個同樣的物體:
a.一個物體保持scale為(1,1,1),改變另一個物體的scale:
1).當三個軸向的縮放值為負數(簡稱負縮放)的個數為偶數時可以合批處理,即scale為(1,1,1)和scale為(-1,-2,3)、(-1,2,-3)、(2,-3,-4),此時三個軸向的負縮放的個數為0個、2個、2個和2個,均為偶數個,可以合批處理,注意正負號,縮放數值可以隨便更改;
2).當三個軸向的負縮放的個數為奇數時可以不能合批處理,即scale為(-1,1,1)、(1,-2,3)、(1,2,-3)和(-2,-3,-4),此時三個軸向的負縮放的個數為1個、1個、1個和3個,均為奇數個,不可以合批處理。
b.同時改變兩個物體的縮放,仍然符合上述的三個軸向的負縮放的個數為奇數個時不合批,偶數個時合批:
1).A物體(1,2,3),B物體(-1,2,3)、(1,-2,3)、(1,2,-3),奇數個負縮放不合批
2).A物體(-1,2,3),B物體(-1,2,3),奇數個負縮放不合批,其他組合類似
3).A物體(-1,-2,3),B物體(1,2,3)、(-1,2,-3),偶數個負縮放,其他組合類似
總結為:對於兩個相同的物體,當兩個物體三個軸向的負縮放的個數為偶數個時(0個,2個),可以合批,當兩個物體中任意一個物體或者兩個物體同時三個軸向的負縮放的個數為奇數個時,不合批。此前有很多文章說對於不同縮放的物體,無論是否為負縮放,均不會合批處理,筆者使用的unity版本是unity2019.1.7f1,可能是因為unity在某個版本已經修復了該問題,有知道的朋友,請告知一聲,謝謝。
- 為了驗證此說法,筆者親自做了測試,對於兩個同樣的物體:
-
使用不同的Material例項會導致GameObjects不能一起批處理,即使它們基本相同。陰影渲染(shadow caster)是一個例外,下文會解釋為什麼。
-
帶有光照貼圖的GameObjects有額外的渲染器引數:儲存光照貼圖的索引和偏移/縮放。一般來說,動態光照貼圖的GameObjects應指向完全相同的光照貼圖位置才能被動態合批處理。
-
使用多個pass的shader不會被動態合批處理。
- 幾乎所有Unity Shaders在forward rendering(前向渲染)中都支援多個燈光 ,為了他們有效地進行額外的傳遞。額外的pre-pixel lights的繪製呼叫不會被動態合批處理,這個在移動平臺上比較少遇到。
- The Legacy Deferred (light pre-pass) rendering path 中禁用動態批處理,因為它必須繪製兩次GameObjects。
github 上Unity官方總結了25種不能被合批處理的情況, Unity-Technologies/BatchBreakingCause
動態合批處理的工作是在cpu上將所有GameObject頂點轉換到世界空間,因此,如果該工作小於執行繪製呼叫,則這是一個優勢。繪製呼叫的資源需求取決於多方面的因素,主要是使用的圖形API。例如,在 consoles or modern APIs,比如Apple Metal,繪製呼叫開銷通常要低得多,此時動態合批處理就不再是優勢了,所以動態合批處理並不是萬能的啊。
動態合批處理(Particle Systems, Line Renderers, Trail Renderers)
對於Unity動態生成的幾何體的元件,動態合批處理與網格相比有不同的工作方式。
- 對於每個相容的渲染器型別,Unity將所有可混合內容構建為1個大型頂點緩衝區(Vertex Buffer)。
- 渲染器為批處理設定材質的狀態。
- Unity將頂點緩衝區(Vertex Buffer)繫結到圖形裝置。
- 對於動態合批處理中的每個渲染器,Unity將偏移更新到頂點緩衝區(Vertex Buffer),然後提交新的繪製呼叫。
在評估圖形裝置呼叫的成本時,渲染元件的最慢部分是材質狀態的設定。相比之下,將不同的偏移繪製呼叫提交到共享頂點緩衝區的速度非常快。
注意:
1.不同材質的陰影會動態合批,只要繪製陰影的 pass是相同的,因為陰影跟其他貼圖等資料無關
2.目前,只有 Mesh Renderers, Trail Renderers, Line Renderers, Particle Systems和Sprite Renderers支援合批處理,而skinned Meshes,Cloth和其他型別的渲染元件不支援合批處理。
3.渲染器僅與其他相同型別的渲染器進行合批處理。
4.對於半透明的GameObject,按照從前到後的順序繪製,Unity首先按這個順序對GameObjects進行排序,然後嘗試對它們進行批處理,但由於必須嚴格滿足順序,這通常意味著對於半透明的材質更少使用合批處理。
5.手動的合併GameObject是代替合批處理的好辦法,比如使用Mesh.CombineMeshes,或者直接在建模時將多個網格合併成單個網格。