Motion Analysis HTR (分等級模型運動分析)

<script src="http://widgets.amung.us/classic.js" type="text/javascript"></script>

Motion Analysis HTR

2008-11-17  

  

     HTR(Hierarchical Translation-Rotation)是一種分級的模型動畫技術,它是從一種.htr文件中讀取模型動畫信息,然後採用分級模型動畫的方法來處理複雜的模型動畫。使用3DMAX可以導出這種文件,文件一般由HeaderSegment names&hierarchyBase positionMotion sections這幾個部分組成,不同的部份分別記錄不同的信息,Header中一般記錄有文件類型,數據類型,關鍵幀數,分級的模型數等等,Segment names&hierarchy中記錄了每級模型的名字和它的父節點,Base position中記錄了本地變換信息(注意:記錄的不是座標,而是座標變換)Motion sections中記錄了所有節點在每個關鍵幀的座標變換。

 

 

 

 

一個htr文件的Header部份

 

 

    在HTR技術中,我們一般使用的變換都是剛體變換,顧名思義,剛體變換就是指在變換的過程中,模型的形狀不發生改變。剛體變換一般指位移變換(Translation),旋轉變換(Rotation)和等比的放縮變換(Scaling)。

 

   旋轉變換(Rotation

 

 

 

 

 

 

 

在上面的座標中,向量V0逆時針方向旋轉 到達V1的位置,現在要用矩陣的乘法來表示這個向量的旋轉。假設旋轉半徑是r,那麼旋轉後:

 

x_{1}=rcos(/theta +/phi )

y_{1}=rsin(/theta +/phi )

 

    上式根據三角公式展開:

x_{1}=rcos/theta cos/phi -rsin/theta sin/phi

x_{1}=rsin/theta cos/phi +rcos/theta sin/phi

可以得到:

 

x_{1}=x_{0}cos/theta -y_{0}sin/theta

 

y_{1}=x_{0}sin/theta -y_{0}cos/theta

寫成矩陣乘法形式,得到:

 

/begin{bmatrix} x_{1}// y_{1} /end{bmatrix} = /begin{bmatrix} cos/theta & -sin/theta// sin/theta&cos/theta /end{bmatrix} /begin{bmatrix} x_{0}//y_{0} /end{bmatrix}

 

這樣就得到了旋轉矩陣 /begin{bmatrix} cos/theta & -sin/theta// sin/theta&cos/theta /end{bmatrix} ,實際應用的時候,一般要擴展到三維空間,下面分別是繞XYZ軸旋轉的

 

旋轉矩陣:

/begin{bmatrix} 1&0&0// cos/theta & -sin/theta &0// sin/theta & cos/theta &0 /end{bmatrix}           /begin{bmatrix} cos/theta &0& sin/theta // 0&1&0// -sin/theta & 0&/cos/theta /end{bmatrix}           /begin{bmatrix} cos/theta &-sin/theta& 0 // sin/theta & cos/theta&0// 0&0&1// /end{bmatrix}

 

在計算機處理信息的時候,都採用4X4的矩陣,這就是仿射變換,如下面的形式。

 

         

 

/begin{bmatrix} 1 &0 &0 &0 // 0&cos/theta &-sin/theta &0 // 0& sin/theta& cos/theta &0 // 0& 0 &0 &1 /end{bmatrix}           /begin{bmatrix} cos/theta& 0& sin/theta& 0// 0&1 &0 & 0// -sin/theta&0&cos/theta& 0// 0& 0& 0& 1 /end{bmatrix}           /begin{bmatrix} cos/theta& -sin/theta& 0& 0// sin/theta&cos/theta&0& 0// 0&0 &1 & 0// 0& 0& 0& 1 /end{bmatrix}

 

   放縮變換(Scaling

 

 

 

很明顯,可以用倍數來描述x0x1y0y1之間的關係

 

x1 = Sx x0

y1 = Sy y0

 

寫成矩陣的形式

/begin{bmatrix} x_{1}// y_{1} /end{bmatrix} =/begin{bmatrix} S_{x} &0 // 0&S_{y} /end{bmatrix} /begin{bmatrix} x_{0}// y_{0} /end{bmatrix}

 

 

 

 

 

這樣就得到了放縮矩陣,同樣的也可以擴展到三維空間和它的仿射變換

 

/begin{bmatrix} S_{x} &0 &0 // 0& S_{y}&0 // 0&0 & S_{z} /end{bmatrix}          /begin{bmatrix} S_{x} &0 &0 &0// 0& S_{y}&0 &0// 0&0 & S_{z}&0// 0&0&0&1 /end{bmatrix}

 

      位移變換(Translation

 

位移變換是最簡單的,但是也比較特殊,因爲找不到一個3X3的矩陣來描述一個三維空間的位移變換。所以對於位移,必須使用仿射變換才能把位移描述爲矩陣的乘法。也就是說,一個三維空間中的位移,我們用一個4X4的矩陣乘法來描述。也就是前面說的仿射變換。

 

       在上面的座標系中,點P0分別在XYZ軸方向上移動了 Tx,Ty Tz到達點P1,那麼P1可以表示爲:

 

/begin{bmatrix} x_{1}// y_{1}// z_{1} /end{bmatrix} = /begin{bmatrix} x_{0}// y_{0}// z_{0} /end{bmatrix} + /begin{bmatrix} T_{x}// T_{y}// T_{z} /end{bmatrix}

 

     因爲不能找到一個3X3的矩陣將位移描述爲矩陣的乘法形式,所以要使用仿射變換。它描述爲下面的形式:

 

/begin{bmatrix} x_{1}// y_{1}// z_{1} /end{bmatrix} = /begin{bmatrix} x_{0}// y_{0}// z_{0} /end{bmatrix} + /begin{bmatrix} T_{x}// T_{y}// T_{z} /end{bmatrix} = /begin{bmatrix} 1 & 0 & 0 & T_{x}// 0& 1 & 0 & T_{y}// 0 & 0& 1& T_{z}// 0&0 &0 & 1 /end{bmatrix} /begin{bmatrix} x_{0}// y_{0}// z_{0}// 1 /end{bmatrix}

 

 

現在爲止,所有的變換可以用一個變換矩陣來描述:

 

M= /begin{bmatrix} R & T// 0& 1 /end{bmatrix}

 

 

 

R表示旋轉矩陣,T表示位移矩陣。常常我們會用到該變換矩陣的逆矩陣,它表示爲:

 

M^{-1}= /begin{bmatrix} R^T & -R^TT// 0& 1 /end{bmatrix}

 

      四元數(Quaternion

 

現在已經有了旋轉矩陣了,但是它們都是圍繞座標軸旋轉,不能滿足實際應用,實際情況中,很多時候我們要用想使一個物體繞任意軸旋轉,下面是一個繞任意軸r旋轉/theta度的矩陣。

 

R= /begin{bmatrix} cos/theta+(1-cos/theta)r^{2}_{x} &(1-cos/theta)r_{x}r_{y}-r_{z}sin/theta &(1-cos/theta)r_{x}r_{z}+r_{y}sin/theta // (1-cos/theta)r_{x}r_{y}+r_{z}sin/theta &cos/theta+(1-cos/theta)r^{2}_{y}& (1-cos/theta)r_{y}r_{z}-r_{x}sin/theta // (1-cos/theta)r_{x}r_{z}-r_{y}sin/theta&(1-cos/theta)r_{y}r_{z}+r_{x}sin/theta &cos/theta+(1-cos/theta)r^{2}_{z} /end{bmatrix}

 

 

 

 

這個矩陣的計算相當複雜,且在實際的應用中,旋轉矩陣一直不停的和其他矩陣或點相乘,不能夠保證得到的矩陣永遠是正交的,所以我們利用四元數來解決這個問題。一個四元數表示如下:

 

q=(w,x,y,z)

 

 

 

 

那麼q = ( cos/theta,sin/theta(x,y,z))就表示一個旋轉變換,它表示繞軸xyz旋轉2/theta角度。現在變換矩陣可以寫爲

 

 

 

M= /begin{bmatrix} Q & T// 0& 1 /end{bmatrix}          M^{-1}= /begin{bmatrix} Q^{T} & -Q^{T}T// 0& 1 /end{bmatrix}

 

      Motion Analysis HTR

 

下面的模型展示了一個簡單的動作,一個機械臂的上升並且旋轉,如下圖

 

 

 

  

 

我們可以使用一連串的變換矩陣乘積來描述物體的運動。在該模型中,它的過程是先逆時針旋轉/theta_{1}角度,然後向X方向移動/L_{1}的距離,再逆時針旋轉/theta_{2}角度,然後再向X方向移動/L_{2}的距離。這個變換可以表示爲下面的矩陣乘法。

 

T=[R(/theta_{1})][T(L_{1})][R(/theta_{2})][T(L_{2})] 

 

T= /begin{bmatrix} cos/theta_{1} &-sin/theta_{1} &0 // sin/theta_{1} &cos/theta_{1} &0 // 0& 0&1 /end{bmatrix} /begin{bmatrix} 1 &0 &L_{1} // 0& 1 &0 // 0& 0 &1 /end{bmatrix} /begin{bmatrix} cos/theta_{2} &-sin/theta_{2} &0 // sin/theta_{2} &cos/theta_{2} &0 // 0& 0&1 /end{bmatrix} /begin{bmatrix} 1 &0 &L_{2} // 0& 1 &0 // 0& 0 &1 /end{bmatrix}

 

通過上面這個變換矩陣可以得到最後(x_{e},y_{e})的世界座標。

 

/begin{bmatrix} x_{e}// y_{e} /end{bmatrix} =/begin{bmatrix} L_{1}cos/theta+L_{2}cos(/theta_{1}+/theta_{2})// L_{1}sin/theta+L_{2}sin(/theta_{1}+/theta_{2}) /end{bmatrix}

 

 

 

 

     HTR的方法中,通常要輸入模型關鍵幀的變換,也就是說在這一關鍵幀中模型保持一個動作,我們要告訴它在下一幀中節點要怎樣變換。注意這裏指的是變換,而不是節點的座標,這樣做是因爲在處理問題時變得簡單一些,我們可以不必告訴它所在的世界座標的位置,這個位置完全可以通過公式來計算,而不是計算後再告訴節點。就好象比如一個人學習跳舞的時候,老師告訴她把手再擡高一點點,這裏的一點點不是一個座標,而是相對於它的父節點的變換。所以還要給每個節點指定它的父節點,每個節點都是以它的父節點爲基礎而變換的。其中有一個節點沒有父節點,因爲它要作爲所有節點的根節點,所有節點都是以根節點爲基礎的。可以把任意一個節點看成是一個根節點,但是隻能有一個根節點。比如一個人的模型動畫,我們用LowerTorso作爲根節點,像下面這個模型。

 

 

 

 

 

這裏使用了20個節點來描述這個模型,每個節點代表人的一個關節,LowerTorso這個節點是所有節點的根節點,初始化的時候,只有它的變換是根據世界座標變換來計算的,其它子節點都是根據根節點和本地座標變換來實現的。

     下面是htr文件中節點和父節點的信息,左邊是節點名,右邊是它的父節點。

 

 

 

         下面通過一個簡單的模型來說明如何計算每個節點的本地座標變換矩陣和世界座標變換矩陣。

 

 

上面是一個由2個正方形組成的物體分別在第一個關鍵幀和第二個關鍵幀的位置。我們把大的正方形作爲根節點,小正方形作爲子節點,小正方形的父節點就是大正方形。所以小正方形一系列的變換都是相對於它的父節點大正方形而變換的。

     在第一個關鍵幀裏,我們能看到如上圖的這個圖形,實際上已經給了物體一些列變換後,物體在世界座標中才能出如圖所示的樣子。下面是第一個關鍵中的變換信息。

 

 

     這裏QuadRoot是根節點,所以它的位置是根據世界座標變換(Gobal Translation)來計算的。 QuadChild節點的父節點是QuadRoot,所以他的本地座標變換(Local Translation)就是世界座標變換。

 

 

GM_{Root}=LM_{Root}= {/color{green} /begin{bmatrix} cos/theta &-sin/theta &0 // sin/theta& cos/theta &0 // 0&0 &1 /end{bmatrix} /begin{bmatrix} 1 & 0 &2 // 0 & 1 &2 // 0& 0 &1 /end{bmatrix} } =/begin{bmatrix} 1 & 0 &2 // 0 & 1 &2 // 0& 0 &1 /end{bmatrix}

 

很容易得到了根節點在世界座標中的位置[22],根節點世界變換和本地變換都是一致的。上式中有綠色的部分就根節點是世界變換矩陣,常常把它寫成一個矩陣/begin{bmatrix} cos/theta &-sin/theta &2 // sin/theta&cos/theta &2 // 0&0 & 1 /end{bmatrix}這種形式。

 

有了根節點的世界變換和本地變換矩陣,現在就可以通過QuadRoot來計算QuadChild的變換了。先計算QuadChild的本地變換矩陣。用GM_{Child}來表示子節點的世界變換矩陣, LM_{Child}表示子節點的本地變換矩陣,LM_{Root}表示父節點的本地變換矩陣,IM表示輸入的變換矩陣。

 

初始化的時候輸入的變換矩陣就是節點的本地變換矩陣,所以很容易得到子節點的本地變換矩陣

 

LM_{Child}= /begin{bmatrix} 1&0 &3 // 0&1&3// 0&0 & 1 /end{bmatrix}

接着再來計算它的世界變換矩陣。用下面這個等式。

 

 

 

GM_{Child}=GM_{Child's Parent} /times LM_{Child}

 

所以我們得到:

 

GM_{Child}= /begin{bmatrix} 1 &0 &2 // 0&1 &2 // 0&0 &1 /end{bmatrix} /begin{bmatrix} 1 &0 &3 // 0&1 &3 // 0&0 &1 /end{bmatrix} = /begin{bmatrix} 1 &0 &5 // 0&1 &5 // 0&0 &1 /end{bmatrix}

 

 

 

下面來看看第二個關鍵幀中的變換。

 

 

 

 

 

     在第二個關鍵幀中,變換信息如下:

 

 

     先計算根節點的變換矩陣。

 

GM_{Root}=ML_{Root}/times IM= /begin{bmatrix} 1 & 0 &2 // 0& 1 & 2// 0& 0 & 1 /end{bmatrix} /begin{bmatrix} cos45 & -sin45 &2 // sin45& cos45 & 3// 0& 0 & 1 /end{bmatrix} = /begin{bmatrix} cos45 & -sin45 &4 // sin45& cos45 & 5// 0& 0 & 1 /end{bmatrix}

 

 

 

 

     再通過QuadChild的父節點QuadRoot的本地變換矩陣乘以QuadChild的本地變換矩陣再乘以輸入的變換矩陣就可以得世界變換矩陣。

 

 

GM_{Child}=GM_{Child's Parent}/times LM_{Child}/times IM 

 

    

 

計算上面的等式可以得到

 

 

GM_{Child}= /begin{bmatrix} 1 & 0 & 5// 0 & 1 & 5// 0 &0 & 1 /end{bmatrix} /begin{bmatrix} cos45 & -sin45 & 3// sin45 & cos45 & 0// 0 &0 & 1 /end{bmatrix} = /begin{bmatrix} cos45 & -sin45 & 8// sin45 & cos45 & 5// 0 &0 & 1 /end{bmatrix}

 

以上的過程就是HTR技術中最核心的部分,通過每個節點的父節點,一直找到根節點,然後一級一級的計算出變換矩陣。這種數據結構可以用樹來描述,然後使用深度優先的方法遍歷樹的節點。下面是個人體模型樹形結構的例子。

 

 

 

 

      實際應用

 

    在實際的應用中,需要建立兩個最主要類,一個是Joint類,它描述了模型的節點。還有一個是Model類,它描述了整個模型。下面的類圖詳細的描述這兩個類。

 

 

 

 

 

 

Model類中,JointList是用來保存節點信息的vector類型的數組。 GTransf是一個把向量和四元數組成爲矩陣的類,frmMotion是自定義的結構體,用來保存htr文件中所有節點數據,它的結構如下。

 

 

  

 

     frame是用來計算幀數, m_NumSegmentsm_NumFramesm_DataFrameRate分別保存了htr文件中[Header]部分的節點數,幀數和幀率。而公有方法FKSolver()就是用來計算在每個關鍵幀裏,每個節點的世界座標變換。如果幀率是30,就要在每一秒內調用該方法30次,幀數的計數就用是frame這個參數,如果frame > m_NumFrames,那麼frame就重置爲0,這樣動畫就能再次重頭開始。下面是設置timer,使FKSolver方法可以在一秒鐘內連續調用30次。

 

下面是FKSolver方法

 

    

    對於Joint類,主要是獲取和設置每個節點的父節點的信息,並且設置初始狀態的世界變換矩陣和本地變換矩陣。下面是主要代碼部分。

 

由於不能發佈視頻,使用HTR技術完成的骨骼動畫請看這裏 http://kr.youtube.com/watch?v=uzmQFWkEYFU

*原創文章,轉載請註明出處*

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章