虚幻引擎中的物理动画

3

主题

4

帖子

9

积分

新手上路

Rank: 1

积分
9
发表于 2022-10-7 17:05:06 | 显示全部楼层
本文是物理动画系列相关文章的第一篇,主要为了介绍物理动画的基础知识,以及虚幻引擎自带的物理动画方案。

物理动画,是程序动画的一种。一般指的是利用物理模拟来辅助动画,可以根据玩家的实时物理状态去改变一些动画姿态,而不用出多套关键帧动画。其他的物理动画还有物理驱动的各种布料,绳子什么的,还有一些纯Motor驱动的物理角色,大家可以参考其他文章。
本系列文章主要是讲解如何用物理模拟的带动力的布娃娃(Active Ragdoll)去改善动画,包括增加各种物理反馈,或者死亡的布娃娃模拟之类的。
UE4和UE5的引擎自带了两套物理动画,只有一个场景演示,官方文档也只有很少的资料[1],网上的教程也甚少。最重要的物理资产调整的教程几乎也没有。大部分情况下,只能通过源码来理解虚幻里面的原理。
另外的重要资料是来自两篇重要的GDC演讲。顽皮狗的:《Physics Animation in Uncharted 4: A Thief's End》[2]和重生工作室的《Physical Animation in Star Wars Jedi: Fallen Order》[3]。
感谢bchr的评论补充,还有一篇EA的GDC视频。[4]
物理动画的目标

在某些场景里面,我们的动画只有比较重要的一部分。比如横杆悬挂场景中,可能只有一个idle的动画和一个左右移动的动画。但是,真实的角色,下半身会随着运动而大幅度摆动。这时候,就需要一个物理的布娃娃去模拟这种摆动,然后把他混合到主动画里面,这样就可以达到一个随着角色运动而符合物理摆动的悬挂的角色了。
所以,我们要做的,首先是一个调整好的布娃娃物理资产。然后,把动画的状态传给布娃娃,让布娃娃尽可能去匹配动画的姿势,然后,布娃娃本身要受物理的影响。然后,把布娃娃和动画混合起来,得出一个输出的物理动画。
这里怎么匹配动画呢?匹配动画一般分为两部分:匹配位置和匹配旋转。
位置匹配
匹配位置很简单,就是尝试匹配世界空间中的动画位置。如何匹配呢?首先每一帧你都能拿到物理部件的位置 p_{rigi} 和对应的动画目标的位置 p_{anim} 。通过计算两者的差值,我们可以算出当前物理和动画的距离。
最简单的是直接设置速度(Physical Animation in Star Wars Jedi的做法)。我们可以算出一个线速度 v_{desired} = (p_{anim} - p_{rigi}) / \Delta t 。然后我们可以拿一个Alpha值来将当前的物理速度和这个理想的线速度进行混合。1的话将会瞬间到达动画位置,0的话则会完全沿用物理当前的线速度。
另外一种是利用弹簧的特性去拉扯物理部件到动画位置。一般类似关节的实现都会用到这种。不过这种有个问题是每个关节Spring和Damping不太直观,需要反复调整。下面的公式来自于Physx的Drive介绍。
force = spring * (targetPosition - position) + damping * (targetVelocity - velocity)
旋转匹配
匹配旋转则有些分歧,不过大部分人的做法都是匹配本地空间的动画旋转。当然也有些实现是基于世界空间的旋转的,比如How To Implement Active Ragdoll[5]这篇。
旋转匹配同样有直接设置角速度和弹簧两种方案。(比较复杂后面用到再讲)
物理动画的难题
这里面有一点比较矛盾,如果布娃娃受动画的约束过大,他旧无法很好地响应物理;如果他过于响应物理,那布娃娃一定会丢失不少动画细节。所以,物理动画的难点,就是在各个场景下,找到物理和动画的一个平衡点,做出做好的效果。
虚幻里的物理动画方案

虚幻引擎自带了两套物理动画方案,一套是Mesh自带的物理刚体模拟,另外一套是PhysicalAnimationComponent。两套方案的实现原理基本不一样。在这一篇我们先简单讲述一下这两套物理动画的原理。
我这里物理引擎用的是的Physx,Chaos的话原理和参数应该是一样的,大家可以去尝试。
对于带动力的布娃娃来说,关节的控制是重中之重,所以希望大家能去看一下PhysX里面关于关节的文章[6]。在后面的章节我们还会介绍怎么设置一个物理资产。


SkeletalMeshComponent里面的物理动画

SkeletalMeshComponent里面的物理动画是整个物理动画的基础。没有Mesh提供的角色物理,PhysicalAnimationComponent无法模拟物理。而且SkeletalMeshComponent还提供了物理和动画混合这个操作,不管你用什么物理动画方案都绕不过他。(当然也可以自己写一套,但是没必要)
SkeletalMeshComponent主要是创建了一套跟角色的物理资产对应的物理刚体,然后用关节将他们连接起来。
如果不设置动力,这一个就是我们常说的无动力布娃娃,表现跟你在Physical Assets里面预览的效果几乎一样。


不过仔细看物理资产,可以发现各个关节里面还有Drive的设置。只要打开了SkeletalMeshComponent的bUpdateJointsFromAnimation开关,SkeletalMeshComponent会在每一帧在UpdateRBJointMotors函数里,将动画的目标通过SetAngularOrientationTarget设到关节上,然后根据你配置的Motor参数来引导关节。这样他就会变成一个有动力的布娃娃系统。
不过从整体上看,SkeletalMeshComponent里面的物理动画只是一个基础的本地旋转模拟的动力布娃娃,缺少位置匹配导致他并不能做出更好的表现,而本地旋转匹配的参数比较生硬,难用。甚至如果完全simulated的话,整个角色都无法站起来(Gang Beasts类的游戏应该需要对盆骨进行处理来稳定整个角色)。大部分虚幻引擎应用中都把他当成一个死亡布娃娃使用。
常见用法一般直接是让布娃娃自由倒地,然后四肢尽可能贴合死亡的pose,或者通过添加各种力(模拟受击)和各种线速度(从动画计算)来让布娃娃模拟各种死亡(后面的章节会讲到)。
PhysicalAnimationComponent

