Jump to content
  • entries
    44
  • 评论
    24
  • 查看
    144,992

3D游戏角色动画


May28

559 查看

3D游戏角色动画

作者:迷糊小亚

QQ:183237048

2006年6月8日

时光飞逝,4年的大学生活已到尾声。我没有留下遗憾,因为我一直向着梦想努力,拼搏。游戏一直是我的梦;)以此文共勉我的同志,大家,不要放弃,一起加油!(本人水平有限,请游戏界的前辈们多多指点。)

摘要:本文主要描述了3D游戏角色动画的原理及应用,从介绍微软的X文件到最为广泛应用的骨骼蒙皮动画,另外简要的介绍了下渐变动画的原理。

关键词:Role Animation Skeletal Animation Morphing Animation Skinned Mesh

Abstract:Introduce 3D Game Role Animation, for example Skeletal Animation and Skinned Mesh etc.

目录

一 概述3D角色动画的应用

二 3D游戏动画基础------基于时间的运动

三 3D游戏角色动画

1 介绍微软的X文件

2 骨骼蒙皮动画的原理与实现

3 增加场景数据

4 简介渐变动画

四 结束语

*****************************************************

正文:

概述3D角色动画的应用

3D角色动画是计算机动画技术的一个重要组成部分,也是计算机图形学的一个分支。无论是在离线渲染环境下,还是在实时渲染环境下,3D角色动画都得到了广泛的应用。在离线渲染环境下,主要应用于动画电影制作和各类广告制作。动画电影制作中所使用的3D角色动画技术的一个重要特点是动画数据量大,渲染需要耗费大量时间,因此动画作品必须预先制作,渲染,然后转化成视频文件播放。在实时渲染环境下,主要应用于虚拟现实,视频游戏,甚至是建模软件,动画制作软件。现在,随着计算机硬件技术的发展,特别是带有硬件加速功能的显卡性能的提高,很多曾经只能在离线环境下应用的技术,都转移到实时渲染环境中来。其中,实时渲染的角色动画技术得到了发展且被广泛的应用。目前,实时角色动画技术大体可分为三种类型。

第一类是关节动画(Skeletal Animation)。关节动画中的角色由若干独立的部分组成。每一个部分对应着一个独立的网格模型,不同的部分按照角色的特点组织成一个层次结构。比如说,一个人体模型可以由头,上身,左上臂,左前臂,左手,右上臂,右前臂,右手,左大腿,左小腿,左脚,右大腿,右小腿,右脚等各部分组成。而某个部分,可能是另一个部分的子节点,同时又是另一个部分的父节点。比如上面的人体模型中,右前臂就是右上臂的子节点,同时也是右手的父节点。而右上臂是上身的子节点,后者则是躯体的子节点。通过改变不同部分之间的相对位置,比如夹角,位移等等,就可以实现所需要的各种动画效果。这类动画的优点很多。首先,在动画序列的关键帧中只需要存储节点间的相对变化,因此动画文件占用的空间很小。其次,可以实现很多复杂的动画效果,如果应用程序支持反向动力学还可以动态实现预先存储的动画序列之外的新的动画效果。当然这类动画也有不少缺点。其中之一是由于角色模型是一个层次模型,要获得某一个部分相对于世界坐标的位置,必须从根结点开始遍历该节点所有的祖先节点累计计算模型的世界变换。但最关键的问题是在不同部分的结合处往往会有很明显的接缝,这会严重的影响模型的真实感。

第二类是渐变动画(Morphing Animation)。这种动画中的角色由一系列的渐变网格模型构成。在动画序列的关键帧中记录着组成网格的各个顶点的新位置或者是相对于原位置的改变量。通过在相邻关键帧之间插值来直接改变该网格模型中各个顶点的位置就可以实现动画效果。相对于关节动画,单一网格模型动画的角色看上去更真实,也不会有关节动画所面临的接缝问题。由于没有使用层次模型,获得模型网格顶点在世界坐标中位置的计算量也很小。但是,这类动画的适应性很弱,角色很难通过实时计算来与环境进行良好的互动,以获得预先存储的动画序列之外的动画效果。另一方面,由于关键帧要存储网格模型所有的顶点信息,动画文件占用的空间比较大。

第三类是骨骼蒙皮动画(Skinned Mesh)。骨骼蒙皮动画可以看作是关节动画和渐变动画的结合。他同时兼有关节动画的灵活和渐变动画的逼真。后面将详细介绍骨骼蒙皮动画的技术细节。

3D角色动画技术和其它动画技术相结合,就能创造出绚丽多彩的游戏世界。

3D游戏动画基础------基于时间的运动

在一个游戏项目中,计时扮演了一个重要的角色。基于时间的运动,也就是创建计时器来控制运动。它能够产生这样一种动画效果:同样处理10000毫秒的动画,在性能好的计算机上得到平滑完整的动画效果,在性能不好的计算机上显得跳帧,但也能够在10000毫秒的时候完成任务,和性能好的计算机是同步的。

