简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容
以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是
一个连续的过程,在这个一个过程中,人身体的骨头在位置在发生变化,
骨头发生变化以后,人的皮肤,肌肉就随着变化,上一个例程中我们计算
(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); }