骨骼動畫

一,3D模型動畫原理和分類

關節動畫(Skeletal Animation):

原理:  角色模型是層次模型,要活的某一個部分相對於世界座標的位置,必須從根節點開始遍歷該節點所有的祖先節點累計計算模型的世界變換。模型不是一個整體的Mesh,而是分成很多部分Mesh,通過一個父子層次結構將分散的Mesh組織在一起,父Mesh帶動其下 Mesh 運動。動畫幀中設置各個子Mesh相對其父Mesh變換(旋轉,移動,縮放),通過子到父,一級級的矩陣累乘,得到該子Mesh 在整個動畫模型所在的座標空間的變換。

  優點:只存儲節點間的相對變化,因此動畫文件佔用的空間很小。其次可以實現更多複雜的動畫效果

  缺點:不同部分的結合處往往會有明顯的接縫。  

漸變動畫:

插值兩個關鍵幀模型的頂點。

image

骨骼蒙皮動畫 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 負責 自身的變換以及迎合父骨骼變化的變換

   image

  combined transform C 則是把骨骼放到世界座標(World space)

image

如何得到 combined transform C ? 也很簡單,看下面

image

  如上,最後末尾的骨骼變換,只需把自己的本地 Local Transform 累乘父骨骼(前面幾個骨骼變換)即可,公式表示爲 Ci=LiPi,

 

    2.3  頂點混合(Vertex blending)

     Vworld = Vmesh * BoneOffsetMatrix1 * CombindMatrix1 * Weight1 

                        + Vmesh * BoneOffsetMatrix2 * Weight2

                        + …

                        +Vmesh * BoneOffsetMatrixN * WeightN

 

image

  其中關於 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;
  }
}

4. 動畫混合
假設人物有跑動動畫,射擊動畫。那麼我們希望遊戲中人物可以邊跑動邊開槍射擊,這樣就需要動畫混合。動畫混合把現有兩組不同的動畫幀重新進行排序,混合
產生新的動畫。D3DX中,使用不同的 tracks 來附加各種動畫集。動畫控制器自動混合所有的使能的track來混合動畫。
HRESULT ID3DXAnimationController::SetTrackEnable(
        UNIT Track,
        BOOL Enable       // true to enable and false to disable
)
the animation controller automatically blends all the enabled track animations together
設置動畫播放速度
when enabling a new animation track we should also difine its playing speed. We can do that
with the folloing method:
ID3DXAnimationController::setTrackSpeed(
UINT Track;
FLOAT Speed;
)
另外,需要設置每個動畫track的權重
it would be advantageous if we could specify how much weight each track
contributes to eh final blended animaiton. using
ID3DXAnimationController::SetTrackWeight(
UINT Track,
FLOAT Weight
);
設置動畫軌跡的優先級,不同優先級的動畫分開混合,最後再做混合
During the blending, all high priotiry tracks will be blended separately and 
all low priority tracks will be blended separately. then the result of these two
sepatate blends will be blended together to produce the final blended animation
setTrackPriority(
UNIT Track,
D3DXPRORITY_TYPE Priority
)



參考:skinned mesh and character animation with DiretX 9

                blog.csdn.net/n5

發佈了95 篇原創文章 · 獲贊 14 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章