基本思路是事先设置好动画关键帧序列,在主循环中判断出第一个动画关键帧和下一个动画关键帧的编号,利用一个时间计数器去定位相对于第一动画关键帧的位置。随着时间计数器的增长,不断从第一个动画关键帧的位置移动至下一个动画关键帧的位置。主要分为以下几个步骤:

1设置动画关键帧序列。

2计算出每一帧的时间Time,Time是相对于程序开始运行的毫秒数。

3定位出第一个动画关键帧和下一个动画关键帧。

4利用Time计算出相对于第一个动画关键帧的毫秒数,再利用这个偏移毫秒数计算出相对于第一个动画帧的偏移位置。

5设置变换矩阵。

6回到第2步。

上图表示了4帧的关键帧动画,其中第0帧和第3帧变换矩阵相同。下面直接看代码,我将结合代码详细叙述。

typedef struct sKeyframe

{

DWORD Time;

D3DMATRIX matTransformation;

} sKeyframe;//关键帧的结构,DWORD Time为执行该帧的时间,D3DMATRIX //matTransformation为在该帧时模型的变换矩阵。

sKeyframe g_Keyframes[4] =

{

// Keyframe 0, 0ms

{ 0, 1.000000f, 0.000000f, 0.000000f, 0.000000f,

0.000000f, 1.000000f, 0.000000f, 0.000000f,

0.000000f, 0.000000f, 1.000000f, 0.000000f,

0.000000f, 0.000000f, 0.000000f, 1.000000f },

// Keyframe 1, 40ms

{ 400, 0.000796f, 1.000000f, 0.000000f, 0.000000f,

-1.000000f, 0.000796f, 0.000000f, 0.000000f,

0.000000f, 0.000000f, 1.000000f, 0.000000f,

50.000000f, 0.000000f, 0.000000f, 1.000000f },

// Keyframe 2, 80ms

{ 800, -0.999999f, 0.001593f, 0.000000f, 0.000000f,

-0.001593f, -0.999999f, 0.000000f, 0.000000f,

0.000000f, 0.000000f, 1.000000f, 0.000000f,

25.000000f, 25.000000f, 0.000000f, 1.000000f },

// Keyframe 3, 120ms

{ 1200, 1.000000f, 0.000000f, 0.000000f, 0.000000f,

0.000000f, 1.000000f, 0.000000f, 0.000000f,

0.000000f, 0.000000f, 1.000000f, 0.000000f,

0.000000f, 0.000000f, 0.000000f, 1.000000f }

};//定义了4帧的关键动画。其中第3帧和第0帧的变换矩阵一样,为了使动画能进入循环状态。

void DoFrame() //此函数在循环内

{

static DWORD StartTime = timeGetTime();

DWORD Time = timeGetTime() - StartTime;

//用timeGetTime()得到一个操作系统运行的毫秒数,储存到static变量以后将不再改变,//DWORD Time变量不断改变,为本程序运行的毫秒数。

Time %= (g_Keyframes[3].Time+1);//得到一个不断从0到1200变化的毫秒数。

DWORD Keyframe = 0; // 从第0帧开始。

for(DWORD i=0;i<4;i++) {

// 如果Time>= 某一关键帧的时间,将关键帧定位于此帧。

if(Time >= g_Keyframes.Time)

Keyframe = i;

}

DWORD Keyframe2 = (Keyframe==3) ? Keyframe:Keyframe + 1;//得到接下来的关键帧,如//果Keyframe为第3关键动画帧,Keyframe2也为第3关键动画帧。

//当Keyframe=1200时这种情况才成立,几率很小。一般情况下,Keyframe2=Keyframe+1。

DWORD TimeDiff = g_Keyframes[Keyframe2].Time -

g_Keyframes[Keyframe].Time;

if(!TimeDiff)

TimeDiff=1;//计算两个sKeyframe的时间差,当Keyframe=Keyframe2=3时,TimeDiff=0,//此时另TimeDiff=1。

float Scalar = (float)(Time - g_Keyframes[Keyframe].Time) / (float)TimeDiff; // Scalar取 //值为[0,1),利用Time计算出相对于Keyframe的偏移毫秒数,再除以两//帧的时间差。

D3DXMATRIX matInt = D3DXMATRIX(g_Keyframes[Keyframe2].matTransformation) -

D3DXMATRIX(g_Keyframes[Keyframe].matTransformation);

matInt *= Scalar; //用于计算相对于Keyfrme的偏移位置。

matInt += D3DXMATRIX(g_Keyframes[Keyframe].matTransformation); // 计算出该帧处相//对于Keyfrme的偏移位置。

g_pD3DDevice→SetTransform(D3DTS_WORLD, &matInt); // 设置 world transformation matrix

设置完变换矩阵,剩下的事情就只是渲染了。创建计时器控制动画的技术是非常简单有效的,这是现代计算机游戏动画的基础,因此,必须深刻理解它的内容。

0 评论


Recommended Comments

没有要显示的评论。

访客
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • 创建新的...