收藏 分享(赏)

骨骼动画的原理与实现.docx

上传人:hwpkd79526 文档编号:4299215 上传时间:2018-12-21 格式:DOCX 页数:11 大小:24.97KB
下载 相关 举报
骨骼动画的原理与实现.docx_第1页
第1页 / 共11页
骨骼动画的原理与实现.docx_第2页
第2页 / 共11页
骨骼动画的原理与实现.docx_第3页
第3页 / 共11页
骨骼动画的原理与实现.docx_第4页
第4页 / 共11页
骨骼动画的原理与实现.docx_第5页
第5页 / 共11页
点击查看更多>>
资源描述

1、骨骼动画的原理与实现3D 2008-05-28 14:04:10 阅读 176 评论 0 字号:大中小 订阅 这里的骨骼蒙皮动画特指 skinnd mesh,也叫骨骼动画。无论是合金装备,波斯王子,还是魔兽世界,到处都是骨骼动画技术的运用。用它塑造了各种各样,栩栩如生的生物。其中,人类体格的骨骼动画运用最为广泛。现在让我们一步一步揭开它神秘的面纱。 从本质上来讲,所有的 3D 角色动画系统都是基于一种逻辑,就是用一定的方法去改变 Mesh 顶点的位置,只是具体改变的方法不同而已。骨骼动画也是一样的。 骨骼动画的基本原理就是首先控制各个骨骼和关节,再使符在上面的 skinned mesh 与其匹

2、配。在骨骼蒙皮动画中,一个角色由作为皮肤的单一网格模型和按照一定层次组织起来的骨骼组成。骨骼层次描述了角色的结构,就像关节动画中的不同部分一样,骨骼蒙皮动画中的骨骼按照角色的特点组成一个层次结构。相邻的骨骼通过关节相连,并且可以作相对的运动。通过改变相邻骨骼间的夹角,位移,组成角色的骨骼就可以做出不同的动作,实现不同的动画效果。皮肤则作为一个网格蒙在骨骼之上,规定角色的外观。这里的皮肤不是固定不变的刚性网格,而是可以在骨骼影响下变化的一个可变形网格。组成皮肤的每一个顶点都会受到一个或者多个骨骼的影响。在顶点受到多个骨骼影响的情况下,不同的骨骼按照与顶点的几何,物理关系确定对该顶点的影响权重,这

3、一权重可以通过建模软件计算,也可以手工设置。通过计算影响该顶点的不同骨骼对它影响的加权和就可以得到该顶点在世界坐标系中的正确位置。动画文件中的关键帧一般保存着骨骼的位置,朝向等信息。通过在动画序列中相邻的两个关键帧间插值可以确定某一时刻各个骨骼的新位置和新朝向。然后按照皮肤网格各个顶点中保存的影响它的骨骼索引和相应的权重信息可以计算出该顶点的新位置。这样就实现了在骨骼驱动下的单一皮肤网格变形动画。或者简单地说骨骼蒙皮动画。骨骼蒙皮动画的效果比关节动画和单一网格动画更逼真,更生动。而且,随着 3D 硬件性能的提高,越来越多的相关计算可以通过硬件来完成,骨骼蒙皮动画已经成为各类实时动画应用中使用最

4、广泛的动画技术。下面讨论骨骼蒙皮动画实现的技术细节。在一个典型的骨骼蒙皮动画模型文件中,会保存如下信息:网格信息,骨骼信息和动画信息。网格信息是角色的多边形模型。该多边形模型一般由三角形面片组成,每一三角形面片有三个指向模型的顶点表的索引。通过该索引,可以确定该三角形的三个顶点。顶点表中的每一顶点除了带有位置,法向量,材质,纹理等基本信息外,还会指出有哪些骨骼影响了该顶点,影响权重又是多少。影响一个顶点的最大骨骼数一般取决于模型的设计和目标硬件平台的限制。比如,对于一个典型的人体骨架,一般只有在关节附近的顶点才会受到相邻几块骨骼的影响,而同时影响某一顶点的骨骼数,也不会超过四块。骨骼信息包括全

5、部骨骼的数量和每一骨骼的具体信息。所有的骨骼按照父子关系组织成一棵树。树根代表整个骨架,其余每一节点包括叶子节点代表一根骨骼。每一根骨骼包括该骨骼在父骨骼坐标系中的变换矩阵,通过该变化矩阵确定了该骨骼在父骨骼坐标系中的位置。在动画信息中则保存了若干关键帧。每一关键帧指出了每一骨骼在该时刻相对于父骨骼坐标系的变换矩阵,当然也可以是该骨骼相对于父骨骼的位置,朝向等变动。在播放动画序列中的任一时刻: 1)首先确定该时刻之前和之后的两个关键帧,然后按照该时刻与前后两个关键帧时刻的时间值插值计算出该时刻该骨骼相对于父骨骼的新变换矩阵,这个变换矩阵往往代表旋转变换,放缩变换也能接受,如果是平移变换矩阵,很

