Axiom3D:Ogre动画基本流程与骨骼动画
在Axiom中,Animation类用于管理动画,在此对象中主要管理着AnimationTrack对象,此对象用于管理动画的各种类型的每一桢.在Axiom中,动画类型主要有变形动画,姿态动画,骨骼动画以及他们之间的混合.而此对象的各子类如NodeAnimationTrack常用于骨骼动画.而VertexAnimationTrack常用于顶点动画(包括变形动画与姿态动画),还有一种是NumericAnimationTrack用于扩展动画功能.而KeyFrame管理一桢,主要属性是当前桢的位置(time).AnimationTrack子类与KeyFrame子类有对应关系,其中NumbericAnimationTrack对应NumericKeyFrame,NodeAnimationTrack对应的是TransformKeyFrame,先说一句,Bone这个类就是继承的Node这个类.而VertexAnimationTrack对应VertexMorphKeyFrame(变形动画桢)和VertexPoseKeyFrame(姿态动画桢).可以简单看下,TransformKeyFrame对应骨骼桢,在骨骼动画中,每桢不同的是骨骼的位置,所以他增加(Rotaton,Scale,Translate)用来表示骨骼的位置变动.而变形动画桢每桢都有不同的顶点集合,所以他增加了VertexBuffer用来表示顶点集合.
如上面图中,AnimationTrack对象主要管理多个桢(KeyFrame),在AnimationTrack中提供一个关键方法,根据GetKeyFrameAtTime,传入一个时间点,获取这个时间点在动画中的前一桢与后一桢,以及在这二桢之间的比例.还提供一个抽象方法Apply,供子类实现,让子类本身AnimationTrack结合对应的子类KeyFrame得到正确的展张属性.简单来说,GetKeyFrameAtTime当前桢的位置,而Apply是得到当前桢上的数据.
简单说下变形动画,在VerterAnimationTrack这个类,其中他的属性AnimationType指出对应实体是变形动画还是姿态动画,变形动画就如MD2动画格式,请参看MD2关键桢动画3D模型加载.在这里面,对应的VertexMorphKeyFrame用HardwareVertexBuffer保存了每一桢的数据.根据父类前面的GetKeyFrameAtTime得到当前所需要的二桢,然后实现父类Apply混合二桢正确以得到正确的HardwareVertexBuffer,在Axiom中,可以分别交给GPU处理和CPU处理,二者的实现过程一样,根据二桢以及在二桢间的位置来计算混合桢的数据,交给GPU的话,在此处只需传入二个桢的数据,以及二桢间的位置,上面的实现交给相关着色器代码实现.姿态动画了解不多,相关代码可以参考ApplyToVertexData与ApplyPoseToVertexData,大致意思应该混合每桢里的参考,和骨骼动画有些类似,会有权重,顶点间偏移信息.姿态动画多用于面部表情.
再来说下骨骼动画,可以先参考MD5骨骼动画模型加载, 骨骼动画用到的控制类是NodeAnimationTrack,他对应用的动画桢是TransformKeyFrame,不同顶点动画,根据线性插值当前二桢与二桢位置得到当前桢,在NodeAnimationTrack中,求得当前桢需要父类Animation里定义的二种插值算法,分别对应是旋转插值与常用插值.常用插值分为线性插值与平滑插值.旋转插值分为线性插值与球面插值.实际上旋转插值实用球面插值相当于常用的线性插值,我们简单想一下,旋转留下的曲线是一条球面曲线,他在单位时间内走的是球面,而不是线性的.这二种算法分别对应的是Quaternion里的Nlerp与Slerp,具体讲解一些网站都有详细介绍.不同的插值算法会有不同的结果,然后和上面的VerterAnimationTrack一样,在Apply里当前桢结合Node中计算骨骼坐标.
Node这个类比较重要,这里不详细说,只说一些和此处有关的,Node里面包含自己的集合类,就是树那一类型的结构,Node里Orientation,Position用来表示他自己的方法,有方法Transform与Rotate,默认这二个方法都是针对父Node来进行操作的.而Bone继承Node,也继承了这些元素.
重温下MD5骨骼动画模型加载,大致过程如下,静态文件包含一些mesh,mesh由一些三角形(面,相当于顶点索引),而每个顶点索引是关联上对应1-4个权重点,权重点有实际的位置,并关联到对应骨骼上.而在动画文件中,每桢中多个骨骼随着改变位置,相应的权重点跟着改变,跟着顶点变,顶点构成的面也随之变化了.
而Ogre中相应骨骼动画过程也差不多,我们可以理解MD5中的静态文件是Ogre中的mesh文件,在Axiom3D:Ogre中Mesh文件格式分析(一)在mesh文件中会保存面(顶点索引),顶点(实际位置),顶点与骨骼关联对象VertexBoneAssignment(顶点索引,骨骼索引,权重).其实这里的顶点就相当于MD5中的权重点,而在VertexBoneAssignment记录了对应的顶点与骨骼的联系.其实是和MD5算起来是一样的.
而在MD5中的动画文件是Ogre中的skeleton,在MD5中,每桢记录了不同的骨骼位置,Ogre中的skeleton也是如此,记录了每桢的不同位置.不同MD5的是,他每个骨骼桢的记录是单独分开的,如骨骼点一有八桢,而骨骼点二有十桢.MD5所有的骨骼点是一起记录的.
可能这么说有些不清楚,先看下相关的Ogre中的带骨骼动画的.mesh文件和skeleton的文件.(本来为二进制文件,图是转成的XML文件,请看OgreXmlConverter)
如上中,相关节点与代码关系.
类对象 | 节点 | 备注 |
IndexData | faces | 顶点索引 |
VertexData | geometry | 顶点数据 |
VertexBoneAssignment | boneassignments | 顶点,骨骼,权重信息 |
Animation | animation | length,动画时间 |
NodeAnimationTrack | track | node指向那个骨骼 |
TransformKeyFrame | keyframe | translate,rotate指定位置 |
结合整个处理过程,Ogre中骨骼动画会分别加载对应mesh文件与skeleton文件.在代码中骨骼动画是Entity对象,加载文件后,里的Mesh对象保存有顶点,纹理坐标,顶点索引,骨骼与顶点索引及权重信息.而Mesh关联的Skeleton会保存所有的Bone信息,以及每个Bone的NodeAnimationTrack.然后会调用Mesh中的CompileBoneAssignments,其中会编历Mesh的boneAssignments,把各个顶点关联的骨骼,权重存入VertexData中,大约如下操作,对应的VertexDeclaration增加一个4个字节(骨骼索引),4个float(4个权重),然后添加一个新的HarderVertexBuffer(OpenGL中),就是上面的骨骼索引与权重(4Byte+4float).至于为什么是4,这是因为每个顶点最多关联4个骨骼,如果过多,前面会对多余的去除,并重新分配权重最大的四个骨骼,不到4的部分都填充0.在这初始化过后就是动画部分.在Entity里的UpdateAnimation方法转到mesh中的skeleton中的NodeAnimationTrack,如变形动画中,GetKeyFrameAtTime得到当前桢的位置.而Apply会根据当前桢的位置求得当前骨骼位置(TransformKeyFrame中的translate,rotate.骨骼位置是在父节点的基础上求得,请看Node相关实现),在这里,如果是用CPU的方式计算顶点,就会调用Mesh的SoftwareVertexBlend,里面会对所有顶点执行BlendPosVector,这个过程就是上面说的MD5中的,骨骼位置变了(SoftwareVertexBlend方法中的matrices,代表各个骨骼的位置),然后每个顶点根据关联的骨骼与权重求得实际的位置.
可以看到,Ogre中的骨骼动画格式与MD5的区别不大,处理Ogre与我原来中的MD5关于顶点混合计算有些不同,Ogre的这种处理能很好的实现CPU与GPU处理共用大部分代码.