接上一篇的内容,上一篇,简单的介绍了,骨骼动画的原理,给出来一个
简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容
以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是
一个连续的过程,在这个一个过程中,人身体的骨头在位置在发生变化,
骨头发生变化以后,人的皮肤,肌肉就随着变化,上一个例程中我们计算
(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];
一个定点的声明如下:
struct Vertex
{
//! 颜色
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);
}