HuldaGnodim 发表于 2021-12-21 07:52

Unity动画TA:不用插件实现物理驱动动画

EA的星战GDC分享开头就是对比物理驱动动画和原动画的效果。有大佬把这个视频搬上了b站,搜索视频标题Physical Animation in Star Wars Jedi_ Fallen Order可以观看该视频。



EA的Star Wars物理动画GDC分享的开头

这个开头想表示,不用物理驱动的动画会穿墙,用了物理驱动的动画因为有了碰撞和约束,所以不会穿墙。这就是我目前想制作的效果。
从前辈们的GDC分享里得到的

为了制作出漂亮的物理动画效果,我们应该:
增加物理运算的迭代次数。Unity中打开项目设置,把图中的两个参数由默认的6和1改成64和32。这两个数值是上面的GDC视频里推荐的。


使用无摩擦的物理材质避免抖动。
除了以上两点之外,Star Wars还总结出了一小堆设置物理动画的经验,比如“关掉CCD”之类的,可能只有基于PhysX的实现才要注意的东西。



Star Wars的物理动画经验总结

如想知道全部,请移步原视频。
骨骼刚体质量逐级递减。来自顽皮狗神秘海域4的分享,在EA的分享里也有提及。
同时使用Motor Controller和Keyframed Controller。这两个概念来源于神秘海域4的分享 Physics Animation in Uncharted 4- A Thief's End - YouTube ,可以在b站搜索这个标题找到视频。Controller在顽皮狗那里指物理动画的控制方式,可以结合Max里的controller概念理解。Motor Controller很好理解,相当于UE4中Constraint上的Motor,或者Unity的ConfigurableJoint上的Drive。我们可以调整这些Motor,来让关节约束的物体做出动作。Keyframed Controller,根据介绍,可以在世界空间直接设置刚体的位置且不会有摇晃倾向,在肢体末端使用这种Controller可以起到IK一般的效果。按照实验,在Unity中最接近Keyframing Controller的做法是,把刚体设置为运动学(isKinematic = true),之后通过刚体的position和rotation控制刚体运动。



神秘海域4 GDC分享 两种Controller的对比

ConfigurableJoint两边的刚体,如果有一个是运动学的,而另一个是动态的,则运动学的刚体保持不动,动态的刚体被运动学的刚体牵着走。如果两个都是运动学的,则ConfigurableJoint看起来完全无效。这是它起到“稳定器”、“IK效果”作用的源头。神秘海域4的GDC分享中的名场面“人在车上晃”,就可以把手设置成运动学,下身设置成运动学,除了手之外的上半身骨骼用Motor驱动。而且。可以根据想要的效果来决定Motor的力度,如果想让晃动幅度大一点,可以减小Motor的力。这些在顽皮狗的GDC分享里面有详细说明。
开发基础

接上两篇继续制作:
Unity动画TA:详解ConfigurableJoint,并参照UE4的Constraint画上容易看懂的Gizmo
Unity动画TA:ConfigurableJoint自制Ragdoll,并开发配套的编辑功能
大部分的开发内容在上两篇已经完成,目前的效果大多是在坐享其成。
如何用Motor驱动物体的旋转

Motor对应Configurable中的Drive。驱动旋转的Drive是AngularDrive,在参数面板上暴露的部分:



AngularDrive相关的参数面板

其中RotationDriveMode一定要在X and YZ和Slerp之间选一个。如果是X and YZ,则hi用Angular X Drive和Angular YZ Drive两个驱动参数,Slerp Drive不影响表现。反之如果是Slerp,则只有Slerp Drive影响表现,Angular X Drive和Angular YZ Drive均不影响表现。
于是现在的问题是,已知目标旋转,如何用Drive驱动约束中的connected body到达目标旋转呢?
于是,想起来之前在二次开发约束的时候,预先存了两个旋转:“约束空间”在父物体空间下的旋转,和“被约束空间”在子物体空间下的旋转。根据父物体和子物体分别的旋转,运行时的每一帧都可以推算出两个空间分别的旋转在哪。问题演变成:当子物体的旋转达到目标旋转的时候,“被约束空间”在“约束空间”下的旋转是什么样的?
    public void SetConnectedBodyWorldSpaceRotationTarget(Quaternion worldRotation)
    {
      ConfigurableJoint joint = configurableJoint;
      Quaternion constraintWorldRotation = configurableJoint.transform.rotation * ParentAnchorRotation; //ParentAnchorRotaion为约束空间在父物体空间下的旋转
      Quaternion constraintSpaceSourceRotation = Quaternion.Inverse(constraintWorldRotation) * worldRotation;
      Quaternion rotationTarget = constraintSpaceSourceRotation * ChildAnchorRotation; //ChildAnchorRotation为被约束空间在子物体下的旋转
      joint.targetRotation = rotationTarget;
    }
