1. 程式人生 > >骨骼動畫程式原理介紹

骨骼動畫程式原理介紹

最近有人問我怎樣實現骨骼動畫,於是我就想起了我以前寫的這篇文章,貼上來給大家看看。

一、文章編寫目的

寫這篇文章,是給程式設計師看的。目的在於給程式設計師介紹骨骼動畫的原理、資料結構和程式實現的粗略方法。

骨骼動畫的應用面很多,主要用在3D角色動畫,不過現在也很多人用於2D動畫。下面的內容不會直接的把程式列出,只會闡述原理,關鍵的步驟是使用矩陣做座標系變換。原理明白之後,不管2D或者3D應該都能自己編寫。

二、什麼是骨骼動畫

傳統的動畫,一般是對一個物體物件進行位移、旋轉、縮放、變形,然後把關鍵幀的資訊記錄下來,在播放的時候按照關鍵幀時間對物體物件進行位移、旋轉、縮放、變形,並在關鍵幀與關鍵幀之間做插值運算。

骨骼動畫的特點是,需要做動畫的物體物件本身不記錄位移、旋轉、縮放、變形資訊,而是通過了第三方的“骨骼”物體記錄動畫資訊,然後物體物件本身只記錄受到骨骼物體影響的權重。在播放的時候,通過骨骼物體的關鍵幀和物體物件記錄的權重,讓動畫重現。

三、骨骼動畫的好處

相對於傳統動畫,骨骼動畫似乎多了2個記錄:骨骼物體和權重。按道理來說,這種骨骼動畫的資源容量和執行效率應該不如傳統的動畫,那為什麼還要做骨骼動畫呢?

好處有以下幾點:

1、骨骼動畫是影響到頂點級別的動畫,而且可以多根骨骼根據權重影響同一個頂點,不論是2D或者3D使用,都可以讓動畫做得更豐富。

2、做到了物件和動畫分離,我們只需要記錄了物體對於骨骼的蒙皮權重,就可以單獨的去製作骨骼的動畫,在保證蒙皮資訊和骨骼資訊一致的情況下,還可以多個物體之間共享動畫。

3、相對於2D的逐幀動畫大大的節省資源容量。

4、3D角色動畫離不開骨骼動畫。

四、美術層面理解骨骼動畫

由於骨骼動畫一般是由美術人員,更準確的是由動畫師來製作的,所以相對於程式設計師來說,美術人員接觸的機會比較多。這篇文章是給程式設計師看的,不過在從資料級別操作骨骼動畫之前,最好還是先了解一下美術層面製作骨骼動畫的方法。至於2D還是3D的情況下做骨骼動畫,其實原理是一樣的,只是2D時少了幾個點,然後一個軸固定值了而已。

這裡以3DsMax來舉例子。需要製作一個骨骼動畫,需要這幾樣東西:

1、網格物體

2、骨骼物體

3、蒙皮修改器

4、關鍵幀

網格物體:

需要做動畫的物件物體,如這個例子裡面我們需要讓這個長方體翹起來

這裡寫圖片描述

骨骼物體:

嚴格的骨骼物件物體,3DsMax有2種,一種是Bone,一種是biped。其實所有的物體都可以作為骨骼,包括了多面體網格物體、輔助物體等,都可以作為骨骼使用。不過為了便於一般操作習慣和匯出資料方便,我們都只用Bone和Biped這兩種。

蒙皮修改器:

3DsMax有2種蒙皮修改器,Skin和Physique。作用都一樣,都是給網格物體的頂點賦予權重。也沒有誰好誰壞之說,看個人使用習慣。不過由於大部分的模型匯出外掛都不支援Physique蒙皮修改器,所以一般來說,都是使用Skin。

蒙皮修改器可以使用範圍框、筆刷或者逐個點來調節權重,不過本質都是一樣的,最後得到的是幾根骨骼相對於某個頂點的影響權重。

這裡寫圖片描述

這裡寫圖片描述

上圖中的頂點受到骨骼bone01的0.25的影響,受到bone02的0.75的影響。這種情況下,該頂點雖然同時受到2根骨骼的影響,但bone02對它的影響會比較多一點。

正常的情況下,一個頂點受到多根骨骼影響的權重加起來應該等於1。

關鍵幀:

關鍵幀資訊包括了

l 時間

l 位移旋轉縮放的值

l 過渡插值的方式

這裡寫圖片描述

這裡寫圖片描述

注意的是,軟體操作上打了關鍵幀的物體才會動,不打關鍵幀的物體不會動或者跟隨著父級物體動。所以很多動畫師對一些由於不需要動的物體不打關鍵幀的。但由於每種動畫匯出外掛的處理不一樣,有些外掛的編寫作者也是忽略了這種情況,而導致了在動畫解析的過程中缺少了部分的資料,導致動畫播放不正常。

