骨骼动画原理(转载)

视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440

本例程展示如何建立骨骼动画,有些人叫蒙皮动画

定义如下:

当前有两种模型动画的方式:顶点动画和骨骼动画。顶点动画中,每帧动画其实

就是模型特定姿态的一个“快照”。通过在帧之间插值的方法,引擎可以得到平滑

的动画效果。在骨骼动画中,模型具有互相连接的“骨骼”组成的骨架结构,通过

改变骨骼的朝向和位置来为模型生成动画。

  骨骼动画比顶点动画要求更高的处理器性能,但同时它也具有更多的优点,

骨骼动画可以更容易、更快捷地创建。不同的骨骼动画可以被结合到一起——

比如,模型可以转动头部、射击并且同时也在走路。一些引擎可以实时操纵单

个骨骼,这样就可以和环境更加准确地进行交互——模型可以俯身并向某个方

向观察或射击,或者从地上的某个地方捡起一个东西。多数引擎支持顶点动画,

但不是所有的引擎都支持骨骼动画。

1. 关键帧动画,早期的cs就是用关键帧动画
  优点:

    计算量小,速度快,在早期计算机性能满足不了要求的时候采用的,

    最具代表性的就是Quake(雷神之锤),采用的md2文件格式。

  缺点:
    画面表现不过好,会有穿刺的情况出现

2.骨骼动画(蒙皮动画)

  优点:
    画面表现细腻,真实感很强,目前大多数游戏都采用该中类型的动画,典型的代表,

    Quake推出的md3文件格式,就是采用骨骼动画

  缺点:
    所有的定点都是根据骨骼的变化实时计算,计算量非常大。

骨骼动画的原理:

  正如其名,这种动画中包含骨骼(Bone)和蒙皮(Skinned Mesh)两个部分

一部分是Bone(骨头),一部分是Skin(皮).就像人体的组成一样。人要想做动作,

骨头要动起来,然后皮就被骨头带动起来,按照这样的理论,就产生了蒙皮动画。

在三维模型当中。bone就是骨头,皮就是skin mesh,mesh的其实就是模型来,

加上skin,说明这个模型的意义,做表皮的。

  
  我们在看待问题,学习东西的时候,要站在设计者的角度去考虑问题,很多

问题就不是问题了,很多问题就更加容易的理解,顺利成章。

  现在我们就站在设计者的角度上来看待骨骼动画,首相设计意图我们已经知
道,就是骨头带动肉动起来,那怎么个带动法呢 ?

来看下一,当我们的弯曲手臂的时候,就是肘关节动,其他的关节不动,而随着

肘关节的弯曲,我们肱二头肌会动,但幅度最大的是手臂,那我们想一下,是不

是这样来描述,当我们动一个关节的时候,会带动一部分肌肉动起来,而不是只

要动一个关节全身都在动。那么我们就可以这样来说,一个骨头动,会影响到一

部分的肉和皮动。逆向思路来思考下,肱二头肌要受到几个骨头的影响,会使得

肱二头肌的形状发生变化,影响最大的肘关节,其次是肩关节。肱二头肌是什么?

在程序中,他就是一些列的点数据。

我们定义个如下结构体(伪代码)

class Point

{

  float x,y,z;     //! 肌肉的位置

int  arBone[n];  //! 影响肌肉的骨头

float arWeight[n]  //! 每一个骨头对肌肉的影响度,例如 肘关节的影响度对肱二头肌很多,而肩关节要少一点。

};

如何来描述肌肉的位置呢?

for( int i = 0 ;i < n ; ++ i)

{

(x,y,z) += 骨头[i] * 骨头的影响度[i];

}

那有如何来描述骨头呢 ?在游戏中,骨头有位置,可以旋转,显示生活中骨头不能缩放,但游戏中可以。

所以描述一个骨头需要三个要素,位置,旋转,和缩放,最容易想到的就是使用一个矩阵来描述他了。

class  Bone :public Matrix

{
};

从上面的描述,我们知道要想绘制出来一模型,我们要存储的信息,所有的定点,所有的骨头,还有

那么每一个点被那么骨头影响,影响度是都少。具体计算如下。

一个人的模型有2000个顶点组成,有20快骨头组成。我们要做的计算如下:

for( int i = 0 ;i < 2000 ; ++ i )

{

for( int x = 0 ; x < 4(假设一个定点被四个骨头影响) ; ++ x )

  {

(x1,y1,z1) += (x,y,z) * bone * weight;

}

}

