Animation Blend
🌆

Animation Blend

The term animation blending refers to any technique that allows more than one animation clip to contribute to the final pose of the character
当我们有几百上千的动画素材(clips)时,在游戏引擎中,我们不是依次地或随机地去播放它。我们实际上是要把很多的动画的素材按照一定的规则,根据 gameplay 把它们 blend 到一起,所以动画的 blending 其实是动画的最重要的一个底层系统。
notion image
  • Assume the character walks at 1.5m/s and runs at 3.0m/s in our game
  • As the character's speed increase, we want to switch its animation from walking to running
我们做了一个走的动画,假设是 1.5m/s,我们还有一个跑的动画是 3m/s。假设这个角色现在是从走逐渐到跑,这个中间应该怎么去过渡它的动画?我们不可能为每一个速度做过动画。
如果只是到了一个速度设一个阈值切换的话,那就会出现上面第三张图里的情况,两个动画间有很明显的切换。
我们可以做线性插值,也就是在上面第四张图看到的结果,有一个很 smooth 的 transition 从走切换到跑,这其实就是动画 blending 的一个最基础的原理。
💡
blending 怎么做?

Math of Blending: LERP

notion image
上节课讲了 LERP 线性插值:每一个 joint 的平移、放缩、旋转 都需要插值,特别是旋转需要用四元数做插值。四元数插值可以用 NLERP 、SLERP 等,反正就是线性插值。
但是这小节提到的插值不同于上节课,上节课的插值就是,比如有无数个 Key frame,去插值一个动画是怎么做呢?本质上是在一个动画的 clips 内部,在帧与帧之间去插值。
而这节课的插值,也就是 blending 的插值,是在不同的动画之间,比如说在走和跑中各取一帧进行差值,所以这是在两个 clips 之间进行的插值。这一点的话是一定要分清楚的,因为所有的动画的 blending 都是在多个 clips 之间进行插值。
插值在上一节课应该知道算法怎么写,就不赘述了,具体可以参考
🧿
Joint Pose
的 NLERP、SLERP 部分。
在这里就讲一下 blending 怎么做,首先第一点就是怎么去算两边的权重。

Calculate Blend Weight

notion image
以从走到跑的这个案例为例,如果移动速度发生了变化,那我们就用速度作为一个自变量,然后就可以推导出来 weight 怎么变化。假设我们走路的速度是 speed1,跑步的速度是 speed2,那可以根据当前的 speed 算出来,走的动画的 weight1 是多少、跑的动画 weight2 是多少,只要保证 weight1 + weight2 = 1。当速度越来越快,weight1、weight2 从 0 到 1 之间过渡基本就行了。
线性插值根据靠近哪个信号,去选择它的权重,这是很直觉的。
💡
下面讲做 blending 的时候特别容易忽视的问题。

Align Blend Timeline

notion image
notion image
做这种插值的动画时,很多时候我们希望它是循环的。当我们在走路的时候和在跑的时候,步频是不一样的,跑的时候步频会相对快一点。这两个动画之间实际上是不同步的。那插值的时候,该用跑的哪一帧和走的哪一帧呢?这个其实是动画 blending 系统里面特别基本的一个问题。
这个时候我们就会要求动画师在做动画的时候,无论这个循环动画做多长,首先必须是循环的。而且比如说走的动画是左右脚各迈一次,那跑的动画也得是左右脚各迈一次,而且脚落地的时间最好大概一致,这个时候我们要把两个循环动画的时间归一化。这样插值才能够保证当时间比如说是 0 的时候,我们的脚都没有提起来。0.25 的时候,左脚提到空中了,0.5 的时候,左脚落下来了。 0.75 的时候右脚提到空中了,1 的时候,右脚又落下来重新开始。
时间的 timeline alignment 实际上是很多动画可以 blend 的一个基础。
💡
如果对这些基础的概念理解得不够准确的话,就特别容易写出各种各样的 bug。比如说如果时间 align 做不好,也就是速度和这个插值没做好,就会发现这个人走路在地上会 ice skating,在地上滑步,这是为什么呢?比如速度没有跟两边的权重产生一个合理的关系,包括步进的速度也没有配合的话,就会产生滑步。而在游戏中的滑步是一个一定要解决的问题。

