網格模型動畫一般有兩種:一種是漸變動畫;另一種是骨骼動畫,這種動畫包含在網格模型之中,通過網格模型不同部分之間的相對運動來實現動畫。
骨骼動畫基本原理
骨骼動畫是目前最流行也最復雜的角色動畫,它包含以下幾個重要元素:骨骼、動畫。骨骼動畫思想的起源很簡單,自然界中的大多數動物都擁有一套骨骼,身體的皮毛血肉都依附于骨骼,當骨骼開始運動的時候,依附于對應骨骼的皮毛血肉都隨骨骼一起運動。在三維圖形編程領域,角色的軀體是由網格模型來表示的,網格模型通常由大量三角形圖元組成,而三角形又是由頂點組成的。為了模仿現實世界中角色自身的動作,就需要為角色網格模型添加一套骨骼,同時需要確定哪些頂點依附于哪塊骨骼,這樣當骨骼運動時就能牽引依附骨骼的頂點一起運動,這就是骨骼動畫的基本原理。
骨骼動畫模型的骨骼是以樹狀層次結構組織起來的,整個骨骼結構中有一塊根骨骼,其他的骨骼都直接或間接連接到根骨骼上,形成角色模型的整個骨骼框架。
一般每塊骨骼都帶有兩個矩陣,一個是初始變換矩陣(LocalTransformMatrix),表示骨骼的初始位置;另一個是組合變換矩陣(CombinedTransformMaitrx),用于對骨骼進行各種變換,從而實現角色動畫。在每次渲染角色模型前,需要更新整個骨骼層次結構,組合每個連續的變換,將上層骨骼的運動傳遞到下層骨骼,這個原理可表示為:
(子骨骼的)CombinedTransformMaitrx =
(子骨骼的)LocalTransformMaitrx x (父骨骼的)CombinedTransformMaitrx
骨骼的組合變換矩陣是隨動畫的播放不斷變化的,而它的初始變換矩陣一般是不改變的,正是所有骨骼的這些矩陣相互作用才牽引著頂點的變化,從而實現了骨骼動畫。因為一次變換只能將骨骼變換到一個特定位置,要形成連續的動畫就需要一幀一幀地連續改變骨骼的位置,每次改變骨骼的位置都需要一個骨骼變換矩陣,在網格模型中不可能保存任意時刻骨骼的變換矩陣,通常是保存關鍵時間點骨骼的變換矩陣(即關鍵楨),然后在播放角色動畫時,根據播放時間進行插值得到任意時刻骨骼的變換矩陣,從而形成連續的角色動畫。
骨骼動畫是通過骨骼變換矩陣實現的,在網格模型中保存的也是關鍵時間點骨骼的變換矩陣,因此插值就是針對這些關鍵時間點上的骨骼變換矩陣進行的。假設在s1時刻骨骼變換矩陣是mat1,在s2時刻骨骼變換矩陣是mat2,在s1和s2之間的任意時刻s,其骨骼變換矩陣mat為:
mat = (1-w) * mat1 + w * mat2
其中w是權值,通過這個權值來調節在s時刻骨骼變換矩陣中mat1和mat2所占的比重,對骨骼變換矩陣進行插值最簡單的方法是線性插值,這時w
= (s-s1) / (s2-s1)
骨骼動畫類的設計與實現
我們需要將骨骼動畫網格模型的相關操作封裝到一組類和結構中,這一組類和結構可以看成一套完整的骨骼動畫網格模型接口,它們之間的關系如下圖所示:

