在上一篇文章中有個技術點引發了我的好奇
GPU Instancing 這個技術是Unity自帶的功能,但是他不支持SkinnedMeshRenderer,所以要采用特殊方法
他這里采用GPU蒙皮+ECS+Job+Burst 基本上做到了目前優化最優解
找到Unity的一篇文章《Animation Instancing – Instancing for SkinnedMeshRenderer》
https://blogs.unity3d.com/cn/2018/04/16/animation-instancing-instancing-for-skinnedmeshrenderer/
動畫實例化– SkinnedMeshRenderer的實例化
作為開發人員,我們始終了解CPU和GPU的性能。隨着場景變得越來越大,越來越復雜,保持良好的性能變得越來越具有挑戰性,尤其是當我們添加越來越多的角色時。我和我在上海的同事在幫助客戶時經常會遇到這個問題,因此我們決定花幾個星期的時間致力於提高實例化字符的性能。我們稱這種技術為“動畫實例化”。
我們經常使用GPU Instancing來實現戶外場景,例如草地和樹木。但是對於SkinnedMeshRenderer(例如字符),我們不能使用實例化,因為皮膚是在CPU上計算的,並一一提交給GPU。通常,我們無法通過一次提交來繪制所有字符。當場景中有很多SkinnedMeshRenderers時,這將導致大量繪制調用和動畫計算。
我們找到了一種方法來降低CPU成本,並通過動畫實例化補充Unity中的GPU實例化。您可以在GitHub上獲取我們的代碼。請注意,這是自定義的實驗性解決方案,直到最近我們才與一些企業支持客戶共享了該解決方案。現在我們已經准備好征求更多反饋–請在項目評論中直接讓我們知道您的想法!
目標
這個實驗項目的最初目標是:
- 實例化SkinnedMeshRenderer
- 實施盡可能多的動畫功能
- LOD
- 支持手機平台
- 剔除
由於時間限制,並非所有目標都得以實現。支持的動畫功能包括:根動畫,附件,動畫事件(尚不支持的功能:轉場,動畫層)。另外,請記住,這僅適用於使用OpenGL ES 3.0及更高版本的移動平台。
但是,我們認為該實驗成功地證明了這種方法可以產生有趣的結果。讓我們深入探討一些細節。
動畫生成
在為角色使用實例化之前,我們需要生成動畫。我們將角色的動畫生成為紋理。這些紋理稱為動畫紋理。紋理用於在GPU上蒙皮。
該生成器從附加到有問題的GameObject的Animator組件中收集動畫。它還收集動畫事件。從Mecanim系統轉移到動畫實例化非常方便。如果要在角色上附加某些內容,則需要在“附件”設置中指定可以將某些內容附加到的骨骼。
當我們完成生成動畫紋理時,Animation Instancing腳本將在運行時加載動畫信息。請注意,動畫信息不是動畫剪輯文件。
實例化
應用“動畫實例化”很簡單。讓我們將Animation Instancing腳本添加到我們生成的游戲對象中。“每個頂點的骨骼”參數控制每個頂點計算的骨骼數量。這里要意識到的重要一點是,減少骨骼可以提高性能,但會降低准確性。
接下來,我們需要修改着色器以支持Instancing。基本上,您需要將這些行添加到着色器中。它不會影響着色,但會為蒙皮添加一個頂點着色器。
1
2
|
#include “AnimationInstancingBase.cginc”
#pragma vertex vert
|
績效分析
我們使用了Mecanim示例場景中演示場景的稍微修改版本,並在iPhone 6上測試了它的性能。讓我們仔細看看原始示例和實例示例的探查器視圖。
中央處理器
原始項目產生300個字符,而我們的FPS大約為15。要達到至少30 FPS,我們必須將字符數限制為大約150。在“動畫實例化”版本中,我們可以產生900個字符,同時保持30 FPS。
如您所見,CPU上的計算會減慢項目速度。
使用實例化項目,我們在CPU上大大減少了動畫計算(骨骼和蒙皮等)。這樣,我們可以生成五到六倍的字符!
在測試場景中,繪制環境需要大約80個繪制調用。角色有三種材料。因此,我們有三個繪制調用來渲染角色。
如果不實例化,則生成250個字符需要大約1100次繪制調用(3 * 250個字符及其陰影)。
使用“動畫實例化”時,生成800個字符后,繪圖調用僅增加到約50個。您可以看到,實例化列中有4800個批處理的繪圖調用和48個批處理(3 * 8個字符+ 3 * 8個陰影)。那是因為我們每批提交100個字符。
顯卡
這項技術會稍微增加GPU成本,因為我們在GPU上添加了外觀。如果角色有陰影,我們必須在陰影通道中再次對角色進行蒙皮。但是,由於它降低了CPU成本,因此提高了總體幀速率。通常,CPU成本是游戲中人群模擬中的最大問題。
記憶
額外的內存用於存儲動畫紋理。紋理保持皮膚矩陣。我們使用RGBAHalf格式紋理。假設一個角色有N個骨骼,每個骨骼有四個像素(一個矩陣);我們生成一個動畫作為M個關鍵幀。因此,一個動畫的成本為N * 4 * M * 2 = 8NM字節。如果一個角色具有50條骨骼,並且我們生成30個關鍵幀,則一個動畫具有50 * 4 * 30 = 6000像素。因此,一個1024 * 1024的紋理最多可以存儲174個動畫。
結論
我們發現,如果您有很多SkinnedMeshRenderers,則動畫實例化可以顯着降低CPU成本。適用於僵屍等類似敵人的人群。
我們希望這個實驗性項目能夠提供一些見識,可以洞察您自己項目的性能挑戰,並使您能夠構建更精細的場景。當然,未來的工作有很多途徑,例如對過渡,動畫層等的支持。
請在Github上檢查代碼,並將您有的任何評論/問題直接發布到項目中!
其他相關:
國內的一份教程:https://www.cnblogs.com/murongxiaopifu/p/7250772.html,寫於2017。
但是這個作者在2020年7月,還給他翻譯成了英文 https://medium.com/chenjd-xyz/how-to-render-10-000-animated-characters-with-20-draw-calls-in-unity-e30a3036349a