Case: Walking to Running

In order to achieve the desired effect, we need a lot of animation clips with intermediate speeds.
Let the animators produce a whole bunch?
notion image
当有了这样一个非常简单的算法,就完成了一个最简单的 animation blending 系统,你就可以得到任何一个无极变速的动画。

Blend Space

1D Blend Space: Directional Movement

notion image
notion image
这个 blending 实际上有一维的变量,左右去控制它整个 blending。假设现在有一个角色,它有一个向前走的动画。然后还有向左走、向右走的动画。那我们在玩游戏的时候,按照上一小节的思想的话,是不是可以让这个轴在左右之间自由的插值,选择在三个动画中的百分比?这个插值就是一维的 Blend Space
但这里面注意一个细节,刚才只有两个动画 walk 和 run ,只在两点之间插值,但这里面取了三个采样点动画。因此这里将其 generalize 了。也就是说虽然还是一个一维字段的插值,但是采样点不是两个了而是三个了,那我们还可以继续加采样点。比如说可以变成五个采样动画:往左边慢移动、往左边快移动、站在中间直接往前走、往右边慢移动、往右边快移动。这样的采样也是一样的。
当我们做一维的动画的 blend space 的时候,实际上并没有规定这些采样点(clips)的总数,同时也并不要求这些 clips 在这个轴上是均匀分布的,它可以有的时候密一点,有时候疏一点。比如说人在正常走的时候,动画动作变化不大,但是一旦开始侧移的时候,动作发生了很大的变化,很可能在一个很小的速度就切换成另外一个动画了。但是当它用更快的速度往侧面走的时候,这个速度动作变化又没有那么大,直到发生显著的变化再切换到第三个动画,所以说动画的分布其实也是可以不均匀的,这就是 blend space 概念。
💡
但是当人往前走,可以走得快也可以走得慢,往左走一样也可以分快慢,我们甚至可以往斜走。这个时候很自然的,就需要一个二维的 Blend Space。

Directional Walking and Running

Players can change direction and speed at the same time We simply place the two 1D Blend Spaces orthogonally and we get an 2D Blend Space
notion image
notion image
如果采样点在最下面那个点,那就是 idle 的站着不动。点再往上走就是 walk,再往上走就是 run。如果这个采样点只是往上走的话,那角色就是从 idle 到 work 到 run 的变化。
同样的如果采样点从左上角到左中部分,那就是往左前方跑切换到往左前方走,角色的动画也会完全不一样。这个就是二维的采样空间,这个采样空间的每一个采样点就是一个动画的素材,我们用它来表达角色的整个动画行为。
⚠️
注意,在 blend space 里面所有的动画 clips 都是要求循环的,如果不循环,就需要全部都不循环,也就是都是一次性播放完,否则的话会产生很多很奇怪的东西。

2D Blend Space

Since the movement speed in the lateral direction is lower in the forward direction, the character should enter the running state in a lower speed in the lateral direction
notion image
后来我们发现一个细节:当这个人从走到跑的时候,如果人加上侧向运动的话,人会很快地进入到一个侧向跑的状态。因为侧向跑的时候人身体很难保持平衡,所以如果速度超过一个简单的阈值,人就开始必须要切换到跑的动画了,否则看上去很不真实。
💡
如果以后跟动画师团队去合作的时候,会发现他们对人的动作的行为,就是肢体的这种变化是非常非常敏感的。
notion image
这些动画素材在采样空间上的分布其实是不均匀的,它们可以随机地散布在各个点上。也就是说动画师觉得这个地方需要一个更细腻的表达,那个地方可能会再加一个 clips。那作为引擎程序员我们也要处理好。
我们要去选择插值,但是这里面已经这么多动画了,那难道每一次 blend 的时候要把七八个动画 blend 在一起,然后算他们的平均权重吗?这个计算比较费,因为每一个动画 clips 首先要去算出来当前这一帧的动画是什么样子,然后要把七八个 pose 在一起进行 blending,这个计算量是非常大的。
这时候我们发现一个规律,可以用 Delaunay 三角化的数学方法,在任何一个速度的方向轴上,确定二维空间一个唯一的点。然后就能选择邻近的三个动画 clips,用 barycentric 重心坐标在这个三个顶点,也就是三个动画里面去插值。这是一个完整的 2D 的 Blend Space 的实现。
📖
Delaunay Triangulation 给了一堆二维空间的点,根据这些顶点,可以生成这个空间的三角形划分。 三维的 Delaunay Triangulation 会比较复杂。
💡
Blend Space 在游戏引擎早期非常好的解决了一个问题:艺术家只需要做几个角色行为的动画,我们就可以在游戏引擎里面实现一个非常丝滑的角色的运动。

