您好,登錄后才能下訂單哦!
Dynamic Batching不生效該怎么辦,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。
最近在項目開發過程中,無意發現游戲場景的繪制占用了大量的Batches,幾乎一個模型顯示就占用了一個Batch,而Saved by batching數量幾乎為0,即沒有任何合批渲染優化。這顯然跟預期相去甚遠,因為雖然場景里有多達上百個模型需要繪制,但大部分都是一模一樣的卡牌模型,引用相同的材質球,按理絕大部分都是可以被Unity自動dynamic batching,進行合并批處理的。哪到底是哪里出了問題?
于是翻看Unity Manual,檢查Dynamic Batching的規則,可以簡單概括為以下幾條:
一般情況下,Unity僅支持對Meshes小于900頂點的物體進行Dynamic Batching,如果Shader里使用了頂點位置,法線,UV值,則僅支持300頂點以下的物體;如果使用頂點位置,法線,UV0,UV1和Tangent向量,則僅支持180頂點以下的物體。
如果兩個物體的scale剛好是呈鏡像的,如scale分別為(-1,-1,-1)或(1,1,1),他們不會被Dynamic Batching。
引用材質實例不同的物體不會被Dynamic Batching,即使兩個物體的材質本質上沒有任何不同。這句話的理解有點繞,簡單舉例就是說,相同的材質實例化了兩份,分別被A和B引用了,那么A和B是不會被Dynamic Batching的,因為他們引用的是兩個不同的實例。
擁用lightmaps的物體將不會被Dynamic Batching,除非他們指向了lightmap的同一部分。
擁有多Pass通道的Shader的物體不會被Dynamic batching。
簡單排查下,模型是達到Unity的1,2,4,5點要求的。于是開始懷疑材質實例引用不同導致了問題。
查看Inspector,可以看到Mesh Renderer引用的Material確實就是所需要的材質球,但后面卻加了Instance字眼,單擊圖中紅框處,Project窗口也并沒有跳轉到材質球文件所有位置,說明這份material是在運行時被實例化的,不是Prafab預設時指向的材質球引用。由此我們可以推測,每份模型都各自實例化了一份自己的material實例,導致沒有被Dynamic Batching。知道了問題原因,還要知道問題出在哪里。檢查物體上綁定的腳本,查看與material相關的代碼,可以看到有這么兩處:
private Texture _mainPlaneTex; private Texture _flashPlaneTex; public MeshRenderer m_rendererPlane; private Material _cachedPlaneMaterial = null; private Material cachedPlaneMat { get { if (_cachedPlaneMaterial == null && m_rendererPlane != null) { _cachedPlaneMaterial = m_rendererPlane.material; } return _cachedPlaneMaterial; } }
private void SetGray(bool bGray) { if (bGray) { cachedPlaneMat.SetTexture("_MainTex", _flashPlaneTex); } else { cachedPlaneMat.SetTexture("_MainTex", _mainPlaneTex); } }
這個模型有模型變灰色的需求,實現方式是通過獲取該物體的material,再視情況將MainTexture設置為正常/變灰貼圖。出問題的地方十有八九就在這塊代碼。查看MeshRenderer的接口,原來Unity提供了兩個獲取Material的方法接口,分別是material及sharedMaterial。
搞懂這兩個接口的區別,弄清楚Unity在底層到底搞了什么鬼,估計離真相就不遠了。
Returns the first instantiated Material assigned to the renderer.
Modifying material will change the material for this object only.
If the material is used by any other renderers, this will clone the shared material and start using it from now on.
Unity官方說當使用Renderer.material獲取Material引用時,會把Render里Materials列表第一個預設的Material進行實例化,并將返回實例。這樣,當我們對這個物體的Material進行修改時,只會修改實例,而不會修改到最原本的材質球,比如我們將同個模型Prafab拖放N多個到場景里,當我們對其中一個模型A的材質球進行修改,比如替換貼圖,其他模型是不會受到影響的,因為A修改的僅僅只是自己實例化出來的材質球。
The shared material of this object.
Modifying sharedMaterial will change the appearance of all objects using this material, and change material settings that are stored in the project too.
It is not recommended to modify materials returned by sharedMaterial. If you want to modify the material of a renderer use material instead.
而如果調用Renderer.sharedMaterial接口,Unity則不會多此一舉地幫我們做實例化,再返回實例了,直接就是返回最原本的材質球。還是上面那個例子,當我們給A模型的材質球替換貼圖,會發現場景的所有模型都被替換了貼圖。
看完了兩個接口的差異,也終于明白了問題的根本原因。為了實現模型變灰的需求,代碼里使用Renderer.material來獲取material實例,殊不知每個物體都各自持有了一份不同的material實例,使得Unity沒辦法將這些一模一樣的物體進行合批渲染,當同屏顯示的物體數目多達上百,就意味著多出上百個不必要的Drawcall,多了不必要的損耗。
而這個問題也不能簡單地將Renderer.material改為調用Renderer.sharedMaterial就解決了,總不能把一個模型變灰,所有模型都要跟著變灰吧?正確的解決方法就是預設正常/灰色兩個材質球,根據不同的情況替換材質。
最后貼下優化前后,場景Drawcall的對比:
優化前
優化后
可以看到,在同屏模型較多的情況下,Batch數由近100驟降到2,基本都被Unity動態合批處理了。
看完上述內容,你們掌握Dynamic Batching不生效該怎么辦的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。