OpenGL10-⾻骼动画原理篇(1)本例程展⽰如何建⽴⾻骼动画,有些⼈叫蒙⽪动画
定义如下:
当前有两种模型动画的⽅式:顶点动画和⾻骼动画。顶点动画中,每帧动画其实
就是模型特定姿态的⼀个“快照”。通过在帧之间插值的⽅法,引擎可以得到平滑
的动画效果。在⾻骼动画中,模型具有互相连接的“⾻骼”组成的⾻架结构,通过
改变⾻骼的朝向和位置来为模型⽣成动画。
⾻骼动画⽐顶点动画要求更⾼的处理器性能,但同时它也具有更多的优点,
⾻骼动画可以更容易、更快捷地创建。不同的⾻骼动画可以被结合到⼀起——
⽐如,模型可以转动头部、射击并且同时也在⾛路。⼀些引擎可以实时操纵单
个⾻骼,这样就可以和环境更加准确地进⾏交互——模型可以俯⾝并向某个⽅
向观察或射击,或者从地上的某个地⽅捡起⼀个东西。多数引擎⽀持顶点动画,
2012年12月四级作文
但不是所有的引擎都⽀持⾻骼动画。
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 <asrt.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 },
span80>万国司考{ 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[] =
{
the son
0.0f, 0.0f, 0.0f,admonish
-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 = fal;
_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;
albertmatrix4x4f rotationMatrixZ;
matrix4x4f boneRotationMatrix;
g_boneMatrix[1].identity();
g_matrixToRenderBone[1].identity();
matrix4x4f offtMatrix_toBoneEnd;
matrix4x4f offtMatrix_backFromBoneEnd;
anslate_y( 3.0f );
anslate_y( -3.0f );
boneRotationMatrix = rotationMatrixY * rotationMatrixZ;
flawlessg_boneMatrix[1] = g_boneMatrix[0] * offtMatrix_toBoneEnd * boneRotationMatrix; g_matrixToRenderBone[1] = g_boneMatrix[1];
g_boneMatrix[1] = g_boneMatrix[1] * offtMatrix_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);
joomlaglPushMatrix();
{
//! 绿⾊⾻头
中生代是什么意思
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 (fal);
}
/**
* ⽣成投影矩阵
* 后⾯为了重⽤性,我们会写⼀个专门的matrix类,完成矩阵的⼀系列擦做
* 这个是很有必须要的,当你对Opengl了解的不断深⼊,你会发现,很多都是和数学有关的*/
void perspective(float fovy,float aspect,float zNear,float zFar,float matrix[4][4])
{
asrt(aspect != float(0));
asrt(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] =
{