接上一篇的內容,上一篇,簡單的介紹了,骨骼動畫的原理,給出來一個
簡單的例程,這一例程將給展示一個最初級的人物動畫,具備多細節內容
以人走路為例子,當人走路的從一個站立開始,到邁出一步,這個過程是
一個連續的過程,在這個一個過程中,人身體的骨頭在位置在發生變化,
骨頭發生變化以后,人的皮膚,肌肉就隨着變化,上一個例程中我們計算
(OpenGL10-骨骼動畫原理篇(1))計算了根據骨頭的位置計算皮膚的位置
只是計算量一刻的動作,走路的過程是連續的,就意味着我們要記錄下來
骨頭在運動過程中所以位置變化數據,這樣才可以根據不同時刻的骨骼的
位置計算出來皮膚的位置。
現在問題出來了,如果美術做了一個動畫有5秒鍾,每一秒播放60幀來
計算,我們要記錄非常多的骨頭的信息,小下面這樣:
假設人有100個骨頭
Bone person[100]
一秒鍾60幀 × 5秒 × 100,這個就是我們要記錄的數據量,由此可見
數據量是非常大的,實際上大可不必這樣做,想一,是否可以記錄一個
關鍵幀的,其他的數據又關鍵幀來計算呢 ?假設我們記錄了10個關鍵點
其他的數據根據時間按照一定的插值算法進行插值,那么數據量就驟然
降低非常多呢。出於這樣的想法,我們增加了一個新的概念,關鍵幀。
骨骼動畫系統的流程如下:
下面我們使用程序的角度來描述下該問題:
1. 獲取到所有的骨骼數據(可使用矩陣存儲)
Bone bones[n];
2. 獲取到關鍵幀數據
Bone arKeyFrame[n][KeyNumber];
3 獲取到皮膚(頂點數據)
Vertex verts[vNumber];
4 通過插值計算出來新的骨骼位置
Bone timeBone[n];
5 根據計算出來骨骼來計算頂點數據
Vert temp[vNumber];
一個定點的聲明如下:
{
//! 顏色
float r, g, b, a;
//! 位置
float x, y, z;
//! 影響度
float weights[2];
//! 矩陣的索引
short matrixIndices[2];
//! 影響整個定點的骨頭個數
short numBones;
};
聲明一個類,保存骨頭的信息.類如下所示,該類保存動畫的所有骨格信息:
struct Vertex { //! 顏色 float r, g, b, a; //! 位置 float x, y, z; //! 影響度 float weights[2]; //! 矩陣的索引 short matrixIndices[2]; //! 影響整個定點的骨頭個數 short numBones; };
接下來,聲明一動畫類,動畫類中維護關鍵幀數據
class Frame { public: tmat4x4<float> _bone[2]; };
一個動畫類,用來保存所有的關鍵幀數據,提供計算骨頭插值算法,
並輸出一幀的骨骼數據,類如下所示。
class SkinAnimation { public: //! 根據給定的時間,輸出一幀骨骼的數據 void calcFrame(float t,Frame& frame) { frame._bone[0] = interpolate(_keyFrame[0]._bone[0],_keyFrame[1]._bone[0],t); frame._bone[1] = interpolate(_keyFrame[0]._bone[1],_keyFrame[1]._bone[1],t); } //! 該動畫有兩個關鍵幀 Frame _keyFrame[2]; };
調用方式如下:
/** * 產生時間 */ static float xxx = 0; /** * 根據關鍵幀計算出來新的骨骼位置 */ _skinAnima.calcFrame(xxx,frame); xxx += 0.01f;
然后我們將定點數據與計算出來的骨骼數據計算,得出最后的皮膚數據:
/** * 繪制表皮,保存臨時點數據 * 這里根據新的骨頭的(就是插值計算出來以后的骨頭來計算皮膚的位置了) */ Vertex calQuadVertices[12]; memcpy(calQuadVertices,g_quadVertices,sizeof(g_quadVertices)); for (int i = 0 ;i < 12 ; ++ i ) { tvec3<float> vec(0,0,0); tvec3<float> vecSrc(g_quadVertices[i].x,g_quadVertices[i].y,g_quadVertices[i].z); for (int x = 0 ; x < calQuadVertices[i].numBones ; ++ x) { //! 計算位置 tvec3<float> temp = vecSrc* frame._bone[g_quadVertices[i].matrixIndices[x]]; //! 計算權重位置 vec += temp * g_quadVertices[i].weights[x]; } calQuadVertices[i].x = vec.x; calQuadVertices[i].y = vec.y; calQuadVertices[i].z = vec.z; }
最后將計算出來的數據給OpenGL,進行繪制了:
glColorPointer(4,GL_FLOAT,sizeof(Vertex),calQuadVertices); glVertexPointer(3,GL_FLOAT,sizeof(Vertex),((float*)calQuadVertices) + 4); for (int i = 0 ;i < 3 ; ++ i ) { glDrawArrays(GL_LINE_LOOP,i * 4,4); }