(转)OGRE中的人脸动画
[前言:在Ogre的Facial Demo中采用了顶点动画(确切点说是Pose Animation)让面部产生面部表情及发音动画,本文简单介绍了顶点动画的概念,并结合Facial Demo,探讨如何实现Pose Animation。]
顶点动画是直接使用顶点让网格(mesh)产生动画的一种方式。每一组动作在顶点动画中对应一个顶点数据实体。顶点动画被存储在.mesh文件中,因为它与网格的顶点紧密相连。实际上,顶点动画分为两种子类型。
1、Morph Animation(变形动画)
变形动画依靠在每个关键帧中及其之间保存和插入顶点绝对位置的快照(snapshot)。当骨骼动画不能恰当处理动画对象时,变形动画会比较有用。在分物体必须从根本改变动画部分的结构和形状时,骨骼动画就不适合了。
由于绝对位置数据的使用,不可能在同样的顶点数据中混合多于一个的变形动画;如果你想使用动画混合,你应该使用骨骼动画,因为它更有效。如果你激活了在同一个顶点数据中包含的多个动画,只有最后一个有效。也就是说动画状态的"weight"选项不能被用于变形动画。
变形动画与骨骼动画能够组合应用(参考 Ogre Manual 8.3.3 Combining Skeletal and Vertex Animation)。同时,变形动画也能在使用顶点渲染器(shader)的硬件中实现(参考Ogre Manual Morph Animation in Vertex Programs)
2、Pose Animation(姿态动画)
姿态动画允许将多个潜在地不同效果等级的顶点姿态(vertex poses)混合成最终的顶点状态。该动画通常用于面部动画,在这种动画中每一个面部表情被作为一个独立的动画,我们可以将一个表情混合在另外一个上,如果每个姿态只影响面部的一部分,也可以组合所有的表情。
为了产生姿态动画,需要引用预先包含在mesh中的一套动作集,这些动作集采用与源顶点的偏移量来表示。但是并不要求每一个顶点都有偏移量,当用软件处理这些数据时,没有偏移量的顶点会被忽略掉。如果用硬件处理的话,没有偏移量的顶点会自动被填充为0。
一旦定义好了姿态,你就可以在动画中引用他们。每一个姿态动画迹(pose animation track)对应着一个单独的几何数据集合(或者是与物体网格对应的,或者是其中一个子网格的),在动画中的每个关键帧可以引用一个或多个姿态(pose),且每一个有其相应的影响值(influence)。你能定义许多关键帧,使用多种姿态的混合,从而产生多个部位协调运动的动画。
应该小心多个姿态同时应用的情况。当在硬件中处理姿态动画的时候(参考Ogre Manual Pose Animation in Vertex Programs),每个激活的动作都需要额外的顶点缓冲器加入到渲染器(shader)中,如果采用软件来处理动画,你处理的激活姿态越多消耗的时间也就越长。也就是说,在一个关键帧中如果有两个姿态,在下一帧中也有两个,那么在它们过渡之间实际上就有4个激活的关键帧。
你可以综合应用姿态动画和骨骼动画,参考 Ogre Manual 8.3.3 Combining Skeletal and Vertex Animation,你也可以采用硬件加速那些混合顶点渲染器(shader)的应用程序(参考Ogre Manual Pose Animation in Vertex Programs)。
3、Pose Animation xml结构
<mesh>
<submeshes>
<submesh material="子网格材质" ...>
<faces count="面数">
<face ... />
</faces>
<geometry vertexcount="顶点数">
<vertexbuffer ...> // 顶点缓冲器内容
<vertex>
<position 顶点位置>
<normal 顶点法向量>
<texcoord 纹理坐标>
</vertex>
</submesh>
</submeshes>
<submeshnames> // 主要是为pose和animation提供索引号
<submeshname name="子网格的名字" index="子网格的索引号(从0开始)">
</submeshnames>
<poses> // 这些参数主要是对手调动画有效
<pose target="submesh or mesh" index="对应哪个网格的姿态" name="姿态的名字">
<poseoffset index="顶点索引" x y z相对偏移量 />
</pose>
</poses>
<animations> // 下面这些动画对于自动播放动画有效
<animation name="动画名字" length="帧长度">
<tracks> // 动画迹
<track target="submesh or mesh" index="姿态索引号" type="动画类型,这里是pose">
<keyframes> // 关键帧
<keyframe time="时间">
<poseref poseindex="pose索引" influence="影响值[0-1]" />
</keyframe>
</keyframes>
<track>
</tracks>
</animation>
</animations>
</mesh>
4、如何实现
4.1 建模工具对Pose Animation的支持
Facial Demo中所用的头部模型是由SoftImage授权使用的XSI面部动画模型,不是所有的导出工具都支持Pose Animation,目前有 SoftImage XSI 5.0 Exporter v1.2.3、oFusion Pro for 3ds max、Maya Ogre导出器支持。
4.2 关键顶点的寻找
通过分析xml中的poseoffset偏移量,可以看到许多非常小的数据,这些数据是由模型导出工具产生的,其实它们完全可以被忽略掉。通过实验,我将原来604个顶点的poseoffset数据减少为75个,而且效果和原来相差不大,说明这75个poseoffset对应的顶点是该动画的关键顶点。
4.3 在程序中如何读取Pose Animation
下面是包括在场景创建函数createScene中创建动画的代码:
// 载入包含pose animation的mesh
MeshPtr mesh = MeshManager::getSingleton().load("aaa.mesh", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// 创建动画将其命名为smile,并将长度初始化为0
Animation* anim = mesh->createAnimation("smile", 0);
// 创建顶点动画迹,这里只有针对索引值为0的Pose动画,故第一个参数为1[1]
// 这些动画pose被定义在<poses>标签中
VertexAnimationTrack* track = anim->createVertexTrack(1, VAT_POSE);
// 创建手动动画,并将关键帧起始位置置为0
manualKeyFrame = track->createVertexPoseKeyFrame(0);
// 创建手动动画所用到的poses引用,并初始化为0
manualKeyFrame->addPoseReference(0, 0.0f);
// 创建物体(这里是Dr. Bunsen的头像)
Entity* head = mSceneMgr->createEntity("Head1", "aaa.mesh");
// 从mesh中得到名为action的动画参数(在<animations>标签中定义)
actionAnimState = head->getAnimationState("action");
// 是否允许自动播放该动画
actionAnimState->setEnabled(true);
// 获取先前从mesh文件中得到的动画smile,并将关键帧起始位置置为0
manualAnimState = head->getAnimationState("smile");
manualAnimState->setTimePosition(0);
下面在是每一帧渲染开始都会调用的frameStarted函数中自动播放动画代码:
// 根据自上一帧以来所消耗的秒数来修改调整关键帧位置
actionAnimState->addTime(evt.timeSinceLastFrame);
--------------------------------------------------------------------------------
[1] Ogre API指出createVertexTrack第一个参数的含义为: Handle to give the track, used for accessing the track later. Must be unique within this Animation, and is used to
identify the target. For example when applied to a Mesh, the handle must reference the
index of the geometry being modified; 0 for the shared geometry, and 1+ for SubMesh
geometry with the same index-1.
顶点动画是直接使用顶点让网格(mesh)产生动画的一种方式。每一组动作在顶点动画中对应一个顶点数据实体。顶点动画被存储在.mesh文件中,因为它与网格的顶点紧密相连。实际上,顶点动画分为两种子类型。
1、Morph Animation(变形动画)
变形动画依靠在每个关键帧中及其之间保存和插入顶点绝对位置的快照(snapshot)。当骨骼动画不能恰当处理动画对象时,变形动画会比较有用。在分物体必须从根本改变动画部分的结构和形状时,骨骼动画就不适合了。
由于绝对位置数据的使用,不可能在同样的顶点数据中混合多于一个的变形动画;如果你想使用动画混合,你应该使用骨骼动画,因为它更有效。如果你激活了在同一个顶点数据中包含的多个动画,只有最后一个有效。也就是说动画状态的"weight"选项不能被用于变形动画。
变形动画与骨骼动画能够组合应用(参考 Ogre Manual 8.3.3 Combining Skeletal and Vertex Animation)。同时,变形动画也能在使用顶点渲染器(shader)的硬件中实现(参考Ogre Manual Morph Animation in Vertex Programs)
2、Pose Animation(姿态动画)
姿态动画允许将多个潜在地不同效果等级的顶点姿态(vertex poses)混合成最终的顶点状态。该动画通常用于面部动画,在这种动画中每一个面部表情被作为一个独立的动画,我们可以将一个表情混合在另外一个上,如果每个姿态只影响面部的一部分,也可以组合所有的表情。
为了产生姿态动画,需要引用预先包含在mesh中的一套动作集,这些动作集采用与源顶点的偏移量来表示。但是并不要求每一个顶点都有偏移量,当用软件处理这些数据时,没有偏移量的顶点会被忽略掉。如果用硬件处理的话,没有偏移量的顶点会自动被填充为0。
一旦定义好了姿态,你就可以在动画中引用他们。每一个姿态动画迹(pose animation track)对应着一个单独的几何数据集合(或者是与物体网格对应的,或者是其中一个子网格的),在动画中的每个关键帧可以引用一个或多个姿态(pose),且每一个有其相应的影响值(influence)。你能定义许多关键帧,使用多种姿态的混合,从而产生多个部位协调运动的动画。
应该小心多个姿态同时应用的情况。当在硬件中处理姿态动画的时候(参考Ogre Manual Pose Animation in Vertex Programs),每个激活的动作都需要额外的顶点缓冲器加入到渲染器(shader)中,如果采用软件来处理动画,你处理的激活姿态越多消耗的时间也就越长。也就是说,在一个关键帧中如果有两个姿态,在下一帧中也有两个,那么在它们过渡之间实际上就有4个激活的关键帧。
你可以综合应用姿态动画和骨骼动画,参考 Ogre Manual 8.3.3 Combining Skeletal and Vertex Animation,你也可以采用硬件加速那些混合顶点渲染器(shader)的应用程序(参考Ogre Manual Pose Animation in Vertex Programs)。
3、Pose Animation xml结构
<mesh>
<submeshes>
<submesh material="子网格材质" ...>
<faces count="面数">
<face ... />
</faces>
<geometry vertexcount="顶点数">
<vertexbuffer ...> // 顶点缓冲器内容
<vertex>
<position 顶点位置>
<normal 顶点法向量>
<texcoord 纹理坐标>
</vertex>
</submesh>
</submeshes>
<submeshnames> // 主要是为pose和animation提供索引号
<submeshname name="子网格的名字" index="子网格的索引号(从0开始)">
</submeshnames>
<poses> // 这些参数主要是对手调动画有效
<pose target="submesh or mesh" index="对应哪个网格的姿态" name="姿态的名字">
<poseoffset index="顶点索引" x y z相对偏移量 />
</pose>
</poses>
<animations> // 下面这些动画对于自动播放动画有效
<animation name="动画名字" length="帧长度">
<tracks> // 动画迹
<track target="submesh or mesh" index="姿态索引号" type="动画类型,这里是pose">
<keyframes> // 关键帧
<keyframe time="时间">
<poseref poseindex="pose索引" influence="影响值[0-1]" />
</keyframe>
</keyframes>
<track>
</tracks>
</animation>
</animations>
</mesh>
4、如何实现
4.1 建模工具对Pose Animation的支持
Facial Demo中所用的头部模型是由SoftImage授权使用的XSI面部动画模型,不是所有的导出工具都支持Pose Animation,目前有 SoftImage XSI 5.0 Exporter v1.2.3、oFusion Pro for 3ds max、Maya Ogre导出器支持。
4.2 关键顶点的寻找
通过分析xml中的poseoffset偏移量,可以看到许多非常小的数据,这些数据是由模型导出工具产生的,其实它们完全可以被忽略掉。通过实验,我将原来604个顶点的poseoffset数据减少为75个,而且效果和原来相差不大,说明这75个poseoffset对应的顶点是该动画的关键顶点。
4.3 在程序中如何读取Pose Animation
下面是包括在场景创建函数createScene中创建动画的代码:
// 载入包含pose animation的mesh
MeshPtr mesh = MeshManager::getSingleton().load("aaa.mesh", ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME);
// 创建动画将其命名为smile,并将长度初始化为0
Animation* anim = mesh->createAnimation("smile", 0);
// 创建顶点动画迹,这里只有针对索引值为0的Pose动画,故第一个参数为1[1]
// 这些动画pose被定义在<poses>标签中
VertexAnimationTrack* track = anim->createVertexTrack(1, VAT_POSE);
// 创建手动动画,并将关键帧起始位置置为0
manualKeyFrame = track->createVertexPoseKeyFrame(0);
// 创建手动动画所用到的poses引用,并初始化为0
manualKeyFrame->addPoseReference(0, 0.0f);
// 创建物体(这里是Dr. Bunsen的头像)
Entity* head = mSceneMgr->createEntity("Head1", "aaa.mesh");
// 从mesh中得到名为action的动画参数(在<animations>标签中定义)
actionAnimState = head->getAnimationState("action");
// 是否允许自动播放该动画
actionAnimState->setEnabled(true);
// 获取先前从mesh文件中得到的动画smile,并将关键帧起始位置置为0
manualAnimState = head->getAnimationState("smile");
manualAnimState->setTimePosition(0);
下面在是每一帧渲染开始都会调用的frameStarted函数中自动播放动画代码:
// 根据自上一帧以来所消耗的秒数来修改调整关键帧位置
actionAnimState->addTime(evt.timeSinceLastFrame);
--------------------------------------------------------------------------------
[1] Ogre API指出createVertexTrack第一个参数的含义为: Handle to give the track, used for accessing the track later. Must be unique within this Animation, and is used to
identify the target. For example when applied to a Mesh, the handle must reference the
index of the geometry being modified; 0 for the shared geometry, and 1+ for SubMesh
geometry with the same index-1.