接上一篇的内容,上一篇,简单的介绍了,骨骼动画的原理,给出来一个

简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容

以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是

一个连续的过程,在这个一个过程中,人身体的骨头在位置在发生变化,

骨头发生变化以后,人的皮肤,肌肉就随着变化,上一个例程中我们计算

(OpenGL10-骨骼动画原理篇(1))计算了根据骨头的位置计算皮肤的位置

只是计算量一刻的动作,走路的过程是连续的,就意味着我们要记录下来

骨头在运动过程中所以位置变化数据,这样才可以根据不同时刻的骨骼的

位置计算出来皮肤的位置。

opencv 人体骨骼动画 opengl骨骼_关键帧

现在问题出来了,如果美术做了一个动画有5秒钟,每一秒播放60帧来

计算,我们要记录非常多的骨头的信息,小下面这样:

假设人有100个骨头

Bone person[100]

一秒钟60帧 × 5秒  × 100,这个就是我们要记录的数据量,由此可见

数据量是非常大的,实际上大可不必这样做,想一,是否可以记录一个

关键帧的,其他的数据又关键帧来计算呢 ?假设我们记录了10个关键点

其他的数据根据时间按照一定的插值算法进行插值,那么数据量就骤然

降低非常多呢。出于这样的想法,我们增加了一个新的概念,关键帧。

骨骼动画系统的流程如下:

opencv 人体骨骼动画 opengl骨骼_opencv 人体骨骼动画_02

  下面我们使用程序的角度来描述下该问题:

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);
}