用Unity做游戲經常會用到3D角色,也就會用到骨骼動畫。骨骼動畫對性能的影響其實非常大的,在說這個問題之前,先來說說骨骼動畫的原理。
我之前做過多種骨骼動畫,包括寫過2D的骨骼動畫系統(類似於現在比較流行的龍骨系統),也在flash的stage3D里面寫過3D的骨骼模型動畫。骨骼動畫的原理實際上都是一樣的:
首先你需要有一個模型,2D或者3D的,這些模型是由頂點組成的,2d模型的頂點就是一個個四邊形的四個頂點,3D模型的頂點就是每個Mesh網格的三角面頂點。
然后,你需要搭建一套骨骼,這些骨骼是樹形結構的,也就是有父子連接關系的,父級骨骼在做運動的時候,子級骨骼是跟隨父級骨骼運動,在這個基礎上然后子級也可以自己運動而不影響父級骨骼。
接下來,你需要把模型的頂點和骨骼做一個對應關系,這就是所謂的蒙皮權重。蒙皮要做的事情,是指定某個頂點受到多少根骨骼的影響,然后在骨骼運動的時候,頂點根據權重的百分比來跟隨骨骼運動。比如一個頂點是受到了2跟骨骼的影響,第一根骨骼的權重是30%,第二根骨骼的權重是70%。在兩根骨骼同時移動的時候,第一根骨骼向左移動了10米,第二根骨骼向右移動了10米,假若向右是正方向,那么這個頂點實際移動的位置應該就是-10*0.3+10*0.7 = 4,也就是向右移動了4米。在實際的計算中,我們不會這么簡單的乘以百分比,是會用矩陣來運算,分別算出正常受到每一根骨骼矩陣影響之后該頂點的最終坐標,然后再乘以百分比相加。
最后再來說說骨骼父子關系。每一個子級的骨骼,需要先獲取到它的父級,通過矩陣來轉換局部坐標系,算出子級相對父級的局部位移旋轉縮放,再將坐標系轉換到世界坐標系,得到子級相對於父級的位移旋轉縮放在世界坐標的實際位置,得到最終在動畫中這根子骨骼的實際坐標。如果一個角色的骨骼數量越多,嵌套的父子關系越復雜,那么這個轉換坐標系計算的過程就越復雜,消耗的cpu運算就越多。
針對這個骨骼消耗的問題,歷代的游戲引擎都有自己的解決辦法。比如很早期的魔獸爭霸3的模型動畫,是使用了頂點動畫的方式。這個做法具體是這樣的,首先在3d軟件里面先做好模型的骨骼動畫,然后在導出的時候,逐幀或者間隔多少幀的計算每個頂點在當前幀的實際坐標,最后只導出頂點的每幀坐標信息而不導出骨骼信息。
這個做法的優點是在游戲運行的時候完全不需要計算骨骼的坐標關系了,直接把所有頂點的當前幀坐標讀取一遍,然后設置頂點坐標就解決了。所以在當年模型的頂點數很少(一個角色模型300個三角面左右),角色是固定形態(不需要局部換裝),幀率要求不高的情況下,這樣做極大的保證了游戲的運行效率。
但這個做法的缺點也是很明顯的。隨着角色模型面數的增大,幀率的提高,導出的動畫文件就增大很多倍,然后由於沒有導出骨骼信息,所以也不能在骨骼動畫播放的過程中修改某個骨骼的信息做到特殊的動畫效果(比如在揮拳的時候手可以根據目標的距離伸長之類),最后也是最嚴重的,失去了骨骼動畫一個基本的作用,就是動態蒙皮換裝,每套骨骼動畫信息只能針對一個模型的。
之后我在做stage3D的骨骼動畫的時候,參考了一部分這個做法,進行了一下改進,改為導入運行的時候,先計算出該骨骼動畫的每幀所有骨骼實際坐標,然后存起來。這樣就不需要每幀計算父子矩陣關系,只需要直接蒙皮計算。這樣做,保證了導出文件的容量(畢竟flash頁游還是很講究文件大小),也能動態的蒙皮換裝,因為固定的只是骨骼,和模型沒有關系。不過這樣做還是不能在播放過程改變骨骼信息做特殊效果。
說了這么多,估計對骨骼動畫的原理已經有了一定的了解了。那么接下來自然就想到了影響骨骼動畫播放的一些效率問題:
1、模型本身的頂點數
從蒙皮權重的角度可以看出,如果頂點越多,在播放時需要計算每個頂點最終坐標的次數就越多。
2、模型骨骼的數量和復雜程度
從骨骼父子關系的計算可以知道,骨骼越多,計算矩陣坐標系的次數就越多。
為了讓骨骼動畫流暢,一般來說,我們在游戲里面會規定角色模型的面數和骨骼數量。比如效果稍微好點的游戲,主角3000面左右、小怪1000面左右之類的。然后骨骼數人形角色一般在30根左右。
在Unity引擎里面,對於骨骼有一個優化的功能。下面舉一個實際例子說明:
我們導入一個怪物模型,然后在Hierarchy面板,可以看到這個模型上面的所有骨骼。
在project面板選擇剛才那個模型的原始fbx文件,可以在Inspector面板看到模型導入的設置參數,在Rig里面有一個Optimize Game Objects的選項,把它勾上。
再把模型拖到場景里面。現在可以看到,模型下面只顯示了mesh網格,那些骨骼結構都沒有了。這時候播放,可以看到角色的動畫是可以正常播放的。

這時候你會想到一個問題,如果我在某些骨骼上面加了一些控制的節點,比如我在手的骨骼上面綁了一個物體,用於武器綁定,那么現在看不到骨骼了,不就不能這樣綁定了?其實還是有辦法的,再回到fbx導入設置里面:
點右下角的加號,可以選擇一些你指定的骨骼排除在外,這些骨骼將不會被隱藏掉。
這時候再把fbx拖到場景里面,可以看到上面出現了我們剛才選擇的排除在外的骨骼。
這樣處理了之后,首先場景里面不需要生成這么多骨骼,場景物體數量就減少了很多了,然后Unity會幫你精簡骨骼計算的矩陣轉換(具體處理方式官方沒有說明,估計類似於我在Stage3D時的處理),這樣可以把骨骼動畫播放時的消耗降低。