我们可以看出这个计算量是非常大的,几乎都在做矩阵的计算。

图中有两个骨头,一个是蓝色的,一个是黄色的,有三个长方形,一个是蓝色的,一个是绿色的,一个是换色的,

蓝色的长方形表示被蓝色的骨头影响,黄色的长方形表示被换色的骨头影响,绿色的表示受两个骨头的影响。

右键按住进行旋转,操作骨头。

  

可执行文件及源代码 :下载

#include "CELLWinApp.hpp"
#include <assert.h>
#include <math.h>
#include "matrix4x4f.h"
#pragma comment(lib,"opengl32.lib")

float g_fSpinX_R = 0.0f;
float g_fSpinY_R = 0.0f;

struct Vertex
{
//! 颜色
float r, g, b, a;
//! 位置
float x, y, z;
//! 影响度
float weights[2];
//! 矩阵的索引
short matrixIndices[2];
//! 影响整个定点的骨头个数
short numBones;
};

Vertex g_quadVertices[12] =
{
{ 1.0f,1.0f,0.0f,1.0f, -1.0f,0.0f,0.0f, 1.0f,0.0f, 0,0, 1 }, // 蓝色
{ 1.0f,1.0f,0.0f,1.0f, 1.0f,0.0f,0.0f, 1.0f,0.0f, 0,0, 1 },
{ 1.0f,1.0f,0.0f,1.0f, 1.0f,2.0f,0.0f, 0.5f,0.5f, 0,1, 2 },
{ 1.0f,1.0f,0.0f,1.0f, -1.0f,2.0f,0.0f, 0.5f,0.5f, 0,1, 2 },

{ 0.0f,1.0f,0.0f,1.0f, -1.0f,2.0f,0.0f, 0.5f,0.5f, 0,1, 2 }, // 绿色
{ 0.0f,1.0f,0.0f,1.0f, 1.0f,2.0f,0.0f, 0.5f,0.5f, 0,1, 2},
{ 0.0f,1.0f,0.0f,1.0f, 1.0f,4.0f,0.0f, 0.5f,0.5f, 0,1, 2 },
{ 0.0f,1.0f,0.0f,1.0f, -1.0f,4.0f,0.0f, 0.5f,0.5f, 0,1, 2 },

{ 0.0f,0.0f,1.0f,1.0f, -1.0f,4.0f,0.0f, 0.5f,0.5f, 0,1, 2 }, // 黄色
{ 0.0f,0.0f,1.0f,1.0f, 1.0f,4.0f,0.0f, 0.5f,0.5f, 0,1, 2 },
{ 0.0f,0.0f,1.0f,1.0f, 1.0f,6.0f,0.0f, 1.0f,0.0f, 1,0, 1 },
{ 0.0f,0.0f,1.0f,1.0f, -1.0f,6.0f,0.0f, 1.0f,0.0f, 1,0, 1 }
};

float arBone[] =
{
0.0f, 0.0f, 0.0f,
-0.2f, 0.2f,-0.2f,
0.2f, 0.2f,-0.2f,
0.0f, 3.0f, 0.0f,
-0.2f, 0.2f,-0.2f,
-0.2f, 0.2f, 0.2f,
0.0f, 0.0f, 0.0f,
0.2f, 0.2f,-0.2f,
0.2f, 0.2f, 0.2f,
0.0f, 0.0f, 0.0f,
-0.2f, 0.2f, 0.2f,
0.0f, 3.0f, 0.0f,
0.2f, 0.2f, 0.2f,
-0.2f, 0.2f, 0.2f,
};

matrix4x4f g_boneMatrix[2];
matrix4x4f g_matrixToRenderBone[2];

inline vector3f operator * (const vector3f& v, const matrix4x4f& mat)
{
return vector3f
(
v.x*mat.v[0][0] + v.y*mat.v[1][0] + v.z*mat.v[2][0] + 1*mat.v[3][0],
v.x*mat.v[0][1] + v.y*mat.v[1][1] + v.z*mat.v[2][1] + 1*mat.v[3][1],
v.x*mat.v[0][2] + v.y*mat.v[1][2] + v.z*mat.v[2][2] + 1*mat.v[3][2]
);
}