这套物理动画组件的核心是约束。他通过Physx的D6约束的驱动,实现了一套由世界坐标匹配和本地的角度匹配驱动的动力布娃娃系统。
首先他会每个Mesh的物理body创建一个对应的纯动画(Kinematic)的刚体(Rigibody),然后给物理的Body和动画的刚体建立一个D6约束。因为关节的RefFrame是Identity,所以关节会尽量把物理的刚体拉到动画的刚体一样旋转和位置。
然后在激活的每一帧里,首先将Kinematic的Actor移动到当前动画的位置(UpdateTargetActors),然后通过约束尝试把物理Rigibody拉扯到动画的位置。
对应不同的匹配类型会用到不同的设置。


首先是位置匹配。关闭了Local Simulation后,会对世界坐标进行匹配。关节的线性驱动会尝试把物理body拉扯到动画位置。驱动参数分别是Physical Animation Data里面的几个参数。
PositionStrength:这个参数决定了Drive的stiffness参数(刚度),力度越大拉力越大。
VelocityStrength:这个参数决定了Drive的damping(阻尼)。这里我也不知道他为啥命名成Velocity,不看代码的人基本不知道这个参数的真实含义。
MaxLinearForce:这里是设置最大的冲力(Impulse)。
具体的含义可以去看PxD6JointDrive的定义。stiffness和damping的相关知识大家可以去看看弹簧的相关定义。

然后是旋转匹配。Physical animation component用的是本地空间驱动的旋转,具体参数如下。
OrientationStrength:这里是角驱动的扭矩(torque)。
AngularVelocityStrength:这里是角驱动的阻尼。
MaxAngularForce:最大的扭矩。
这样,通过设置不同关节的各个详细参数,我们可以用Physical Animation Component实现一套基本功能都齐全的物理动画。

小结

在这一篇里我们总结了物理动画的一些基础概念,以及虚幻里面的两套实现不同布娃娃的原理。下一篇(不鸽的话)我会介绍一下物理资产相关的东西。
参考


  • ^基于物理的动画 | 虚幻引擎文档 https://docs.unrealengine.com/5.0/zh-CN/physics-driven-animation-in-unreal-engine/
  • ^Physics Animation in Uncharted 4: A Thief's End https://www.youtube.com/watch?v=7S-_vuoKgR4&list=LL&index=5&t=1655s
  • ^Physical Animation in Star Wars Jedi: Fallen Order https://www.youtube.com/watch?v=TmAU8aPekEo
  • ^Physics Driven Ragdolls and Animation at EA: From Sports to Star Wars https://www.gdcvault.com/play/1025210/Physics-Driven-Ragdolls-and-Animation
  • ^how-to-implement-active-ragdoll http://peyman-mass.blogspot.com/2018/01/how-to-implement-active-ragdoll.html
  • ^https://docs.nvidia.com/gameworks/content/gameworkslibrary/physx/guide/Manual/Joints.html
回复

举报 使用道具

3

主题

7

帖子

12

积分

新手上路

Rank: 1

积分
12
发表于 2022-10-7 17:05:25 | 显示全部楼层
gdc2018有一篇ea关于物理动画的分享,个人认为干货也非常多,里面详细介绍了基于速度去匹配动画和基于约束去匹配的两种方案。
回复

举报 使用道具

3

主题

5

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2022-10-7 17:06:19 | 显示全部楼层
https://www.gdcvault.com/play/1025210/Physics-Driven-Ragdolls-and-Animation
回复

举报 使用道具

4

主题

9

帖子

18

积分

新手上路

Rank: 1

积分
18
发表于 2022-10-7 17:07:08 | 显示全部楼层
至于position和velocity的命名,应该是类似physx那个公式,position参与stiffness的计算,velocity参与damping部分的计算[捂脸]
回复

举报 使用道具

0

主题

5

帖子

7

积分

新手上路

Rank: 1

积分
7
发表于 2022-10-7 17:08:00 | 显示全部楼层
对,忘了这篇了[飙泪笑]
回复

举报 使用道具

2

主题

4

帖子

8

积分

新手上路

Rank: 1

积分
8
发表于 2022-10-7 17:08:10 | 显示全部楼层
哦哦,原来是这样!
回复

举报 使用道具

2

主题

6

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2022-10-7 17:09:02 | 显示全部楼层
UpdateTargetActors里面是把InstanceData.TargetActor移动到当前动画的位置,这里InstanceData.TargetActor是Kniematic Rigid Body不是物理的Rigibody吧
回复

举报 使用道具

3

主题

6

帖子

13

积分

新手上路

Rank: 1

积分
13
发表于 2022-10-7 17:09:22 | 显示全部楼层
对的,是拉kinematic,然后通过约束来拉物理的rigibody。估计写的时候写混了。感谢提醒!
回复

举报 使用道具

4

主题

6

帖子

16

积分

新手上路

Rank: 1

积分
16
发表于 2025-3-5 03:58:45 | 显示全部楼层
沙发???
回复

举报 使用道具

您需要登录后才可以回帖 登录 | 立即注册
快速回复 返回顶部 返回列表