一,3D模型動畫原理和分類
關節動畫(Skeletal Animation):
原理: 角色模型是層次模型,要活的某一個部分相對於世界座標的位置,必須從根節點開始遍歷該節點所有的祖先節點累計計算模型的世界變換。模型不是一個整體的Mesh,而是分成很多部分Mesh,通過一個父子層次結構將分散的Mesh組織在一起,父Mesh帶動其下 Mesh 運動。動畫幀中設置各個子Mesh相對其父Mesh變換(旋轉,移動,縮放),通過子到父,一級級的矩陣累乘,得到該子Mesh 在整個動畫模型所在的座標空間的變換。
優點:只存儲節點間的相對變化,因此動畫文件佔用的空間很小。其次可以實現更多複雜的動畫效果
缺點:不同部分的結合處往往會有明顯的接縫。
漸變動畫:
插值兩個關鍵幀模型的頂點。
骨骼蒙皮動畫 Skinned Mesh
在骨骼控制下,通過頂點混合動態計算蒙皮網格的頂點,而骨骼運動相對於其父骨骼,並由動畫關鍵幀數據驅動。
一個骨骼動畫通常包括 骨骼層次結構數據,Mesh 數據,Mesh skin info 和骨骼動畫(關鍵幀)數據。
二 Skinned Mesh 原理和結構分析
關節動畫上面說了,是使用多個分散的Mesh,而Skinned Mesh 則是一個整體,僅有一個Mesh。
Skinned Mesh技術關鍵點在於蒙皮,即將Mesh中的頂點綁定在骨骼上,每個頂點可以被多個骨骼控制,通過權重來影響
頂點變化,從而消除了裂縫。
1 骨骼及骨骼層次結構
模型通過骨骼改變位置和朝向,而不是Mesh。 骨骼是一個座標空間。而骨骼層次就是嵌套的座標空間。
2 蒙皮信息和蒙皮過程
2.1 skin info
是 Mesh 頂點以及骨骼關係的信息。頂點Skin info 包含影響頂點的骨骼數目,指向這些骨骼的指針,以及不同骨骼
作用於該頂點的權重(Skin Weight)
2.2 座標空間轉換
骨骼中決定模型頂點最終的世界座標。骨骼受一塊骨骼作用時的座標變換過程
mesh vertex : Mesh SPace---[BoneOffsetMatrix]--->Bone space---[BoneCombinedTransformMatrix]---> World Space
Mesh space 是建模時使用的空間,比如3d Max建模可以將某位置作爲原點。關於這點,在下面Vertex Blending有詳解
骨骼有本地矩陣變換 transform L 以及 combined transform C。 本地的 L 負責 自身的變換以及迎合父骨骼變化的變換
combined transform C 則是把骨骼放到世界座標(World space)
如何得到 combined transform C ? 也很簡單,看下面
如上,最後末尾的骨骼變換,只需把自己的本地 Local Transform 累乘父骨骼(前面幾個骨骼變換)即可,公式表示爲 Ci=LiPi,
2.3 頂點混合(Vertex blending)
Vworld = Vmesh * BoneOffsetMatrix1 * CombindMatrix1 * Weight1
+ Vmesh * BoneOffsetMatrix2 * Weight2
+ …
+Vmesh * BoneOffsetMatrixN * WeightN
其中關於 BoneOffsetMatrix 作個解釋:因爲我們在頂點混合的時候是直接處理Mesh vertex,上面我們是通過
BoneCombinedTranformMatrix 把骨骼座標轉換爲世界座標(人物的整體位置),矛盾在於Mesh Vertex 不在骨骼空間
所以需要BoneOffsetMatrix 作箇中間轉換到Bone Space ,公式表達如下
Fi = Mi Ci,
D3DX 實現部分重要部分說明:
1. 骨骼層次
typedef struct _D3DXFRAME {
LPSTR Name;
D3DXMATRIX TransformationMatrix;
LPD3DXMESHCONTATINER pMeshContainer;
struct _D3DXFRAME * pFrameSibling;
struct D3DXFRAME *pFrameFirstChild;
} D3DXFRAME, *LPD3DXFRAME;
BONE 對應D3DXFRAME,TransformationMatrix 表示 本地轉換矩陣(Local transformation matrix), pFrameFirstChild 指向第一個子幀,pFrameSibling 指向兄弟幀
2. 創建 Combined Transform
void CombineTrasnforms(FrameEx * frame,
D3DXMETRIX &P)
{
D3DXMATRIX & L = frame->TransformationMatrix;
D3DXMATRIX& C = frame->combinedTranform;
C = L * P;
FrameEx* sibling = (FrameEx *)frame -> pFrameSibling;
FrameEx* firstChild = (FrameEx*)frame->pFrameFirstChild;
if(sibling)
combineTransforms(sibling, P);
if(firstChild)
combineTransforms(firstChild, C);
}
3. VertexBlend
extern float4x4 WorldViewProj;
extern float4x4 FinalTransforms[35];
extern texture Tex;
extern int NumVertInfluences = 2;
sampler S0 = sampler_state
{
Texture = <Tex>;
MinFilter = LINEAR;
MagFilter = LINEAR;
MipFilter = LINEAR;
};
struct VS_OUTPUT
{
float4 pos : POSITION0;
float2 texCoord : TEXCOORD;
float4 diffuse : COLOR0;
};
VS_OUTPUT VertexBlend(float4 pos : POSITION0,
float2 texCoord : TexCOORD0,
float4 weights : BLENDWEIGHT0,
int4 boneIndies : BLENDINDICES0
)
{
VS_OUTPUT output = (VS_OUTPUT) 0;
float4 p= float4(0.0f, 0.0f, 0.0f, 1.0f);
float lastWeight = 0.0f;
int n= NumVertInfluences - 1;
for(int i=0; i<n; ++i)
{
lastWeight += weights[i];
p += weight[i] * mul(pos, FinalTransforms[boneIndices[i]]);
}
lastWeight = 1.0f - lastWeight;
p += lastWeight * mul(pos, FinalTransforms[boneIndices[n]]);
p.w =1.0f;
output.pos = mul(p, WorldViewProj);
output.texCoord = texCoord;
output.diffuse = float4(1.0f, 1.0f, 1.0f, 1.0f);
return output;
}
techique VertexBlendingTech
{
pass P0
{
vertexShader = compile VS_2_0 VertexBlend();
Sampler[0] =<S0>;
Lighting = false;
}
}
HRESULT ID3DXAnimationController::SetTrackEnable(
UNIT Track,
BOOL Enable // true to enable and false to disable
)
ID3DXAnimationController::setTrackSpeed(
UINT Track;
FLOAT Speed;
)
另外,需要設置每個動畫track的權重ID3DXAnimationController::SetTrackWeight(
UINT Track,
FLOAT Weight
);
設置動畫軌跡的優先級,不同優先級的動畫分開混合,最後再做混合setTrackPriority(
UNIT Track,
D3DXPRORITY_TYPE Priority
)
參考:skinned mesh and character animation with DiretX 9
blog.csdn.net/n5