class Tutorial10 :public CELL::Graphy::CELLWinApp
{
public:
Tutorial10(HINSTANCE hInstance)
:CELL::Graphy::CELLWinApp(hInstance)
{
_lbtnDownFlag = false;
_fSpinY = 0;
_fSpinX = 0;
_bMousing_R = 0;
}
virtual void render()
{
do
{
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);

glMatrixMode( GL_MODELVIEW );
glLoadIdentity();
glTranslatef( 0.0f, 0.0f, -15 );

glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);

{
g_boneMatrix[0].identity();
g_matrixToRenderBone[0].identity();

matrix4x4f rotationMatrixY;
matrix4x4f rotationMatrixZ;
matrix4x4f boneRotationMatrix;

g_boneMatrix[1].identity();
g_matrixToRenderBone[1].identity();

matrix4x4f offsetMatrix_toBoneEnd;
matrix4x4f offsetMatrix_backFromBoneEnd;

offsetMatrix_toBoneEnd.translate_y( 3.0f );
offsetMatrix_backFromBoneEnd.translate_y( -3.0f );

rotationMatrixY.rotate_y( g_fSpinY_R);
rotationMatrixZ.rotate_z(-g_fSpinX_R);
boneRotationMatrix = rotationMatrixY * rotationMatrixZ;

g_boneMatrix[1] = g_boneMatrix[0] * offsetMatrix_toBoneEnd * boneRotationMatrix;
g_matrixToRenderBone[1] = g_boneMatrix[1];
g_boneMatrix[1] = g_boneMatrix[1] * offsetMatrix_backFromBoneEnd;
}
/**
* 绘制表皮,保存临时点数据
*/
Vertex calQuadVertices[12];
memcpy(calQuadVertices,g_quadVertices,sizeof(g_quadVertices));
for (int i = 0 ;i < 12 ; ++ i )
{
vector3f vec(0,0,0);
vector3f vecSrc(g_quadVertices[i].x,g_quadVertices[i].y,g_quadVertices[i].z);
for (int x = 0 ; x < calQuadVertices[i].numBones ; ++ x)
{
//! 计算位置
vector3f temp = vecSrc* g_boneMatrix[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;
}
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);
}
glDisableClientState(GL_COLOR_ARRAY);

/**
* 绘制骨头
*/
glVertexPointer(3,GL_FLOAT,0,arBone);
glPushMatrix();
{
//! 绿色骨头
glMultMatrixf( g_matrixToRenderBone[0].m );
glColor3f( 1.0f, 1.0f, 0.0 );
glDrawArrays(GL_LINE_STRIP,0,sizeof(arBone)/12);
}
glPopMatrix();

glPushMatrix();
{
//! 蓝色骨头
glMultMatrixf( g_matrixToRenderBone[1].m );
glColor3f( 0.0f, 0.0f, 1.0 );
glDrawArrays(GL_LINE_STRIP,0,sizeof(arBone)/12);
}
glPopMatrix();

SwapBuffers( _hDC );
} while (false);
}

/**
* 生成投影矩阵
* 后面为了重用性,我们会写一个专门的matrix类,完成矩阵的一系列擦做
* 这个是很有必须要的,当你对Opengl了解的不断深入,你会发现,很多都是和数学有关的
*/
void perspective(float fovy,float aspect,float zNear,float zFar,float matrix[4][4])
{
assert(aspect != float(0));
assert(zFar != zNear);
#define PI 3.14159265358979323f

float rad = fovy * (PI / 180);

float halfFovy = tan(rad / float(2));
matrix[0][0] = float(1) / (aspect * halfFovy);
matrix[1][1] = float(1) / (halfFovy);
matrix[2][2] = -(zFar + zNear) / (zFar - zNear);
matrix[2][3] = -float(1);
matrix[3][2] = -(float(2) * zFar * zNear) / (zFar - zNear);
#undef PI
}
virtual void onInit()
{
/**
* 调用父类的函数。
*/
CELL::Graphy::CELLWinApp::onInit();
glMatrixMode( GL_PROJECTION );
GLfloat matrix[4][4] =
{
0,0,0,0,
0,0,0,0,
0,0,0,0,
0,0,0,0
};
perspective(45.0f, (GLfloat)_winWidth / (GLfloat)_winHeight, 0.1f, 100.0f,matrix);
glLoadMatrixf((float*)matrix);
glClearColor(0.35f, 0.53f, 0.7f, 1.0f);

}

