Array王锐力作:osg与PhysX结合系列内容——第5节 角色动画效果(下)

〖Array王锐大神力作〗osg与PhysX结合系列内容——角色动画效果(下)

角色动作的过渡切换

我们在研究下一步的高级角色动作之前,先看一下ozz的角色运动更新机制,这样也可以为我们后续“魔改”这个引擎的内部逻辑打下更好的基础。

ozz的动画更新流程主要分为下面四个步骤:

  • 动画数据的采样:即根据上一帧到这一帧的时间间隔,计算动画应该经过的时间,进而从关键帧插值解算出每个关节点的局部坐标。
  • 多个动画的融合:将多个动画采样的结果按照独立的权重,融合在一起,得到最终叠加后每个关节点的局部坐标。这个步骤对于角色动画的过渡效果实现是至关重要的。
  • 骨骼数据的更新:将之前解算得到的角色动画关节点的局部坐标,匹配到实际角色的骨骼上,并计算得到骨骼坐标系(“世界”坐标系)下每个关节的实际位置矩阵。
  • 蒙皮和模型网格的更新:根据每个关节位置的变化,以及关节与模型各顶点之间的权重关系(即蒙皮),计算每个顶点更新后的位置坐标,以及法线结果,更新到模型网格上。

ozz将这套流程中的每个步骤都封装成独立的工作线程,也就是一个独立的Job。ozz自己负责每个Job的并行执行和效率效果,而各个步骤之间输入和输出的中间数据,我们随时都可以介入修改或者替换,这也使得ozz成为了一个非常灵活而且强大的中间件工具。

角色动画高级技巧

如图所示就是整个ozz系统大致的工作流程。注意SoA的含义是“Struct of Array”,在这里也就是一组关节数据的意思。

在这里插入图片描述
ozz允许多个动画被同时载入和采样,然后将它们计算的结果融合到一起。当然,每个动画应当有不同的权重值设置。一般来说,所有动画的权重之和不应该超过1.0,否则可能会有一些奇怪的结果出现。

从工作流程图中可以看出,每个动画都会用到一个独立的SamplingJob采样器,它会在动画数据的时间线上选择一个时刻,并计算该时刻对应的各关节位置姿态数值。

每个采样器输出的位置姿态结果,合并到BlendingJob中,按照不同的权重混合,就可以得到介于两个动画效果之间的一组新的位置姿态值。

采样器的权重值即可以全局设置,也可以根据每个关节来独立设置,因此可以实现精准的动画融合策略。

我们先来考虑全局权重设置的典型应用场景:假设有两个动画采样器,分别对应于“站立”(idle)和“走路”(walking)。

从站立姿态切换到走路姿态,或者反之的时候。如果直接切换,那么骨骼关节肯定会从当前的位置姿态突变到一个新的姿态。这个过程显得比较突兀,有必要加入一个合理的过渡过程。

考虑从站立姿态切换到走路姿态,过渡时间为0.5s。那么所谓的过渡实质上就是:idle采样器的权重值从1到0,而walking采样器的权重值从0到1。每个时刻t的动画融合结果应该是:

  • sampler[idle] * (1.0 – t / 0.5) +
  • sampler[walking] * (t / 0.5),其中t∈[0, 0.5]

实现的过渡过程如视频所示。在osgPhysX中这一切都是通过select()和seek()函数结合动画和权重参数配合完成,此处不再赘述。

角色动画效果-part1

简单的方法,无疑能够让我们的动画系统增色不少。那么下一步我们再看看有没有更高级的动画技巧可用。

1、分部融合(Partial Blend)

  • 考虑两个动画采样器,例如“走路”(walking)和“手枪瞄准”(pistol_aiming),它们各自有独立完整的动画流程,直接融合在一起肯定是不伦不类的。
  • 但是在实际应用中显然存在着这样一类需求:角色没有持枪的时候双手下垂,跑步的时候则是摆臂姿势;但是如果角色同时持枪瞄准,则无论站立还是跑步的时候,都需要做出举枪瞄准的手势,虽然这个时候角色下半身的运动姿态通常是不变的。如果为所有“持枪”和“未持枪”的动作都制作两套动画,那么无疑是一种资源的浪费。因此,前文提出的“根据每个关节来独立设置权重”的方案在这里就非常有价值了。
  • 我们可以设置walking采样器中,所有脊柱(spine)之下的关节的独立权重都是1.0,而脊柱之上的关节权重都是0.0;同理,pistol_aiming采样器中,脊柱之下的关节权重都是0.0,而脊柱之上的关节权重是1.0,从而得到这种融合的效果,如视频所示。

角色动画效果-part2

  • osgPhysX中通过selectPartial()函数来实现这类复杂的权重设置。