五、程式層面理解骨骼動畫

相對於美術層面,程式方面需要以下的幾個資料:

1、頂點索引資料

相對於美術只用關係網格物體的原始的位移旋轉縮放,程式關心的是網格物體的每一個頂點的位置座標,還有頂點之間組成網格的索引順序。所謂的原始,指的是物體在沒有進行蒙皮之前的位置。

如果是做2D,那麼資料就會變成每個面片的四個頂點的座標,而索引就要自己用演算法去生成。當然索引只是在3d渲染時才用到,如果是純2d,你可能只關心四個頂點的位置,然後繪製出面片。

由於之後一直都需要針對每一個頂點去儲存和提取資料,所以在一開始的時候,就要把這些頂點的順序固定下來。

2、骨骼物體資料

骨骼物體我們不會關心他是什麼型別,體積有多大,或者由多少個頂點組成。我們只關心它的位移旋轉縮放的原始座標,還有父子連結的關係。這個原始座標需要和網格物體的原始狀態保持一致,即沒有蒙皮和做動畫之前,骨骼的位置。由於骨骼通常是很多根的,所以在記錄的開始,也必須先給骨骼都排序。

對於2D來說,骨骼有可能並沒有實質性的形象,你可以用一個矩形或者一個線條做代表,最後需要匯出的只是該物件的座標,還有父子連結的關係。
這裡寫圖片描述

父子連結關係:圖中有2根骨骼。其中父物體發生位移旋轉,子物體會跟隨父物體做位移旋轉。但子物體做位移旋轉的時候,父物體是不會受到影響的。

這裡會涉及到2個座標系的概念,一個是世界座標,一個是區域性座標。

如果一個物體沒有父物體,也就是說它的父物體就是“世界”,那麼它的當前座標就是世界座標;

如果一個物體有父物體,那麼它除了有世界座標以外,還有一個相對於父物體的相對座標。

例如:

父物體的世界座標是(1,0,0),子物體的世界座標是(0,1,0),那麼子物體相對於父物體的區域性座標就是(-1,1,0)

3、每一個點的蒙皮權重

之前我們記錄頂點資訊和骨骼資訊時,都是已經排好序的,所以我們在記錄蒙皮權重的時候,就可以只是記錄各自的序號,還有對應的權重。一個頂點原則上是可以受到無限多根骨骼的影響,不過在實際的應用中,為了不讓計算過於複雜,一般一個頂點最多隻會受到4根骨骼的影響。

4、關鍵幀資料

根據記錄骨骼座標的座標系不同,可以選擇記錄每一根骨骼的絕對座標,也可以記錄有動畫的骨骼的相對座標。

根據是否計算插值,可以選擇記錄每一幀的資訊,也可以選擇只記錄關鍵幀的資訊,還有關鍵幀的插值方式。

之前我們介紹了絕對座標和相對座標,如果是記錄世界座標,那是最準確的,但由於每一根骨骼的每一幀都需要去記錄,不管它是否做過動畫,這樣動畫的資料量會比較大。如果記錄的是相對座標,那麼如果在子物體沒有做動畫的情況下,它就不需要記錄,而是通過父物體的座標來計算出子物體的世界座標。這樣做可以讓匯出的資料容量很小。不過這會增加解析時候的運算量。使用哪一種方式記錄關鍵幀,需要根據自己的專案實際情況來決定。

五、骨骼動畫的計算原理

1、4*4矩陣的概念

對於骨骼關鍵幀的記錄,我們需要記錄位移(xyz)、旋轉(通常用四元數xyzw)、縮放(xyz)這麼多個數據。我們計算的時候也可以逐個點去分別計算他們的位移旋轉縮放來做動畫。不過骨骼動畫一般會涉及到座標系的轉換,所以我們通常會都用矩陣來做運算。

一般的矩陣運算都是3*3矩陣,但在做3D方面的運算時,通常會把矩陣擴充套件為4*4矩陣。具體的做法請參考矩陣的基礎知識。大概過程是,先通過旋轉和縮放算出3*3矩陣,然後再增多一個維度填上位移座標。

根據矩陣左乘和右乘的不同,你的矩陣可能不一樣,下面的公式是以左乘為基礎的。

這裡寫圖片描述
這裡寫圖片描述

不同的引擎不同的作者,可能使用不同的矩陣相乘規則,由於矩陣相乘是不滿足交換律的,也就是AB!=BA,所以一開始必須先搞清楚當前的引擎矩陣究竟是左乘還是右乘的,還有座標系是用左手座標系,還是右手座標系。形式轉換經常是錯誤的根源。

經過位移旋轉縮放的計算,或者直接從3D軟體裡面就匯出矩陣,我們得到的4*4矩陣應該是這樣的

左乘:

r11 r12 r13 0