其中,D3DXFRAME和D3DXMESHCONTAINER是Direct3D提供的兩個結構,D3DXMESHCONTAINER結構用于保存模型的網格數據,D3DXFRAME用于保存模型的骨骼框架。結構D3DXMESHCONTAINER_DERIVED繼承自Direct3D提供的結構D3DXMESHCONTAINER,結構D3DXFRAME_DERIVED繼承自Direct3D提供的結構D3DXFRAME,分別進行了相應的擴充,使其能夠保存所需要的其他數據。
cAllocateHierarchy類負責從動畫網格模型文件加載各種數據,該類繼承自Direct3D中的ID3DXAllocateHierarchy接口。
cAnimMesh類是唯一對外開放的類,它通過cAllocateHierarchy類的對象從模型文件中加載所需的數據,并負責處理骨骼動畫信息以及網格模型的渲染。
繼承并擴展結構體D3DXFRAME
為了在渲染網格模型的同時播放包含在網格模型的動畫,需要處理兩個單獨的實體:骨骼結構(即框架結構)和網格模型。框架結構和網格模型的相關數據分別使用D3DXFRAME_DERIVED和D3DXMESHCONTAINER_DERIVED結構保存。需要指出的是.x文件中的一個網格模型可以由多個框架和多個網格組成,但具體到某一個框架時,它一般只有一個網格,當然它也可以有多個網格。
為了方便加載骨骼動畫網格模型,Direct3D提供了兩個重要的結構體:D3DXFRAME和D3DXMESHCONTAINER,其中D3DXFRAME用來加載框架,其定義如下:
Encapsulates a transform frame in a transformation
frame hierarchy.
typedef struct D3DXFRAME {
LPSTR Name;
D3DXMATRIX TransformationMatrix;
LPD3DXMESHCONTAINER pMeshContainer;
D3DXFRAME * pFrameSibling;
D3DXFRAME * pFrameFirstChild;
} D3DXFRAME, *LPD3DXFRAME;
Members
- Name
- Name of the frame.
- TransformationMatrix
- Transformation matrix.
- pMeshContainer
- Pointer to the mesh container.
- pFrameSibling
- Pointer to a sibling frame.
- pFrameFirstChild
- Pointer to a child frame.
Remarks
An application can derive from this structure to add
other data.
顯然在實現動畫網格模型的繪制前,不僅要得到每個框架的初始變換矩陣,同時還要得到從該框架的所有父節點到本級框架的組合變換矩陣,這是因為任何一個父框架的位置改變都會影響該框架自身位置的變化,所以在此將結構D3DXFRAME擴展為D3DXFRAME_DERIVED,在D3DXFRAME_DERIVED中添加一個成員變量CombinedTransformMaitrx,用TransformationMatrix記錄在任何動畫數據未加載前框架的初始變換矩陣,也就是該框架的初始位置,用CombinedTransformMaitrx來記錄從所有的父框架到該框架自身所積累起來的組合變換矩陣,這樣就將整個網格模型很方便地組織起來了。
結構D3DXFRAME_DERIVED的定義如下:
struct D3DXFRAME_DERIVED : public D3DXFRAME
{
D3DXMATRIX CombinedTransformMatrix;
};
繼承并擴展結構體D3DXMESHCONTAINER
結構體D3DXMESHCONTAINER用來加載每個具體網格模型的數據,其定義如下:
Encapsulates a mesh object in a transformation frame
hierarchy.
typedef struct D3DXMESHCONTAINER {
LPSTR Name;
D3DXMESHDATA MeshData;
LPD3DXMATERIAL pMaterials;
LPD3DXEFFECTINSTANCE pEffects;
DWORD NumMaterials;
DWORD * pAdjacency;
LPD3DXSKININFO pSkinInfo;
D3DXMESHCONTAINER * pNextMeshContainer;
} D3DXMESHCONTAINER, *LPD3DXMESHCONTAINER;
Members
- Name
- Mesh name.
- MeshData
- Type of data in the mesh.
- pMaterials
- Array of mesh materials.
- pEffects
- Pointer to a set of default effect parameters.
- NumMaterials
- Number of materials in the mesh.
- pAdjacency
- Pointer to an array of three DWORDs per triangle
of the mesh that contains adjacency information.
- pSkinInfo
- Pointer to the skin information interface.
- pNextMeshContainer
- Pointer to the next mesh container.
結構體D3DXMESHCONTAINER中沒有記錄網格模型的紋理信息,所以將該結構體擴展為D3DXMESHCONTAINER_DERIVED,定義如下:
struct D3DXMESHCONTAINER_DERIVED : public D3DXMESHCONTAINER
{
IDirect3DTexture9** ppTextures;
};
其中ppTextures用來存儲網格模型的紋理對象。