對於移動平台來講,一直不建議大量使用粒子特效。因為對CPU和GPU來講,粒子系統都是一個性能消耗的大戶。但是現在粒子特效在游戲中的需求越來越高,因此做了一些Unity粒子優化方面的嘗試。由於針對移動平台,因此不考慮Computer Shader這種DirextX11的特性。
拿來做優化實例的粒子特效,包含5個粒子發射器。類型為Billboard的粒子X3,Mesh粒子X2。其中一個Billboard粒子和一個mesh粒子共用了一個材質。對於這個粒子特效來說,5個DrawCall,一個都沒有Batching掉。
然后我們將這個粒子復制10個,發現成功Batching的只有billboard粒子,Mesh粒子沒有動態Batching。
按照其官方文檔來講,Unity動態合批的規則比較苛刻。首先材質必須一致,其次頂點屬性個數不超過900個【實際上已經支持遠不止900個了】,對於一個Shader,如果包含了頂點位置,UV,頂點顏色三個屬性的話,那么這個個數就只有300個。
對於Billboard粒子來說,它基本上會符合上述的要求,因為Billboard粒子拋棄了這些屬性。但是對於Mesh粒子來說,需要共用同樣的材質。
表面上的規則是這樣的,但實際應用上有所差別。
首先是Billboard粒子,即使是使用相同材質的粒子,也要保證這些粒子在渲染隊列中是連續的,否則不會完全的Batching。這就需要在粒子的Renderer標簽中設置Sorting Fudge/Sorting Order/Order in Layer來保證它們的渲染順序連續。但是調整Render Queue的弊端也比較明顯,特效er在制作特效的時候,並不一定希望相同粒子的渲染隊列連續。因為對於半透明物體,渲染順序會影響他們的顯示效果。
其次是mesh粒子,這個的問題在於多次嘗試,依舊沒有一次成功Batching掉的經歷。即使使用的Mesh是一個Quad,發射器發射的粒子很少,絕對不會超過300個頂點限制的粒子。復制多個出來,依舊不會batching。但是並沒有在Unity的官方文檔中找到Mesh粒子不會被動態Batching掉的相關說明。所以這塊到底真實情況如何我也不能保證,如果有嘗試成功的一定要告訴我。
___________________________________________________________分割線____________________________________________________________________________
然后就是一些其余的嘗試。
首先是合圖,這也是Unity的文檔中動態合批時給出的建議,在多個不同的粒子中使用一個材質球。但是根據我的嘗試,在Mesh粒子中並不適用。Mesh粒子雖然有UV,可以進行合圖。但是如上述所說,並不會動態合批,最終的結果只是減少了一個材質球而已。Billboard粒子通過Texture Sheet Animation中的設置可以實現有限制的UV分割(感謝@柒夏爸爸 指正),不過用於UV分割是均分,合並后的紋理不能公用空白區域,會有些浪費空間。
嘗試使用Shader+腳本的方式來代替粒子效果。
對於一些簡單的粒子效果,理論上完全可以使用Shader+腳本的方式來簡單模擬。因此,我嘗試性的寫了個Shader,通過頂點着色器來實現旋轉,放縮,Billboard效果。
用Shader+Quad的方式代替之前特效中的幾個粒子發射器,然后復制多份,打包到安卓平台監視性能。令人驚訝的是,和使用Billboard動態合批優化后的粒子比,CPU性能消耗高了不少。原來的粒子特效復制十個擺放在場景中,CPU的渲染在10ms+,DrawCall在100以內。修改之后的效率是CPU渲染12ms左右,DrawCall 110左右。簡單的分析原因就是上面講過的,首先是這些替代的Quad完全打亂了的billboard粒子的渲染隊列導致不連續不能動態Batching,導致DrawCall增加,CPU時間占用提高。不過從OverDraw來看,GPU倒是釋放了部分的性能,但這往往以效果的犧牲為代價。
但是Mesh的渲染隊列調整只能在Shader中修改Queue或者腳本中調整SortOrder【Unity2017已經支持直接在材質設置渲染隊列】,如果使用同個Shader來應用不同的Material,需要加腳本來控制SortOrder排序。
根據新的嘗試,使用Shader通過頂點着色器實現旋轉,縮放,Bllboard,在片段中實現閃爍和序列幀播放效果,從功能上來說,並沒有問題。但是真正的問題是出在動態Batching上,使用頂點動畫的Mesh,在動態Batching下會出現頂點位置的異常。目前沒有找到很好的解決方案,如果在Shader中DisableBatching的話,性能的消耗代價太大。