这一段代码在世界空间下给被约束物体设置了合适的旋转目标,然后我用这个函数制作了一个简易效果。

初步效果
https://www.zhihu.com/video/1453532090528178176
这个效果中,四个刚体被三个约束连接起来,Slerp Drive的Spring设置为100000,Damping为5,最大力为float上限。我让上边的两个立方体一个球体努力保持和我操作的方块在世界空间下旋转相同,但是它们会因为碰撞或者约束不允许而到达不了目标旋转。
物理驱动角色动画

这是最关键的一步,角色防穿模能不能行在此一举了。
如同所有人可能会想到的,我给一个角色放了两个骨架,一个纯动画驱动,另一个物理驱动,每帧跟着动画驱动的那个骨架走。(这一步相当于已经放弃纠正“慢一帧”的努力了,反正物理驱动动画的误差比“慢一帧”要大得多,能否接受完全在于最后效果表现如何)
我用了上一篇文章给Mannequin配置的Ragdoll,用上述函数驱动它的动画。这个地方踩了一个小坑:不能直接设置骨骼的世界旋转,而应该设置它的Local旋转。直接设置世界空间下的旋转非常有可能导致不稳定。所以我给每个骨骼设置的旋转又都做了一遍Local转World操作。
另一个小坑是,所有物理骨骼的根骨骼,此处是pelvis,必须设置为运动学,每帧直接设置到目标位置和旋转,否则会不稳定。在pelvis设置为运动学之后,两只手和两只脚是否要设置成运动学,取决于想让动画有多稳。
现在已经可以实现防穿模效果了。比如这个憨憨一般的跑步。

跑步撞墙
https://www.zhihu.com/video/1453537820153933824
或者蹲着模仿大力神杯。

把人压下去
https://www.zhihu.com/video/1453538124223242240
最能说明效果的其实还是上上篇文章开头的那个视频,Mannequin带着满身的约束Gizmo和Collider在跑步,太带感了。下一个视频用来说明这次物理动画的原理。红色模型为纯动画驱动,黑色模型为物理驱动。在模型不显示之后,可以看见一堆Collider在运动的样子。

原理说明
https://www.zhihu.com/video/1453539603064401920
这次又累了。前项目的主程大佬说,在PuppetMaster插件里面这些功能全都是有的,我纯属造了个轮子。不过我应该不亏,一来本来也没人要用我这次写出来的东西,二来在这个过程中熟悉了物理动画的原理。
只是,原本我以为做出来这个效果是能证明实力的东西。但是工程师们告诉我,Unity想做自己的物理动画系统都要自己Invoke PhysX,走出Unity的物理管线的限制。不仅如此,还要考虑复杂的运动情形下的表现优化,那才是有意义能说明问题的……
这说明,曾经我在山脚下,以为爬上了山就能摘到月亮。
<hr/>更新于2021/12/16 21:40
https://github.com/Aric-Zhang/PhysicsDrivenAnim
因为有同学要我的垃圾工程,放出来了。目前已经暂时弃坑,转头实现其他东西。真的要研究物理动画在Unity里的实现的话建议去研究PuppetMaster,那个才是稳定商业级的,这个只是个算法验证……

super1 发表于 2021-12-21 07:53

AnimationTarget和PhysicsBody的约束调整起来还是很费劲的
页: [1]
查看完整版本: Unity动画TA:不用插件实现物理驱动动画