virtual int events(unsigned msg, unsigned wParam, unsigned lParam)
{
switch(msg)
{
case WM_LBUTTONDOWN:
{
_mousePos.x = LOWORD (lParam);
_mousePos.y = HIWORD (lParam);
_lbtnDownFlag = true;
SetCapture(_hWnd);
}
break;
case WM_LBUTTONUP:
{
_lbtnDownFlag = false;
ReleaseCapture();
}
break;
case WM_RBUTTONDOWN:
{
_ptLastMousePosit_R.x = _ptCurrentMousePosit_R.x = LOWORD (lParam);
_ptLastMousePosit_R.y = _ptCurrentMousePosit_R.y = HIWORD (lParam);
_bMousing_R = true;
}
break;
case WM_RBUTTONUP:
{
_bMousing_R = false;
}
break;
case WM_MOUSEMOVE:
{
int curX = LOWORD (lParam);
int curY = HIWORD (lParam);

if( _lbtnDownFlag )
{
_fSpinX -= (curX - _mousePos.x);
_fSpinY -= (curY - _mousePos.y);
}

_mousePos.x = curX;
_mousePos.y = curY;

_ptCurrentMousePosit_R.x = LOWORD (lParam);
_ptCurrentMousePosit_R.y = HIWORD (lParam);

if( _bMousing_R )
{
g_fSpinX_R -= (_ptCurrentMousePosit_R.x - _ptLastMousePosit_R.x);
g_fSpinY_R -= (_ptCurrentMousePosit_R.y - _ptLastMousePosit_R.y);
}
_ptLastMousePosit_R.x = _ptCurrentMousePosit_R.x;
_ptLastMousePosit_R.y = _ptCurrentMousePosit_R.y;

}
break;
}
return __super::events(msg,wParam,lParam);
}
protected:
unsigned _primitiveType;
/**
* 保存纹理Id
*/
unsigned _textureId;

float _fSpinX ;
float _fSpinY;
POINT _mousePos;
bool _lbtnDownFlag;

POINT _ptLastMousePosit_R;
POINT _ptCurrentMousePosit_R;
bool _bMousing_R;
};

int CALLBACK _tWinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPTSTR lpCmdLine,
int nShowCmd
)
{
(void*)hInstance;
(void*)hPrevInstance;
(void*)lpCmdLine;
(void*)nShowCmd;

Tutorial10 winApp(hInstance);
winApp.start(640,480);
return 0;
}

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

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

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

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

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

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

  

可执行文件以及代码

视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440

接上一个例程OpenGL10-骨骼动画原理篇(2),对骨骼动画的基本原理做了介绍,接下来

要对之前做的工作做一个分析和优化,骨骼动画要做大量的数学计算,当一个模型的顶点

与骨骼的数量都很多的情况下,会消耗大量的cpu时间,接下来要做的事情就是对程序进行

优化,从上面的计算过程,可以得出,有两个地方的计算量比较大,首先是矩阵和顶点相乘

,其次是每一帧要插值新的骨骼出来,相对定点计算来讲,骨骼的插值计算量应该算是较小

的,一个人物模型少则1000个顶点,多则几千个顶点。因此我们优化就从定点的计算开始

  当然,我们有三种方案,甚至更多(我只用其中的两个)。

  一:使用CPU优化

  二:使用Shader(glsl)优化

  三:使用cuda或者OpenCL进行优化

  先来看第一种,说道cpu优化,大家可能想到两件事情,一是从算法角度出发去优化算法,

二是使用使用更好的CPU指令进行优化,没错,我们就是要这样做,矩阵相乘的算法优化的空

间可能已经不大,但是我们采用SIMD指令集和对浮点运算做优化空间还是很大的。

  SIMD:

    (Single Instruction Multiple Data,单指令多数据流)能够复制多个操作数,并把它们打包

在大型寄存器的一组指令集,例:3DNow!、SSE。以同步方式,在同一时间内执行同一条指令。以

浮点计算来说,基本上可以到达四倍的加速比。因此采用SIMD可以大幅度的提高性能。cup上使用SIMD

指令:

  

