MD5模型的格式、导入与顶点蒙皮式骨骼动画I

MD5模型是ID公司第一款真正意义上的骨骼格式模型,在04年随着Doom3一起面世,经过几个版本的变更,现在在骨骼模型格式中依然有其重要地位。本文记录一下ZWModelMD5中的一些细节,先是稍微笔记一下骨骼模型的基本概念和MD5文件的格式与导入。——ZwqXin.com

[MD2格式模型的格式、导入与帧动画]

[MD3模型的格式、导入与骨骼概念动画]

本文来源于 ZwqXin (), 转载请注明原文地址:

经过MD2的帧动画和MD3的骨骼概念动画,当然还有MD4/MDL的尝试,在那个骨骼模型开始风行的时代,MD5作为骨骼动画出现了。在今天,3D模型通常分为静态模型、帧动画模型、骨骼动画模型,它们分别应用于不同的场合,静态模型就不用说了,帧动画模型主要用于人物动作简单、固定、与场景不怎么需要交互的场合,而骨骼动画模型就是与此相对了。

骨骼的这个概念与我们人体的骨骼还是类似的。我们可以把自己看做一堆骨骼,然后外面蒙上一层肌肉啊皮啊什么的,然后这些肌肉啊皮肤啊的就跟随骨骼的运动而运动。当然了,重要的是我们体内还有那么多器官,那些MD5人体和怪物模型就没有了(笑)。骨骼与骨骼之间是用骨骼节点连接的,我们称骨骼为Bone,称骨骼节点为Joint,一根bone的一端或两端连着两个Joint,而一个Joint可能连着数条Bone。骨骼模型的描述也分为以Bone为主和以Joint为主,MD5是后者。你可以认为Joint就是控制点,通过控制Joint的位置和旋转,可以控制整个骨骼,而整个骨骼也就影响模型的外皮(顶点网格),于是动画模式建立了。Joint的集合可以用一个树的数据结构描述——跟MD3一样,有一个总的父节点,总的父节点下连着一个或多个子节点,这些子节点本身也作为父节点下连一个或多个子节点……父节点的移动直接先作用到子节点上(抬动肩关节时手臂节点也跟着作同样的运动,之后手肘节点跟着手臂节点作同样移动……类推到指尖节点),再叠加上子节点本身的移动(手臂节点本身可以再那基础上作移动,其影响共同作用到手肘节点……用身体摆摆姿势,这其实是很形象的),于是这个前向的驱动模式建立了。每个Joint的运动信息可以抽象成一个变换矩阵M([乱弹OpenGL中的矩阵变换(上)] ),这样这个驱动模型可以看做是每个时刻给予每个节点一个变换矩阵,变换节点的位置和旋向以驱动骨架。

既然骨架模型建立了,接下来就是骨架与模型顶点数据的关系。骨骼模型本身渲染出来的不是骨架,而是组成网格(皮肤)的一堆顶点。这堆顶点是怎样定义的呢?在MD2中,每帧都包含一堆顶点位置数据,结果就是程序需要存储大规模的顶点位置数据。MD5则不直接储存顶点位置数据,而是让程序每帧”计算“出来。在MD5的文件中的网格数据包括纹理坐标(因为最后的顶点数目是固定的,做一纹理坐标数据的数目与之一致)、索引(把顶点组成三角面片,因为最后的顶点是有序的,前一帧的顶点跟后一帧的一一对应,所以只要按这个次序定义索引即可)、节点权重(weight,这就是关联骨骼节点跟顶点的东西,下述);一个顶点数据有一个纹理坐标、一个或多个weight组成,然后索引数据组织顶点。与以往不同的是,这里面没有法线数据,MD5采用的是与3DS([用Indexed-VBO渲染3DS模型] )和OBJ([OBJ模型文件的结构、导入与渲染Ⅰ] )一样的策略,让程序自己去计算。

一个weight包含了它对应的Joint的索引(这样一来就建立了 vertex->weight->joint的连接),一个位置值(pos)和一个作用比率(bias)。一个顶点的计算公式如下:

].bias)

其中,MJ-x表示第x个weight对应的节点Joint的变换矩阵。作用比率bias的总和需要是1(100%),这样一个顶点位置可以看作是各个经过矩阵变换后的weight位置的加权平均。而这个Joint矩阵在动画过程中变化的话,结果就是对应计算出来的顶点位置也跟着变化了。这就是骨架驱动皮肤的过程,也称为”蒙皮“。这步计算可以在CPU上执行,也可以在GPU上执行——通过vertex shadr执行蒙皮,就称为”顶点蒙皮(vertex skinning)“,我将在下篇文章讲述。

一个MD5模型包含两个文件,其中.md5mesh后缀的文件包含了该模型的几何体数据(mesh),而.md5anim后缀的文件则包含了该模型的动画信息。这一点与MD3模型是一样的,只不过很多方面看上去更为规范,没有在[MD3模型的格式、导入与骨骼概念动画]文末提及的那些令人不爽的“小提示"。另一点很本质上不同的是,md5的两个文件都是文本文件,这当然提供了更大的方便性,但同时也容易出现文件被乱改的问题(当然了,本来idSoft就只是想自用而已)。

一个MD5可以只有md5mesh文件,这样模型只不过不含动画信息而已。而这时候出来的模型的姿态被称为Bind-pose。以前看视频看人用maya建模(就是看那部《堕落的艺术》的幕后花粹时),在修改模型,未定义动作之前,人物会呈现一个站立并两手平举的姿态。这就是一个模型的bind-pose姿态吧。这个概念在顶点蒙皮过程中尤显重要,不过你只需要记住这就是没有动画信息(没有md5anim)时候给予模型的一个”预设姿势“好了。下面看看文件结构

md5mesh:

那些版本号啊XX总数的就不管了,从md5mesh文件开头看起,首先是Joint的定义:名称、父节点序号(-1说明本身是总父节点,这个序号其实就是行号了,譬如上面”origin“节点的序号就是0,无父节点; "body"节点序号是1,父节点序号是0,也就是说父节点是”origin“)、bind-pose姿态下节点的位置(位移)和旋转(旋转用四元数【[GimbalLock万向节锁与四元数旋转] 】表达,括号里是xyz,需程序自行计算w值)——后面两者可以组成一个变换矩阵Mself-bindpose,即bindpose姿态下各个节点自身的变换矩阵,如果给这个矩阵依次向上左乘该节点的树分支上各级父节点的变换矩阵,得到就是bindpose下该节点的真正变换矩阵MJ-x(bindpose)了。

md5mesh:

md5mesh文件后面部分就是一个个网格对象(mesh)的数据了。看上面注释,跟前面的讲述是一致的。注意这里vert末尾两个数据是对应下面那堆weight的,而且总是相邻的一个或多个weight,所以只需要第一个的序号和连续的weight的个数就可以确定了。顶点仅会被附近的weight影响。

接下来看md5anim:

hierarchy{ //Joint 名字 父节点序号 flag 影响的帧数据起始索引frame0{ //帧0数据

有多少和我一样,坐在不足平米的空间里,

MD5模型的格式、导入与顶点蒙皮式骨骼动画I

相关文章:

你感兴趣的文章:

标签云: