视频教程请关注 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. [leetcode]29. Divide Two Integers两整数相除

      Given two integers dividend and divisor, divide two integers without using multiplication, divisio ...

  2. ES6 Iterator

    不同数据集合怎么用统一的方式读取 可以用for...of循环了

  3. POJ 1741.Tree 树分治 树形dp 树上点对

    Tree Time Limit: 1000MS   Memory Limit: 30000K Total Submissions: 24258   Accepted: 8062 Description ...

  4. 让.net core 支持静态文件

    想不到默认的.net core竟然不支持静态文件,还需要额外配置中间件来支持 1.Nuget安装  Microsoft.aspnetcore.staticfiles 2.在Startup.cs中使用服 ...

  5. 【机器学习】Octave 实现逻辑回归 Logistic Regression

    ex2data1.txt ex2data2.txt 本次算法的背景是,假如你是一个大学的管理者,你需要根据学生之前的成绩(两门科目)来预测该学生是否能进入该大学. 根据题意,我们不难分辨出这是一种二分 ...

  6. MongoDB学习记录(一) - 安装、启动与建立数据库

    简要说明一个基本概念:MongoDB中的三要素:数据库(database).集合(collection)和文档(document). 文档:类似于JSON对象,由字段(field)和值(value)组 ...

  7. qhfl-7 结算中心

    结算中心,即从购物车前往支付前的确认页面,这里要开始选择优惠券了 """ 前端传过来数据 course_list 课程列表 redis 中将要存放的结算数据 { sett ...

  8. HDU 4309 Seikimatsu Occult Tonneru (状压 + 网络流)

    题意:输入 n 个城市 m 条边,但是边有三种有向边 a b  c d,第一种是 d 是 0,那么就是一条普通的路,可以通过无穷多人,如果 d < 0,那么就是隧道,这个隧道是可以藏 c 个人, ...

  9. Hibernate 基础入门

    概述: JDBC:将java和数据库连接的驱动程序加载到项目中,在代码里面,注册驱动,创建链接,创建satement对象,发送并执行sql之类,关闭连接. hibernate :数据持久化一个框架,对 ...

  10. Xutils简

    //解析 private void myinitData() { RequestParams parms=new RequestParams("http://huixinguiyu.cn/A ...