由淺到淺入門批量渲染(四)

zt枸杞憂天發表於2021-01-06
上回(由淺到淺入門批量渲染(三))簡單總結了一下例項化渲染,這次我們說說優化骨骼蒙皮動畫。

前言

試想一下,當我們在遊戲場景中放置大量(成百上千)帶有骨骼蒙皮動畫的單位時,會發現幀數已經開始下降,這是為什麼呢?

經過多年研究,我發現,造成這種情況的根本原因是:放的太多了。

然而,在開發某些型別的遊戲(如策略或即時戰略等)時,通常又需要儘可能的多放些小兵或者怪物,來烘托戰場氣氛。

需要呈現大量的角色,又需要保證效能,是一件挺麻煩的事情;如果你也在嘗試解決這個問題,並且暫時還沒有找到合適的方法,那接下來要講的內容可能會幫到你;因為我們將進入這一系列(從淺到淺)的下半部分:總結目前較為成熟的針對骨骼蒙皮動畫的優化方案。

骨骼蒙皮動畫的開銷

“欲練神功,必先自宮”是笑傲江湖中非常有名的一句臺詞,它也是神功《葵花寶典》和《辟邪劍法》武功祕籍上的第一句話。以前聽到它時只是感覺這就是邪魔外道武功的一個符號罷了;但現在想來,當時還是年輕了,沒看懂。它其實是一條學習任何技能、知識的訣竅,也是一把開啟成功之門的鑰匙。

由淺到淺入門批量渲染(四)

葵花寶典上的斑斑血跡記錄了多少悲傷故事

其實,這句話想傳達的真正意思是:做事情,要打好基礎。

所以,要優化骨骼蒙皮動畫,就要先簡單瞭解下它的效能瓶頸所在。

骨骼蒙皮動畫的流程

可以簡粗(簡單粗暴)的將骨骼蒙皮動畫的工作流程分為以下幾個階段:

播放動畫階段

動畫控制器會根據關鍵幀資訊等,調整骨骼的空間屬性(旋轉、縮放、平移)。

計算骨骼矩陣階段

從根骨骼開始,根據層級關係,逐一計算出每一根骨骼的轉換矩陣。

這個矩陣連線的是這根骨骼的本地座標系和角色座標系(通常會是角色腳下);也就是說通過它可以在某一幀動畫結束後,將(某一根)骨骼座標系下的座標或向量,轉換到角色座標系下。

蒙皮階段

更新網格上每個頂點的屬性。

由於動畫改變的是骨骼而不是頂點的空間屬性;而且網格中的頂點是相對於網格座標系下的,並非在角色座標系下。所以在這一階段,我們首先要依據建立骨骼蒙皮動畫時,被記錄下來的頂點和骨骼的關係,找到對應的骨骼(Unity中通過Mesh.boneWeights獲取,一個頂點最多可受到四根骨骼影響)。

其次,通過網格座標系到骨骼本地座標系的轉換矩陣(Unity中通過Mesh.bindposes獲取),來建立從網格座標系到骨骼座標系的橋樑;結合上階段得到的骨骼座標系到角色座標系的轉換矩陣,實現將動畫對骨骼的影響最終作用到頂點上,並將其更新到角色座標系下。

由淺到淺入門批量渲染(四)
頂點的屬性被動畫控制器“間接”更新

渲染階段

當頂點變換到角色座標系下後,就可以進行渲染了。這裡與一次普通的渲染沒什麼太大差別,唯一需要注意的是,Unity不會對蒙皮網格渲染器進行合批,所以每一個骨骼蒙皮動畫例項都至少需要一次DrawCall。

骨骼蒙皮動畫的開銷

可以將骨骼蒙皮動畫的主要開銷,也簡粗的分成以下幾個部分:

  • 更新動畫的開銷
  • 計算骨骼矩陣的開銷
  • 蒙皮開銷
  • 渲染開銷

這裡我建立了一個簡單的場景,來簡單測試下這些開銷。

使用Unity 2018.4.14f1版本建立一個測試場景,場景中包含500個使用相同模型的骨骼蒙皮動畫角色,並迴圈播放空閒、移動、攻擊動畫;測試機是華為P20(Geekbench5得分約為1400),通過Profiler檢視執行耗時。