Case: Applauding on Different Poses

Is it possible to make a single applauding animation that can be applied to all poses?
There are multiple robots in different poses in the scene
We need to make applause animations for various poses separately
notion image
还是以小机器人为例,它想鼓掌,站着鼓掌、坐着鼓掌,甚至蹲着鼓掌,那我们要怎么做呢?
我们知道鼓掌的动画不可能为每一个姿势(站着、坐着…)都做一个,因此我们可以对 skeleton 做一个 mask,也就是说有些动画只 apply 到机器人的上半身,有些动画只 apply 到下半身。比如下半身 apply 蹲的动画,上半身 apply 一个鼓掌的动画。

Skeleton Masked Blending

notion image
这个 mask 只 apply 到一半左右的骨骼,因此计算量也会下降很多,这个时候就可以自由地把两种甚至更多的动画在这个角色身上进行混合,这是一种 skeleton mask 的 blending。那你有了这样的一个 API 的话或者
有了这样的一个方法,就可以实现这样的一个效果。小机器人无论是什么造型,无论在什么位置,都在鼓掌欢迎我们。
notion image

Additive Blending

Add a difference clip into a regular clip to produce a new clip
Additive Blending introduces a new kind of animation called a difference clip, which represents the difference between two regular animation clips.
A difference clip can be added into a regular animation clip in order to produce interesting variations in the pose and movement of the character.
notion image
假设我们对这个机器人提出更高的要求,不仅要对我鼓掌,还要向我点头示意。这里面有一个隐性的要求,就是要向着我去点头。机器人既要向不同方向点头,又要有各种坐姿,又要让它的手在不停地鼓。这怎么做呢?其实这就需要一个很重要的 blending ,叫做 Additive Blending
Additive Blending 就是在这个动画里面,我们不仅只作用到这个 skeleton 的局部,而且只存它的动画变化量。这样的话我们无论怎么渲染,无论怎么动 skeleton,都可以在 skeleton 的结果上再加上一个 extra 的旋转、translation 或者是 scale ,从而在上面又去 apply 一层动画。
你有这样的一个技术,就可以实现这样的一个效果,能看到很多的机器人在冲着我们去点头。
notion image

Additive Blending - Abnormal Bone Results

⚠️
Additive Blending 做得不好的话,容易做出 broken case。
Additive blends are more likely to have abnormal bone results
notion image
 
比如角色本身做了一个转身的动作,他的脖子已经扭了。如果我们再给他加上一个扭头,他的脖子可能就超过了旋转的上限,这时角色看上去就会非常不自然。
⚠️
Additive blending 一般都会做得非常的谨慎,后面在规划角色运动的时候,我们会有意识地避免过度地叠加这些动画,因为它会让有些 Joint 的行为看起来非常异常。

总结

前面讲了三种 blending:
  1. 非常简单的 LERP,也就是全身的线性 blending。
    1. 1D Blend Space
      1. Blend poses based on a single input value 双线性插值
    2. 2D Blend Space
      1. Blend poses based on two input values
      2. Triangular blend 三角形插值
  1. Masked Blending。只对一个局部的 blending。
  1. Additive Blending。blending 全部做完了之后,再在上面加一个修饰,加一个差分量。
基本上有了这些东西之后,我们就可以把那几百个上千个动画按照我们想要的方法,各种叠加在一起,再根据 gameplay 做一些简单的小游戏。