__m128 sse_mul_ps(__m128 v, __m128 const m[4])
{
    __m128 i0 = m[0];
    __m128 i1 = m[1];
    __m128 i2 = m[2];
    __m128 i3 = m[3];

    __m128 m0 = _mm_mul_ps(v, i0);
    __m128 m1 = _mm_mul_ps(v, i1);
    __m128 m2 = _mm_mul_ps(v, i2);
    __m128 m3 = _mm_mul_ps(v, i3);

    __m128 u0 = _mm_unpacklo_ps(m0, m1);
    __m128 u1 = _mm_unpackhi_ps(m0, m1);
    __m128 a0 = _mm_add_ps(u0, u1);

    __m128 u2 = _mm_unpacklo_ps(m2, m3);
    __m128 u3 = _mm_unpackhi_ps(m2, m3);
    __m128 a1 = _mm_add_ps(u2, u3);

    __m128 f0 = _mm_movelh_ps(a0, a1);
    __m128 f1 = _mm_movehl_ps(a1, a0);
    __m128 f2 = _mm_add_ps(f0, f1);

    return f2;
}

  采用Shader优化,将矩阵与顶点的计算工作放到丁点Shader中完成,这样做以后,cpu机会完全

的解放出来,计算量可以忽略不计。然而这种方案也有些弊端,我们知道shader中,不能像cpu中编写

c++代码那种去动态的申请空间,所有的工作必须提前分配好。看下面的shader实现代码:

//!    必须提前分配足够大的空间
uniform mat4 boneMatrices[2];

attribute vec4 weights;
attribute vec4 matrixIndices;
attribute vec4 numBones;

void main( void )
{
    vec4 index  = matrixIndices;
    vec4 weight = weights;

    vec4 position    = vec4( 0.0, 0.0, 0.0, 0.0 );

    for( float i = 0.0; i < numBones.x; i += 1.0 )
    {
        position = position + weight.x * (boneMatrices[int(index.x)] * gl_Vertex);
        index  = index.yzwx;
        weight = weight.yzwx;
    }
    gl_Position = gl_ModelViewProjectionMatrix * position;
}

  这里不再针对shader做特别介绍,后面将补冲shader相关的例程

  将矩阵数据传递给shader

//! 使用shader计算顶点的位置
glUseProgramObjectARB( _programObj );
glUniformMatrix4fvARB( _boneMatrices_0, 1, false, frame._bone[0].data());
glUniformMatrix4fvARB( _boneMatrices_1, 1, false, frame._bone[1].data());

将每一个定点的权重,矩阵的索引,以及矩阵的个数给shader:

                   //! 传递权重
                    fWeights[0] =   g_quadVertices[i * 4 + x].weights[0];
                    fWeights[1] =   g_quadVertices[i * 4 + x].weights[1];
                    glVertexAttrib4fvARB(_weights, fWeights );

                    //! 传递索引
                    fMatrixIndices[0]   =   g_quadVertices[i * 4 + x].matrixIndices[0];
                    fMatrixIndices[1]   =   g_quadVertices[i * 4 + x].matrixIndices[1];
                    glVertexAttrib4fvARB(_matrixIndices, fMatrixIndices );

                    //! 传递数量
                    fNumBones[0]        =   g_quadVertices[i * 4 + x].numBones;
                    glVertexAttrib4fvARB(_numBones, fNumBones );

  虽然有这样的缺点,但我们是可以避免的,采用的方式就是:根据动画的骨骼的数量,权重的数量去动态的

产生shader代码然后进行编译执行,这是一个解决方案,当然我们还有另外一个解决方式,就是首先预先分配

一些方案,例如当骨头数量小于32个的时候,我们调用一个shader,当在64一下的时候调用另外一个,在多一

点就调用128的 shader,....

  第三种方案,对你的显卡有很高的要求,必须支持OpenCL或者cuda,才可以去使用他,当然OpenCL或者

cuda可以直接的访问OpenGL的数据,效率上来说与shader相当(我还没有进行这样的实现),有兴趣的可以进行

尝试。

  CPU优化版本代码下载(稍后上传,敬请关注)

  Shader优化版本代码下载(稍后上传,敬请关注)

时间: 2024-10-07 04:32:04

骨骼动画原理(转载)的相关文章

从Sprite3D理解3D骨骼动画原理

为了能够更好的使用cocos为我们提供的Sprite3D,我和大家分享一下Sprite3D中关于骨骼动画原理的部分,本文使用cocos2d-x 3.2版本,这是cocos首次出现3D骨骼动画的版本,相对与本文写出来时候最新的3.5版本,由于没有其他比如灯光等功能,3D骨骼动画模块读起来要更加的清晰.如果文章有纰漏或者错误的地方,也请大家指教. 目前引擎支持3种动画格式,分别是.obj,.c3b,.c3t,由于.obj没有骨骼,.c3b是二进制,而.c3t是json格式,所以本文就用官方test中

