视频教程请关注 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)的更多相关文章

  1. Unity3D 骨骼动画原理学习笔记

    最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭哧吭哧的~ 首先列出学习参考的前人的文章,本文较多的参考了其中的表述: 1. ...

  2. OpenGL10-骨骼动画原理篇(3)-Shader版本代码已经上传

    视频教程请关注 http://edu.csdn.net/lecturer/lecturer_detail?lecturer_id=440 接上一个例程OpenGL10-骨骼动画原理篇(2),对骨骼动画 ...

  3. OpenGL10-骨骼动画原理篇(2)

    接上一篇的内容,上一篇,简单的介绍了,骨骼动画的原理,给出来一个 简单的例程,这一例程将给展示一个最初级的人物动画,具备多细节内容 以人走路为例子,当人走路的从一个站立开始,到迈出一步,这个过程是 一 ...

  4. 骨骼动画的原理及在Unity中的使用

    制作骨骼动画 我们看看这几步操作后,我们得到了那些数据: 1.每个皮肤顶点的初始世界坐标. 2.每个骨骼关节顶点的初始世界坐标. 3.每个顶点被骨骼顶点的影响信息. 4.骨骼如何移动. 骨骼动画原理 ...

  5. 浅谈Unity3D 骨骼动画

    转载请标明出处http://www.cnblogs.com/zblade/ 最近研究了一下游戏中模型的骨骼动画的原理,做一个学习笔记,便于大家共同学习探讨. ps:最近改bug改的要死要活,博客写的吭 ...

  6. Spine Skeleton Animation(2D骨骼动画)

    骨骼动画 首先我们来看到底什么是骨骼动画: 在早期的机器上,渲染本身已经占用了很多CPU资源,因此,对于渲染,往往采取的是一种空间换时间的策略,以避免在模型的渲染中继续加重CPU的负担.帧动画模型在这 ...

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

    首先贴一个介绍max的sdk和骨骼动画的文章, 虽然很早的文章, 但是很有用, 感谢前辈们的贡献: 3Ds MAX骨骼动画导出插件编写 1.Dual Quaternion 关于Dual Quatern ...

  8. MD5骨骼动画模型加载(一)

    前面我们分析了静态模型OBJ格式,桢动画模型MD2,这篇主要分析骨骼动画MD5的一些概念并且实现. 混合桢动画有计算简单,容易实现等优点,但是在需要比较细致的效果时,则需要更多的关键桢,每桢都添加相同 ...

  9. MD5骨骼动画模型加载

    前面我们分析了静态模型OBJ格式,桢动画模型MD2,这篇主要分析骨骼动画MD5的一些概念并且实现. 混合桢动画有计算简单,容易实现等优点,但是在需要比较细致的效果时,则需要更多的关键桢,每桢都添加相同 ...

随机推荐

  1. Add custom field in Material Master

    1.Add fields in the Append Structure of table MARA. 2.Configure SPRO IMG -> Logistics General -&g ...

  2. 6M - 循环多少次?

    我们知道,在编程中,我们时常需要考虑到时间复杂度,特别是对于循环的部分.例如, 如果代码中出现 for(i=1;i<=n;i++) OP ; 那么做了n次OP运算,如果代码中出现 fori=1; ...

  3. CSU 1684-Disastrous Downtime

    题目链接:https://nanti.jisuanke.com/t/28879 思路:贪心,从最早收到请求的时刻开始,统计每个相差1000毫秒的时间段内接收的请求数量再计算该时间段内所需机器数目,答案 ...

  4. 《Miracle_House团队》第一次作业:团队亮相

    Our Team:Miracle_House part 1 团队成员组成: NO.1  汝春瑞   201571030125   (组长) Style:乐观开朗,认真踏实,责任心强,还有就是爱笑.随和 ...

  5. 编译搭建lnmp+zabbix

    搭建nginx 1)基础依赖包安装 yum -y install gcc gcc-c++ vim tree make cmake autoconf yum -y install openssl ope ...

  6. ABP框架系列之十三:(Authorization-授权)

    Introduction Almost all enterprise applications use authorization in some level. Authorization is us ...

  7. Solidity合约记录——(一)如何寻找以太坊真实Solidity源码

    在自主学习Solidity智能合约的过程中,第一份入手资料无疑是官方文档.感谢前辈们还能提供出文档的中文翻译,作为我入门的第一手资料:文末附上有用的学习链接{持续更新中} 阅读完基础文档同时上手合约后 ...

  8. 学以致用二十四-----shell脚本中的列表及space

    1.接触列表的概念是在pyhon中,殊不知在shell中也是有列表的. 如: 结果 列表的下标和python中一样,也是以0开头 注意 list 和list2 的区别   一个是用括号括起来,一个是用 ...

  9. 用jstack自动化捕抓异常java代码脚本

    #!/bin/bashdate=` date +%y%m%d-%H%M`pid=`top -bn1 |grep java | awk '{print $1 "\t" $9}' |h ...

  10. django创建上下文

    在app中创建context_processes.py(可以是别的名字),然后加载到settings里,这样所有的网页都可以传入变量 from .models import User def app0 ...