2、附属物品(Attachment)

  • 附属物品,顾名思义就是随着人体某个关节运动的物品。例如拿在手中的手枪和棍棒,或者别在腰间的腰带,手机,抑或头上戴的帽子和脖子上绕着的披风。
  • 附属物品的设计可以复杂(例如柔体,衣物,或者和多个关节关联)也可以简单,不过它们都需要一个最基本的信息,就是被附属的关节在当前时刻的位置和姿态。
  • 这个位置姿态矩阵属于角色自身的模型坐标系,也就是以角色根节点为原点的坐标系统。以Mixamo的角色为例,角色模型坐标系的原点位于站立姿态时双脚在地面的中心点,Y+方向为角色向上的方向,Z+方向为角色向前的方向。
  • 如视频所示,osgPhysX提供了专门的封装函数getModelSpaceJointMatrix()来实时获取任何关节的模型坐标系矩阵,注意一定要在update()和update*IK()之后再执行这个函数,否则得到的矩阵值可能不是最新的。

角色动画效果-part3

3、反向运动学(IK)

  • 反向运动学,简单来说就是通过子骨骼关节的目标位置姿态来反推算上级父关节的位置姿态。这样得到的结果可能有多个,但是我们可以添加更多的约束条件来更精准的结果。
  • IK算法有很多的分支,例如Unreal中的TwoBoneIK,FABRIK,CCDIK等。ozz本身实现了TwoBoneIK(定义三个关节为一组,通过末端关节的运动推算前两个关节)和AimIK(定义一组多个关节和权重,通过末端点的运动位置和方向,推算各个关节的位置姿态)。
  • osgPhysX中通过updateAimIK()和updateTwoBoneIK()两个函数来封装ozz的IK功能,如视频所示,因为部分参数设置和调优的问题,在某些角度IK肯定还是存在变形,但是大多数情况下已经可以用于具体的应用需求了。

角色动画效果-part4

构建测试场景并运行

我们在osgPhysX/tests/character_animation_test.cpp中实现了上述所有的动画功能,这样至少能够让我们的系统看起来像是一个高级游戏角色引擎的原型了。

程序代码中,首先通过loadAnimations()加载所有的预设动画:这些动画都来自于mixamo,并保存到osgPhysX/utils/ozz/tools/data目录下,我们将所有的动画ozz文件都保存到执行程序目录中的一个子文件夹animations中,并且将skeleton和mesh的ozz文件放在执行目录下。
在这里插入图片描述
在这里插入图片描述
程序运行时,可以通过PageUp/PageDown来切换显示不同的动画效果,每次切换中间已经做了0.5秒的过渡。

可以通过Home键来显示PartialBlend的效果,即当前动画效果与持枪效果的叠加;按End键恢复正常状态。

可以通过Insert/Delete键来显隐IK效果,并通过数字键0-4切换不同的IK部位(头/手/脚)跟随鼠标运动。

按Tab键之后,角色会始终手持一个圆柱体(用来演示Attachment功能),无论他在什么动画状态下。
更多的动画功能,就有待读者去挖掘了,下一章我们还是回归物理引擎比较好。

数据驱动的角色运动

因为ozz的存在,我们完全有可能通过外部输入的骨骼数据来驱动一个动画角色。只要能够提供每个关节在角色模型坐标系下的位置和姿态数据,就可以通过外部的数据来驱动角色的实时肢体运动。

这里所说的外部数据包括但不限于Microsoft Kinect,运动捕捉设备,或者一些比较新的AI运动预测算法(例如OpenPose+3dbaseline)等。对于osgPhysX而言,只需要逐帧通过setModelSpaceJointMatrix()来设置关节数据;另一种方案是只采集头部,双手和双脚的IK数据,然后推算中间的关节姿态。不过相关的实现案例,暂时并不属于本教程所关注的内容。

至此,我们花了巨大的篇幅,在实现和讲解物理引擎的同时,重新塑造了基于osg的角色动画系统,并且让它有了完整的工作流程(从Mixamo到ozz,再到osg中显示),以及支持多种常用的角色动画技巧(动画过渡,动作叠加,附属物,IK等)。

也许后续我们会将这个角色动画系统从osgPhysX中独立出来,单独开源和加以改造应用;又或者有感兴趣的朋友可以加入到osgPhysX的贡献工作中,或者单独摘出这部分代码进行魔改,将角色系统调优和发扬光大。不过截至目前,它还只是一个独立的类(osgPhysicsUtils::PlayerAnimation),以及一个独立的例子罢了。

更多的可能性,就随着这个教程的延续(不过我们下一章会开始一个全新的话题),以及随着时间,慢慢发酵吧~

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片