r21 r22 r23 0

r31 r32 r33 0

tx ty tz 1

右乘:

r11 r21 r31 tx

r12 r22 r32 ty

r13 r23 r33 tz

0 0 0 1

其中r3*3是旋轉縮放計算的矩陣,tx、ty、tz是位移

2、座標系轉換概念

之前說過,骨骼有世界座標和區域性座標的分部。其實所有東西都有絕對座標和相對座標的概念。

拿頂點和骨骼來說,頂點本身有世界座標,也有相對於骨骼的相對座標。

關於絕對座標系和相對座標系的轉換,請參考3D圖形數學裡面的座標系轉換。我只說簡單的原理。

假設有:物體A和物體B,A的世界座標是Va世界,B的世界座標是Vb世界

現在我們需要把A的座標轉換成相對於B的相對座標,記作Vab。

那麼我們需要構造一個矩陣M世界-b,

Vab = Va世界*M世界-b。

其中M世界-b是根據Vb世界求出來的,一般是Vb世界的逆矩陣。

反過來,如果制定了A在B的相對座標,也知道了B的實際座標,我們需要求回A的世界座標。那麼應該是

Va世界= Vab*Mb-世界。

其中Mb-世界矩陣就是M世界-b的逆矩陣。乘以逆矩陣的幾何含義,就是把之前做過的變換取消掉。

一定要先把這個矩陣轉換座標系的概念搞清楚,這其實就是整個骨骼動畫的原理關鍵。其中的A就是物體的頂點,B就是骨骼。

3、骨骼動畫原理

前面廢話了這麼多,終於到核心部分了。

我們先從最簡單的情況考慮。

假設我們的模型只有一個頂點,骨骼也只有一根,這個頂點完全受這根骨骼的影響。
這裡寫圖片描述

紅色點是頂點,藍色的線是骨骼,這裡把骨骼畫成線只是為了便於敘述,其實骨骼是沒有長度概念的。
這裡寫圖片描述

我們旋轉了骨骼,由於頂點完全受骨骼影響,所以頂點的座標也發生了變化。

那麼我們怎樣求出頂點在動畫之後的座標呢?

通過觀察,頂點在動畫前和後後,雖然實際座標發生了變化,但是相對於骨骼的相對座標是沒有發生變化的。只要知道了相對座標,再通過動畫後骨骼的座標,就能通過轉換座標系,得到動畫後頂點的世界座標了。

那麼,我們可以這麼做:

設:

頂點的動畫前世界座標為:V世界0

頂點在動畫後世界座標為:V世界1

骨骼的動畫前世界座標為:B世界0

骨骼在動畫後世界座標為:B世界1

然後通過B世界0可以計算出從世界座標轉換到骨骼相對座標的:M0世界-b

通過B世界1可以計算出從骨骼相對座標轉換到世界座標的:M1b-世界

然後,我們要求出V世界1

首先求出頂點相對於骨骼的相對座標Vb:

Vb = V世界0*M世界-b

然後當運動之後,我們就可以求得

V世界1=Vb*M1b-世界

那麼整個過程就是

V世界1 = V世界0*M世界-b*M1b-世界

看似過程有點複雜,其實Vb是固定的,我們甚至可以在匯出資料的時候就先算好了。到了真正需要做動畫的時候,我們只需要再知道一個B世界1,就足夠我們算出當前的頂點應該在的位置了。

我們再加大複雜程度:

假設頂點還是一個,但骨骼變成了2根,頂點受a骨骼0.75權重的影響,受b骨骼0.25的影響
這裡寫圖片描述
這裡寫圖片描述

我們需要做的和前面一樣,雖然骨骼變成兩根了,但我們可以先忽略了權重,分別求出V世界1a和V世界1b。

之後的事情就很簡單了:

V世界1 = V世界1a*0.75+V世界1b*0.25。

如此類推,把權重標記成W,就變成了

V世界1 = V世界1a*Wa+V世界1b*Wb+V世界1c*Wc+V世界1c*Wc……

4、骨骼動畫的整個工作流程:

1.匯出原始資料,包括頂點原始位置、骨骼原始位置、蒙皮權重和骨骼關鍵幀。

2.解析資料,為每一個頂點記錄下相對於影響它的骨骼的相對座標,和該骨骼對它的影響。然後把骨骼關鍵幀解析成每一幀的4*4矩陣,並按照時間儲存起來。

3.骨骼動畫開始播放,先獲得該時間點的所有的骨骼的矩陣,用快取器存起來已備接下來所有的點都能訪問。然後每個頂點根據第2步解析的影響它的骨骼的編號獲取相應骨骼的座標,配合權重計算出該頂點真實的世界座標。

4.把所有頂點按照第3步都計算完畢,然後渲染出來,整個角色的動畫就出現了。