Unity3D 骨骼动画原理学习笔记

最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1.骨骼动画详解 :http://blog.csdn.net/ccx1234/article/details/6641944,不过这篇文章的原文已经被csdn封了:D,可以看看对应的转载的文章也行 2.OpenGL10-骨骼动画原理篇:http://www.cnblogs.com/zhanglitong

骨骼动画原理

动画相关理论详解 一 .骨架 骨架由一系列具有层次关系的关节(骨骼)和关节链组成,是一种树结构,选择其中一个是根关节,其它关节是根关节的子孙,可以通过平移和旋转根关节移动并确定整个骨架在世界空间中的位置和方向.父关节运动能影响子关节运动,但子关节运动对父关节不产生影响,因此,平移或旋转父关节时,也会同时平移或旋转其所有子关节. 二.骨骼的表示 通常会将关节进行编号\(0\sim N-1\),编号也称关节索引,通过索引查找关节比关节名查找高效的多.通常一个关节包含以下信息 关节名 父关节索引(根关

Unity Spine Skeleton Animation 2D骨骼动画 For Game 介绍

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,我们致力于打造业内unity3d培训.学习第一品牌. 首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这种条件下应运而生.比较著名的帧动画格式是Quake2所采用的MD2.到今天为止,帧动画依然存在,只不过帧动

Spine Skeleton Animation 2D骨骼动画 For Game 介绍

首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这种条件下应运而生.比较著名的帧动画格式是Quake2所采用的MD2.到今天为止,帧动画依然存在,只不过帧动画更多地是来描述小且动作相对少些的物体. GPU出现后,CPU的问题早已不像以前那么突出,一些新的手段和技术也可以被应用进来了.骨骼动画相对于帧动画而言,更加灵活多变,但同时,骨骼动画需要更多的计算量,因

骨骼动画程序

最近有人问我怎样实现骨骼动画,于是我就想起了我以前写的这篇文章,贴上来给大家看看. 一.文章编写目的 写这篇文章,是给程序员看的.目的在于给程序员介绍骨骼动画的原理.数据结构和程序实现的粗略方法. 骨骼动画的应用面很多,主要用在3D角色动画,不过现在也很多人用于2D动画.下面的内容不会直接的把程序列出,只会阐述原理,关键的步骤是使用矩阵做坐标系变换.原理明白之后,不管2D或者3D应该都能自己编写. 二.什么是骨骼动画 传统的动画,一般是对一个物体对象进行位移.旋转.缩放.变形,然后把关键帧的信息

骨骼动画具体解释

转载:http://blog.csdn.net/ccx1234/article/details/6641944 近期,再次研究其骨骼动画,发现这篇文章讲的相当不错,通俗易懂,非常好的学习资源. 骨骼蒙皮动画(Skinned Mesh)的原理解析(一) 一)3D模型动画基本原理和分类 3D模型动画的基本原理是让模型中各顶点的位置随时间变化.主要种类有Morph动画,关节动画和骨骼蒙皮动画(Skinned Mesh).从动画数据的角度来说,三者一般都採用关键帧技术,即仅仅给出关键帧的数据,其它帧的数

【转】将3DMAX中的动画通过OGREMAX导入OGRE中,并生成相应的骨骼动画

原创内容转载请注明:http://weibo.com/gdexqin 程序代码的的基础在http://blog.sina.com.cn/s/blog_7c03dc6f01012um2.html中 先看导入后的效果. 动画模型是用别人传到网上的(好像这个人物叫真名法典的样子,不太认识,但是感谢上传者),之前我们已经完成了通过OGREMAX导入静态的场景的效果,导入骨骼动画我们需要对OGREMAX的导出选项进行一些修改. 下面先了解一下OGREMAX的一些基本选项(转自网络): ----------

引擎设计跟踪(九.14.2b) 骨骼动画基本完成

首先贴一个介绍max的sdk和骨骼动画的文章, 虽然很早的文章, 但是很有用, 感谢前辈们的贡献: 3Ds MAX骨骼动画导出插件编写 1.Dual Quaternion 关于Dual Quaternion, 这里不做太详细的介绍了,贴出来几个链接吧: http://en.wikipedia.org/wiki/Dual_quaternion http://www.seas.upenn.edu/~ladislav/kavan08geometric/kavan08geometric.pdf http