6、容易将 Mesh 四分五裂。 2)对于皮肤网格中的每一个顶点,计算它在世界坐标中新的位置和朝向。首先找到影响该顶点的所有骨骼。然后计算每一骨骼对该顶点的影响。也就时说,计算在该骨骼独立作用下顶点的新位置。计算按照如下公式: 顶点的新位置 = 最初状态顶点的位置* 最初状态骨骼世界变换矩阵的逆矩阵* 骨骼的新变换矩阵(I) 然后将所有这些新位置按照每一骨骼的影响权重加权求和。注意所有权重的和应该恰好为 1。在公式(I)中,最初状态顶点的位置为什么首先要与最初状态骨骼世界变矩阵的逆矩阵相乘呢?前面说过,骨骼的新变换矩阵是相对于父骨骼变换的,另一方面这个新变换矩阵是世界变换矩阵,它的任何变换是相对于

7、世界坐标系原点的。因此需要把最初状态顶点移动到相当于父骨骼节点是原点的位置上,再进行矩阵变换。 3)根据网格模型顶点的新位置和朝向绘制角色网格。 下面我们结合具体的图形 API 环境(Direct3D)来进一步介绍骨骼动画。 第一,了解骨骼结构(Skeletal Structures)和骨层级(Bone Hierarchies): 骨骼结构就是连续很多的骨头(Bone)相结合,形成的骨层级。第一个骨头叫做根骨( root bone),是形成骨骼结构的关键点。其它所有的骨骼作为孩子骨(child bone)或者兄弟骨(sibling bone)附加在根骨之上。所谓的“骨”用一个帧(frame)对

8、象表示。在 Directx 中,用一个 D3DXFRAME 结构或者 X 文件中的 Frame template 来表示帧对象。下面看一下 Frame template 和 D3DXFRAME 结构的定义: template FrameFrameTransformMatrix frameTransformMatrix; / 骨骼相对于父节点的坐标变换矩阵,就是一个matrixMesh mesh; / 骨骼的 Mesh typedef struct _D3DXFRAMELPSTR Name; / 骨骼名称 D3DXMATRIX TransformationMatrix; / 相对与父节点的坐标变

9、换矩阵 LPD3DXMESHCONTAINER pMeshContainer; / LPD3DXMESHCONTAINER 对象, /用来加载 MESH,还有一些附加属性 ,见 SDK struct _D3DXFRAME *pFrameSibling; / 兄弟节点指针,和下面的子节点指针 / 一块作用构成骨骼的层次结构。 struct _D3DXFRAME *pFrameFirstChild; / 子节点指针 D3DXFRAME, *LPD3DXFRAME; 注意 D3DXFRAME * pFrameSibling 和 D3DXFRAME * pFrameFirstChild,主要是利用这两

10、个指针形成骨层级。pFrameSibling 把一个骨头连接到兄弟层级,相对的,pFrameFirstChild 把一个骨头连接到子层级。通常,你需要用建模软件为你的程序创建那些骨骼结构,输出骨层级到 X 文件以便使用。Microsoft 有 3D Studio Max 和 Maya 的输出插件( exporter),可以输出骨骼和动画数据到 X 文件。很多建模程序也都有这样的功能。 X 文件包含了帧数据,用一个帧(frame )模版的数据对象层级代表骨骼层级。如下图: 注意上面的图,利用 D3DXFRAME pointers 指针形成了一个兄弟帧和孩子帧的链表。 在前面 template F

11、rame 中已经提及过每个 Frame 数据对象中存放着一个变换矩阵,这个矩阵描述了该骨骼相对于父骨骼的位置。另外在根 Frame 数据对象中内嵌了一个标准的 Mesh 数据对象。Frame 定义了骨骼的层级,而 Mesh 中的 SkinWeights 数据对象定义了 Frame 代表的骨头。我们用 D3DXFRAME 结构容纳从 X 文件加载进来的 Frame 数据对象。为了更好的容纳 Frame 数据对象,我们需要扩展下D3DXFRAME 结构: struct D3DXFRAME_EX : D3DXFRAME D3DXMATRIX matCombined; / 组合变换矩阵,用于储存变换的

