骨骼动画
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
骨骼动画(Skeletal Animation)
相信这里没有人没玩过采用骨骼动画技术的游戏,看看那些热门的动作游戏,例如《波斯王子》、《分裂细胞》和《战神》,你就知道骨骼动画的威力了(我承认是猜的)。骨骼动画技术用来使我们的3D模型在屏幕上动起来,通过和动作捕捉技术结合,可以让模型做出非常逼真的动作。而这样一个极具威力的技术,其原理却相当简单。
假设我们要让游戏主角做出一个动作,例如波斯王子拿弯刀往前一劈。最简单的方法,就是让模型师建一个动画序列,然后在程序中逐帧播放,就像放电影一样。不过这样一来工作量就太大了,玩家也需要N个G的硬盘来安装这个游戏。与此不同,骨骼动画技术采用了一种很聪明的方式。首先,建模师完成一个标准姿势的3D模型,通常是双手沿着肩的方向伸展平放,双脚打开。所有的后继动作将由这个基础动作演变得到。在完成这个基准模型之后,建模师再建一个骨骼结构,一系列相互关联的顶点,就像一个骨架一样,与人体模型各个关节匹配并且都会有一定数量的顶点与之关联。想象一下人和人身上的骨骼就很容易知道我在说什么。之后,在我们想要完成的动画序列中,挑选一些关键帧,对每个关键帧,将骨骼的位置与关键帧匹配。然后把这一系列的关键帧骨骼保存起来,除了骨骼的位置,同时保存的还有从基准位置变换到当前关键帧的旋转、平移、缩放或者一个混合的坐标变换矩阵。在我们引擎中,首先根据当前时间查找这时候角色是处于哪两个关键帧中间。找到之后以时间为参数在关键帧的坐标变换矩阵之间求插值,用插值结果来决定骨骼当前的位置。骨骼位置求出来后,所有和骨骼关联的顶点的坐标也可以相应求出来了。通过使用骨骼动画技术,我们用相对较少的数据就可以播放很平滑的动画!
了解了相关原理,来看看如何在directx中播放骨骼动画。我的参考书是《Advanced Animation with DirectX》。
现在我们知道为了播放骨骼动画,需要有骨骼(bone)的数据,模型(mesh)的数据,关联骨骼和模型上每个顶点的关联数据,以及关键帧的坐标变换数据。所有这些数据必须以某种形式存在于某个地方供我们获取才行。这里要介绍的MS的x文件格式以及从中获取数据的方法。强烈建议大家都来学习一下x文件格式!你会发现它即简单又强大,即使用来存放自定义数据也是相当的方便,一旦掌握之后我保证你会对它爱不释手。
典型的x文件以数据模板和实际数据两部分组成。数据模板类似c++中的结构定义,不过更为灵活和开放。实际数据就是遵守模板定义的数据段。看一个例子,
template Employee {
<3D82AB43-62DA-11cf-AB39-0020AF71E433> // 每个模板关联唯一的GUID
STRING Name; // 姓名
DWORD Sex; // 性别
[ContactEntry] // 联系方式, 另一个模板,模板可以嵌套
}
template ContactEntry {
<4C9D055B-C64D-4bfe-A7D9-981F507E45FF> // GUID
STRING PhoneNumber; // 电话号码
STRING Address; // 地址
}
Employee David{
"David";
1;
ContactEntry{
"100-100000000";
"far far away";
}
}
从上面这个简单的例子我们就可以看出x文件的大概模样了,详细的情况大家可以参考《Advanced Animation with DirectX》。下面我们看如何来读取这样一个x文件,借助下几个对象,
ID3DXFile -- x文件格式文档对象。例如Employee.x这样一个文件。
ID3DXFileEnumObject -- 用来枚举x文档的顶级模板数据。所谓顶级模板数据是指那些没有
父模板的数据,例如上面的David数据段。
ID3DXFILEDATA -- 模板数据。上面的David和他的联系方式都是ID3DXFILEDATA
对象,自包含。
下面看实际的分析函数, 下面的代码适用于DirectX 9.0 SDK Update (October 2004),原书的代码有点过时了。
//-----------------------------------------------------------------------------
// 名称 : Parse
// 描述 : 分析x文件格式文档
//-----------------------------------------------------------------------------
bool Parse( char *filename, void **pData )
{
LPD3DXFILE lpD3DXFile;
LPD3DXFILEENUMOBJECT lpD3DXFileEnumObj;
LPD3DXFILEDATA lpD3DXFileData;
// 参数检查
if( NULL == filename )
return false;
// 创建X文件对象
HRESULT hr = D3DXFileCreate( &lpD3DXFile );
if( FAILED( hr ) )
return false;
// 注册标准模板
hr = lpD3DXFile->RegisterTemplates(
( LPVOID )D3DRM_XTEMPLATES, D3DRM_XTEMPLATE_BYTES );
if( FAILED( hr ) )
{
Release
return false;
}
// 创建X文件枚举对象
hr = lpD3DXFile->CreateEnumObject(
filename, D3DXF_FILELOAD_FROMFILE, &lpD3DXFileEnumObj ); if( FAILED( hr ) )
{
Release
return false;
}
// 解析开始
bool parseResult = BeginParse( pData );
if( true == parseResult )
{
// 查询顶级模板数
SIZE_T childCount = 0;
lpD3DXFileEnumObj->GetChildren( &childCount );
// 分析每个订级模板
for( DWORD i=0; i { // 获取当前模板 hr = lpD3DXFileEnumObj->GetChild( i, &lpD3DXFileData ); if( FAILED( hr ) ) break;