由淺到淺入門批量渲染(四)
簡單的步兵模型

由淺到淺入門批量渲染(四)
場景執行後

我們把與骨骼蒙皮動畫有關的主執行緒運算開銷整理出來,看一下在骨骼蒙皮動畫工作時哪些計算耗時最多。

由淺到淺入門批量渲染(四)
與骨骼蒙皮動畫有關的主執行緒耗時佔比

可以發現,動畫更新的耗時佔比最高,其次是蒙皮網格的更新(計算矩陣、蒙皮等),最後是渲染。

需要指出的是,我使用的Unity(版本2018.4.14f1)將動畫更新和蒙皮放到了工作執行緒中;所以像蒙皮這種“逐頂點、理論上應該開銷很大的操作”帶來的耗時增加,並沒有體現在主執行緒中;而且我在打包時也沒有勾選多執行緒渲染(Multi-threadedRenderer),所以渲染指令的呼叫也都發生在主執行緒。

由淺到淺入門批量渲染(四)
動畫更新被放在工作執行緒中執行

由淺到淺入門批量渲染(四)
蒙皮被放在工作執行緒中執行

由淺到淺入門批量渲染(四)
渲染指令在主執行緒中呼叫

常見優化方式

Unity下可以通過以下兩種方式快速優化骨骼蒙皮動畫:

  • 在匯入模型時進行的優化
  • 在打包設定中開啟GPU蒙皮

這兩者的優化效果怎麼樣呢。

匯入模型時的優化

由淺到淺入門批量渲染(四)

勾選模型匯入設定進行優化

在相同的測試環境下,再次進行測試後可以發現,這種方法確實可以產生一定效果。

其原因我認為主要有以下兩點:

不再為骨骼建立不必要的遊戲物件

對匯入模型進行優化後,Unity將不會為骨骼建立實際的遊戲物件了(我們也可以暴露出一些骨骼作為掛點)。

由淺到淺入門批量渲染(四)

由淺到淺入門批量渲染(四)
這些消失的遊戲物件一定程度上也減少了CPU的效能開銷

計算矩陣被移到工作執行緒

除此之外,Unity還會將計算骨骼矩陣的操作放到工作執行緒中,來減少主執行緒耗時。

由淺到淺入門批量渲染(四)
計算矩陣在工作執行緒中進行

由淺到淺入門批量渲染(四)
主執行緒耗時不再包含計算矩陣

GPU蒙皮

開啟GPU蒙皮,Unity會通過ComputeShader的方式,使用GPU進行蒙皮。

由淺到淺入門批量渲染(四)
勾選設定開啟GPU蒙皮

GPU上有大量的ALU(算數邏輯單元),可以並行大量的數值計算,效率較高,應該很適合這種針對頂點屬性的數值計算。

但是實際情況是:在移動裝置上使用GPU蒙皮反而會使主執行緒的耗時增加。

由淺到淺入門批量渲染(四)
開啟GPU蒙皮後主執行緒耗時增加

通過Profiler可以發現,CPU把更多的時間放在了執行ComputeShader上,由於骨骼動畫的例項很多(500個),所以這個呼叫時間本身成為了效能熱點。

由淺到淺入門批量渲染(四)
開啟GPU後,蒙皮網格更新中增加了GPU蒙皮

由淺到淺入門批量渲染(四)
執行GPU蒙皮耗時較高

所以,以目前的情況來看,這種在移動裝置上使用GPU蒙皮的方式,似乎不適合處理大量骨骼蒙皮動畫例項(也許是我使用的方式存在問題)。

寫在最後

我們簡單總結了骨骼蒙皮動畫的工作方式,分析了主要的效能開銷及耗時,以及比較了Unity中常用的兩種優化方式。

但在面對數以千計的角色表現需求上時,無論使用何種Unity自帶的優化,都顯得有些力不從心;所以下次的更新,將介紹兩種目前較為有效、成熟的“奇技淫巧”,來(一定程度上)解決這個問題。

相關閱讀:

由淺到淺入門批量渲染(三)
由淺到淺入門批量渲染(二)
由淺到淺入門批量渲染(一)


來源:騰訊遊戲學院
原文:https://gameinstitute.qq.com/community/detail/133615

相關文章