12、骨骼矩阵 D3DXMATRIX matOriginal; / 从 X 文件加载的原始变换矩阵 D3DXFRAME_EX() Name = NULL; pMeshContainer = NULL; pFrameSibling = pFrameFirstChild = NULL; D3DXMatrixIdentity( D3DXMatrixIdentity( D3DXMatrixIdentity( D3DXFRAME_EX() delete Name; Name = NULL; delete pFrameSibling; pFrameSibling = NULL; delete pFrameFir

13、stChild; pFrameFirstChild = NULL; 利用我们以前介绍的 cXParse 类可以遍历 X 文件的数据对象,从而加载出 Frame 数据对象。下面的代码都是写在方法 ParseObject 中,如下: / 判断当前分析的是不是 Frame 节点if( objGUID = TID_D3DRMFrame ) / 引用对象直接返回,不需要做分析。一个数据段实际定义一次后可以被其他模板引用,例 /如后面的 Animation 动画模板就会引用这里的 Frame / 节点,标识动画关联的骨骼。if( pDataObj-IsReference() )return true; /

14、 D3DXFRAME_EX 为 D3DXFRAME 的扩展结构,增加些数据成员 D3DXFRAME_EX *pFrame = new D3DXFRAME_EX(); / 得到名称pFrame-Name = GetObjectName( pDataObj ); / 注意观察文件就可以发现一个 Frame 要么是根 Frame,父节点不存在, 要么作为某 /个 Frame 的孩子 Frame 而存在。if( NULL = pData )/ 作为根节点的兄弟节点加入链表。pFrame-pFrameSibling = m_pRootFrame;m_pRootFrame = pFrame;pFrame

15、= NULL; / 将自定义数据指针指向自己,供子节点引用。pData = ( void* )else/ 作为传入节点的子节点D3DXFRAME_EX *pDataFrame = ( D3DXFRAME_EX* )( *pData );pFrame-pFrameSibling = pDataFrame-pFrameFirstChild;pDataFrame-pFrameFirstChild = pFrame; pFrame = NULL; pData = ( void* ) 记住我们只需要做一件事情,判断类型,分配匹配的对象然后拷贝数据,下面来分析 Frame 中的 matrix, / fra

16、me 的坐标变换矩阵, 因为 matrix 必然属于某个 Frame 所以 pData 必须有效else if( objGUID = TID_D3DRMFrameTransformMatrix / 先取得缓冲区大小,应该是个标准的 4x4 矩阵 DWORD size = 0;LPCVOID buffer = NULL;hr = pDataObj-Lock( if( FAILED( hr ) )return false; / 拷贝数据if( size = sizeof( D3DXMATRIX ) )memcpy( pDataObj-Unlock(); pDataFrame-matOriginal

17、 = pDataFrame-TransformationMatrix; 第二,修改和更新骨骼层级: 加载完骨骼层级之后,你可以操作它,更改骨骼的方位。你需要创建一个递归函数,按照名字找到相应的Frame 数据对象。这个函数如下: D3DXFRAME_EX *FindFrame(D3DXFRAME_EX *Frame, char *Name) if(Frame Name Name, Name) / strcmp 函数比较两个字符串,如果两个字符串相等,返回 0 return Frame; / 在 sibling frames 找匹配的名字 if(Frame pFrameSibling) D3DX

18、FRAME_EX *FramePtr = FindFrame(D3DXFRAME_EX*)FramepFrameSibling, Name); if(FramePtr) return FramePtr; / 在 child frames 找匹配的名字 if(Frame pFrameFirstChild) D3DXFRAME_EX *FramePtr = FindFrame(D3DXFRAME_EX*)FramepFrameFirstChild, Name); if(FramePtr) return FramePtr; / 如果没有找到,返回 NULL return NULL; 如果你想找到一个

19、叫“Leg”的 Frame,可以把“Leg ”传入 FindFrame 函数,并且提供指向 RootFrame的指针: / pRootframe 为 D3DXFRAME_EX root frame 指针 D3DXFRAME_EX *Frame = FindFrame(pRootFrame, “Leg“); if(Frame) / 可以在这里做一些处理,比如旋转操作 / 你在这里可以稍微的旋转这个骨头 D3DXMatrixRotationY(TransformationMatrix, 1.57f); 一旦你修改变换骨头,你需要更新整个骨骼层级,也就是把变换的组合矩阵存入 D3DXFRAME_EX

20、 结构的 matCombined 成员中,用于后面的渲染。下面的函数应该增加到 D3DXFRAME_EX 结构中,如下: void UpdateHierarchy(D3DXMATRIX *matTransformation = NULL) D3DXFRAME_EX *pFramePtr; D3DXMATRIX matIdentity; / 如果为空,用一个全同矩阵 if(!matTransformation) D3DXMatrixIdentity( matTransformation = / 把变换矩阵组合到 matCombined 中 matCombined = TransformationMatrix * (*matTransformation); / 更新兄弟层级 if(pFramePtr = (D3DXFRAME_EX*)pFrameSibling) pFramePtr-UpdateHierarchy(matTransformation); / 更新孩子层级 if(pFramePtr = (D3DXFRAME_EX*)pFrameFirstChild) pFramePtr-UpdateHierarchy( 现在 matCombined 储存着每个骨骼相对于原点的变换矩阵,然后只要把各个顶点附在相应的骨骼上,就能渲染了。

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 文学艺术 > 动画

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报