《Fundamentals of Computer Graphics》第五版 第十六章 计算机动画

59 阅读27分钟

Animation 一词源于拉丁语 anima,意为赋予生命、兴趣、灵魂、运动或活动的行为、过程或结果。一方面,计算机可以作为工具帮助传统动画快速制作,另一方面,计算机有着此前难以获得的独有的能力。

现代建模工具可以相对容易地创建出精细三维模型,渲染算法可以生成各种外观,数值模拟算法可以帮助制作特别困难的物理动画,动作捕捉系统可以记录并使用真实动作。这让计算机动画技术在电影、商业、汽车设计、建筑、医学、科研等领域得以爆发式地应用,与此同时也产生了一些全新的领域和应用,如计算机动画电影、增强/虚拟现实、电脑游戏等。

本章仅对那些直接用于生成或操纵运动的技术和算法进行概述。特别地,本章将大致区分并简要描述四种主要的计算机动画方法:

  • 关键帧(Keyframing),提供某些时刻的必要数据,剩余的由计算机填充;
  • 程序式动画(Procedural animation),使用专门设计的、经验的数学函数和程序来模拟某些特定的运动;
  • 基于物理(Physics-based)的动画技术,求解运动微分方程;
  • 动作捕捉(Motion capture),使用特殊的设备和技术记录真实世界运动,并传递给计算机模型。

动画基本原则

1987 年,John Lasseter 把自 20 世纪 30 年代开始华特迪士尼工作室发展起来的动画基本原则,带到了计算机动画领域。这些原则共 12 个:挤压与拉伸(squash and stretch)、预备动作(anticipation)、主次分明编排(staging)、连续动作与关键帧动作(straight-ahead and pose-to-pose action)、跟进与交叠(follow through and overlapping action)、慢进慢出(slow-in and slow-out)、弧线(arcs)、次要动作(secondary action)、节奏(timing)、夸张(exaggeration)、立体(solid drawing skill)、吸引力(appeal)。此外,对于计算机动画而言,控制力和灵活度的平衡至关重要。虽然这些原则广为人知,但实践中仍有许多因素影响这些原则的执行程度。

节奏(Timing)

节奏(timing),或者说动作速度,会影响动作本身的含义、情绪状态、甚至是感知到的物体重量——物体越重,加速度越小。动作需要占据足够长的时间来引起注意,同时又要避免过慢以致于很无聊。对于有录音的计算机动画,声音本身就是自然的时间锚点。实际上,大多数情形都是先录制声音,再同步动画。

动作编排(Action Layout)

好的动画编排(staging)应该向观众突出重点。人眼视觉系统主要响应的是刺激的相对变化而不是绝对值,这可以很好地帮助到动作编排。

每个动作都可以分为三个部分:预备(anticipation)、动作本身、跟进(follow through)。通常,动作本身是最短的部分,某种意义上也是最无趣的。如果动作本身很快、很重要或很困难,那么预备动作变得特别重要。放大预备动作的幅度可以强调这些性质,并保证不会被忽视。主要动作通常会产生一个或多个交叠动作(overlapping action)。物体的附属部分通常会被主导部分拖动,并在跟进动作阶段持续一段时间的运动。通常,前一个动作尚未完成,后一个动作就已经开始。

交叠动作是保证自然性所必须的,而次要动作(secondary action)通常用于增加动作的趣味性,并实现逼真的、复杂的动画,重要的是区分主次。

动画技术

有些技术可以让运动看起来更自然,挤压与拉伸(squash and stretch)就是其中之一。通常物体沿运动方向拉伸,沿受力方向挤压。速度越快/受力越大,拉伸/挤压越明显。保持物体总体积不变可以避免产生增大或缩小的错觉。物体形变(deformation)有以下几个原因。运动速度过快时,前后两帧的物体可能完全没有交叠,从而产生频闪(strobing),拉伸可以保证物体交叠。拉伸和挤压也可以表示物体的柔韧性,弹性越强,形变越大,而刚体的形状几乎不变。

squash-and-stretch.png

真实运动没有沿直线的,所以动画中使用弧线(arcs)描述物体运动。真实世界中物体运动速度也不会发生突变,因此动画中运动一般遵循慢进慢出(slow-in and slow-out)原则。手绘动画有时采用连续动作(straight-ahead action),即,从第一帧开始,按顺序一帧一帧绘制;对于计算机动画,关键帧(keyframing,pose-to-pose action)更适用,这一技术需要先设置一系列排布稀疏的关键帧,然后再填充帧间动画,关键帧技术对节奏的控制更精细。

keyframing.png

上述几乎任何技术都可以适当夸张(exaggeration)来实现更好的艺术效果或强调某些特点。最终的目标是让动画有吸引力(appeal)。极度复杂或过于对称会降低吸引力。为了得到好的结果,传统的动画制作者需要立体绘画技巧(solid drawing skill),计算机动画也类似。

控制力与灵活度

传统动画完全控制整个制作过程,但需要付出每帧手绘的代价,极度费时费力。计算机动画需要明确的权衡,一方面,给动画制作者更直接的控制,但需要制作者更多的工作;另一方面,使用更加自动化的技术,但提供的控制力很弱。好的算法应该提供充分的灵活性,同时只让制作者提供直观的、易提供的、足以实现想要效果的信息。

关键帧(Keyframing)

3D 计算机动画中的关键帧并非直接用图像表示,而是用一组描述 3D 场景的参数:所有物体的中心、物体颜色、物体的伸缩系数、复杂物体中各部分之间的模型变换、相机位置和方向、光源强度等。动画需要其中一部分参数随时间变化。每帧都设置这些值并不高效,通常只对某些重要的时刻——关键帧(key frames)tkt_{k},设置这些参数——关键值(key values)fkf_{k}。关键帧和关键值的组合 (tk,fk)(t_{k},f_{k}) 称为 key。不同参数的关键帧可能不同,但通常会同时对某些参数设置 key。关键帧离得越近,动画制作者对其控制力越强,但也很费时。通常对于相对简单动画,增加 key 的间距;对于复杂动画,缩小 key 的间距。

一旦确定了 (tk,fk)(t_{k},f_{k}),就需要计算出所有其它帧所对应的 ff 值。插值可以方便地处理这一问题,穿过所有给定数据点的连续曲线称为动画曲线(animation curve)f(t)f(t)。无需导数,直接通过给定的 key 来计算所需信息更适合动画。曲线的一阶导数表示速度,二阶导数对应加速度或者受力,这在真实世界中可以发生突变,因此动画曲线仅需要 C1C^{1} 连续性。综上,Catmull-Rom 样条是最合适的选择之一。

animation-curve.png

大多数动画系统可以让制作者交互式地对初始曲线精细调控,比如插入 key、调整 key、修改切线等。TCB 控制是另一种帮助调整曲线形状的技术,TCB 分别表示张力(tension)tt连续性(continuity)cc偏移(bias)bb。TCB 控制就是使用三个相应的参数一致地调整某个 key 的入切线和出切线,从而改变附近的曲线形状。TCB 样条中的切线斜率为:

Tkin=(1t)(1+c)(1b)2Δt(fk+1fk)+(1t)(1c)(1+b)2Δt(fkfk1)Tkout=(1t)(1c)(1b)2Δt(fk+1fk)+(1t)(1+c)(1+b)2Δt(fkfk1)\begin{aligned} T_{k}^{\text{in}} &= \frac{(1 - t)(1 + c)(1 - b)}{2\Delta t}(f_{k+1} - f_{k}) + \frac{(1 - t)(1 - c)(1 + b)}{2\Delta t}(f_{k} - f_{k-1}) \\ T_{k}^{\text{out}} &= \frac{(1 - t)(1 - c)(1 - b)}{2\Delta t}(f_{k+1} - f_{k}) + \frac{(1 - t)(1 + c)(1 + b)}{2\Delta t}(f_{k} - f_{k-1}) \end{aligned}

ttccbb 一般均处于 [1,1][-1,1] 上。

张力 tt 通过对入切线、出切线缩放来控制关键帧附近的曲线锐度。偏移 bb 可以选择性地增加关键帧附近的权重,将曲线沿左侧或右侧直线拉直。bb 接近 11 时,动作过冲(overshooting);bb 接近 1-1 时,动作欠冲(undershooting)。cc 非零时,入切线和出切线不同,曲线没有 C1C^{1} 连续性。ttccbb 均为零时对应 Catmull-Rom 样条。

TCB-control.png

运动控制

只控制动画曲线的形状不足以同时控制路径和速度。标准拟合方法得到的多项式曲线的运动速度是不均匀的。虽然人眼视觉系统无法很好地区分不均匀的变化速率,但动画中物体运动应尽可能和真实情况保持一致。

fitting-curve.png

将动画和曲线参数化区分开,物体在时刻 tt 的位置可以表示为:

p(u(s(t)))\vec{p}(u(s(t)))

其中,s(t)s(t) 是物体在 tt 时刻对应的弧长,u(s)u(s) 是弧长 ss 处对应的参数,p(u)\vec{p}(u) 是参数曲线。

motion-control.png

标准的距离-时间函数(distance-time function)s(t)s(t) 有:

  1. 匀速运动 s(t)=vts(t)=vt
  2. 匀加速运动 s(t)=v0t+at2/2s(t)=v_{0}t+at^{2}/2,可用于模拟 ease-in、ease-out。

s(t)s(t) 的导数是运动速度,它的正负反映了沿曲线的运动方向。为保证灵活性,动画系统一般提供了交互式编辑 s(t)s(t) 的能力。除了距离-时间函数之外,有时控制速度-时间函数(velocity-time function)v(t)v(t)、甚至是加速度-时间函数(acceleration-time function)a(t)a(t) 可能更方便,而动画系统首先需要积出 s(t)s(t)

曲线参数 uu 和弧长 ss 之间的关系由动画系统自动建立。实际上,动画系统首先确定出参数 uu 所对应的弧长 s(u)s(u),然后反解出 u(s)u(s)。对于大多数曲线,无法得到封闭形式的解析解,而需要数值求解。

另一种方法是用一组足够密的参数值 uiu_{i} 将曲线近似为折线段,然后创建一个近似弧长表:

s(ui)=j=1ipjpj1=s(ui1)+pipi1s(u_{i}) = \sum_{j=1}^{i}\|\vec{p}_{j} - \vec{p}_{j-1}\| = s(u_{i-1}) + \|\vec{p}_{i} - \vec{p}_{i-1}\|

由于 s(u)s(u) 是关于 uu 的非减函数,因此可以搜索这张表找出 ss 所处区间,然后对区间端点线性插值即可。如果需要更高精度,可以该值为起点,用牛顿-拉夫森算法(Newton-Raphson algorithm)进一步计算。

tabular.png

插值旋转

旋转在刚体动画中发挥着特殊作用,需要更专业的插值方法和表示。指定物体取向有多种方式,变换矩阵是其中之一,但是直接对矩阵元插值无法得到正确结果。另一种表示物体取向的方式是用依序的三个绕坐标轴的转动,可以是在定系中的固定角表示(fixed-angle representation),也可以是在动系中的欧拉角表示(Euler-angle representation)。对于欧拉角表示,如果三个转动中刚好有两个在定系中共轴,会导致自由度降低,这种现象称为万向锁(gimbal lock)。比如,三个依次绕 XYZ 轴的转动中,第二个转动 90 度时,第一个转动可以直接等价于第三个转动。此外,还可以直接使用转轴和转角来表示物体取向,四元数(quaternion)可用于方便地处理这种表示。

Euler-angle.png

四元数(quaternion)由一个标量 ss 和一个三维向量 v=(x,y,z)\vec{v}=(x,y,z) 构成:q=[s x y z]=[s; v]q=[s\ x\ y\ z]=[s;\ \vec{v}]。四元数加法定义如下:

q1+q2[s1+s2; v1+v2]q_{1} + q_{2} \equiv [s_{1} + s_{2};\ \vec{v}_{1} + \vec{v}_{2}]

四元数标量乘:

aq[as; av]aq \equiv [as;\ a\vec{v}]

四元数乘法定义:

q1q2[s1s2v1v2; s1v2+s2v1+v1×v2]q_{1}q_{2} \equiv [s_{1}s_{2} - \vec{v}_{1}\cdot\vec{v}_{2};\ s_{1}\vec{v}_{2} + s_{2}\vec{v}_{1} + \vec{v}_{1}\times\vec{v}_{2}]

四元数乘法满足如下规律:

a(q1q2)=(aq1)q2=q1(aq2)(q1q2)q3=q1(q2q3)a(q_{1}q_{2}) = (aq_{1})q_{2} = q_{1}(aq_{2}) \\ (q_{1}q_{2})q_{3} = q_{1}(q_{2}q_{3})

四元数乘法不满足交换律。四元数的(norm):

q=s2+v2|q| = \sqrt{s^{2} + \vec{v}^{2}}

四元数的模满足:

q1q2=q1q2aq=aq, 标量 a|q_{1}q_{2}|=|q_{1}||q_{2}| \\ |aq| = |a||q|,\quad\forall\ \text{标量}\ a

四元数的乘法逆:

q1=1q2[s; v]q^{-1} = \frac{1}{|q|^{2}}[s;\ -\vec{v}]

乘法逆满足:

(q1q2)1=q21q11(aq)1=a1q1(q_{1}q_{2})^{-1} = q_{2}^{-1}q_{1}^{-1} \\ (aq)^{-1} = a^{-1}q^{-1}

绕归一化向量 n\vec{n}ϕ\phi 角的转动可用如下的归一化四元数表示:

q=[cos(ϕ/2); sin(ϕ/2)n]q = [\cos(\phi/2);\ \sin(\phi/2)\vec{n}]

对点 p\vec{p} 的旋转可以表示为:

[0; p]=q[0; p]q1[0;\ \vec{p}'] = q[0;\ \vec{p}]q^{-1}

其中,p\vec{p}' 是旋转后的点,四元数 [0; p][0;\ \vec{p}] 用于表示点 p\vec{p}。四元数乘法保持了转动,即乘积的转动等于转动的乘积。实际上,非归一化四元数 q=qqnormq=|q|q_{\text{norm}} 也可以用上式表示转动,表示的转动与归一化四元数 qnormq_{\text{norm}} 相同。

使用四元数做动画,直接将四元数看作四维空间中的点,做插值即可。球面线性插值(spherical linear interpolation,简称 slerp)可以保证四元数是归一化的。但是先做一般线性插值,再投影到单位球进行归一化,在实践上更简单。

quaternion-lerp.png

形变(Deformations)

非均匀伸缩(nonuniform scaling)是形变的最简单的例子。更一般地,可以对构成物体的所有点的局部坐标应用形变函数(deformation function)p=f(p,γ)\vec{p}'=f(\vec{p},\gamma),来重新排布这些点,并生成一个新的形状,其中 γ\gamma 是形变函数使用的参数向量。多个函数可以结合起来实现更复杂的形变。简单的形变函数有:弯曲(bend)、扭转(twist)、锥化(taper)。

deformation.png

对形变函数的参数做关键帧可以很容易地实现形变动画,但它的缺点是:

  1. 非标准形变的数学函数选取困难;
  2. 形变是全局的(global),而非局部的。

为了实现物体的局部形变,同时提供更直接的控制,可以选择一个种子顶点(seed vertex),将其移动到一个新的位置,并调整附近的顶点。形变在物体各部分所影响的范围以及位移量由衰减函数(attenuation function)控制。对种子顶点运动做关键帧,可以生成形变动画。

自由形变(free-form deformation,简称 FFD)是一种更常见的形变技术。首先,建立一个局部坐标网格包裹住物体上需要形变的部分,计算出所有相关的点相对于网格的归一化坐标 (s,t,u)(s,t,u)。接下来,用户可以自由地将网格形状从 Pijk\mathbf{P}_{ijk} 变为 Pijk\mathbf{P}'_{ijk}。最后,以原始坐标为插值系数 (s,t,u)(s,t,u),以形变后的格点 Pijk\mathbf{P}'_{ijk} 为控制点,使用三元贝塞尔插值重建物体:

P(s,t,u)=i=0LCLi(1s)Lisij=0MCMj(1t)Mjtjk=0NCNk(1u)NkukPijkP(s, t, u) = \sum_{i=0}^{L}C_{L}^{i}(1-s)^{L-i}s^{i}\sum_{j=0}^{M}C_{M}^{j}(1-t)^{M-j}t^{j}\sum_{k=0}^{N}C_{N}^{k}(1-u)^{N-k}u^{k}\mathbf{P}'_{ijk}

可以验证,以原始格点 Pijk\mathbf{P}_{ijk} 为控制点时,所得插值结果就是点的原始位置。

其中,LLMMNN 分别是网格在各方向上的最大索引。实际上,FFD 网格可看作物体的低分辨率版本,允许通过相对少量的、直观的调整,来实现任意复杂物体的光滑形变。FFD 网格本身可以被动画系统当作规则物体,可以变形、动画、进一步形变,以实现网格所绑定的物体的形变。

FFD.png

角色动画(Character Animation)

用于动画的人物模型通常分两层:皮肤(skin)或外壳——观测者最终看到的非常精细的曲面、骨架(skeleton)——作为人物运动学模型的关节层次结构(hierarchical structure of joints)。有时,还会有额外的中间层——肌肉(muscle)。

skeleton.png

类似场景图,对关节树做深度优先遍历可以得到整个骨架在世界坐标系中的几何信息,变换矩阵栈(transformation stack)可以辅助这一过程。这一简单通用的技术在动画中有个专门的名字——正向运动学(forward kinematics,简称 FK)。为表示骨架中的这些变换,通常使用一组专门的参数:连杆长度(link length)、关节角度(joint angle)等。

大多数情况下,动画制作者只想指定关节链端点的行为——对应某种特定的动作,然后由动画系统根据末端器(end effector)的运动自动计算出内部关节参数。逆向运动学(inverse kinematics,简称 IK)可以实现这一目标。

IK.png

x\vec{x} 是末端器的位置(也可能包含方向),α\vec{\alpha} 是从根节点到末端器之间所有内部关节的参数向量。为简单考虑,关节参数与末端器之间的关系可以写为:

x=(x1,x2,x3)T=F(α)\vec{x} = (x_{1}, x_{2}, x_{3})^{T} = \mathbf{F}(\vec{\alpha})

对两边取微分可得:

δx=Fαδα\delta\vec{x} = \frac{\partial \mathbf{F}}{\partial\vec{\alpha}}\delta\vec{\alpha}

其中,雅可比矩阵(Jacobian):

Fα=[f1α1f1α2f1αnf2α1f2α2f2αnf3α1f3α2f3αn]\frac{\partial \mathbf{F}}{\partial\vec{\alpha}} = \begin{bmatrix} \frac{\partial f_{1}}{\partial\alpha_{1}} & \frac{\partial f_{1}}{\partial\alpha_{2}} & \cdots \frac{\partial f_{1}}{\partial\alpha_{n}} \\ \frac{\partial f_{2}}{\partial\alpha_{1}} & \frac{\partial f_{2}}{\partial\alpha_{2}} & \cdots \frac{\partial f_{2}}{\partial\alpha_{n}} \\ \frac{\partial f_{3}}{\partial\alpha_{1}} & \frac{\partial f_{3}}{\partial\alpha_{2}} & \cdots \frac{\partial f_{3}}{\partial\alpha_{n}} \end{bmatrix}

雅可比矩阵可以从骨架的几何关系得出。在任一时刻,都可以根据当前位置和想要到达的位置计算出 δx\delta\vec{x}。只要能解出 δα\delta\vec{\alpha},就可以应用正向运动学重新调整骨架的位置。

Jacobian.png

然而,大多数情况下,上述方程组没有解析解,而且是欠约束(underconstrained)——方程数量少于变量数。找出特解有许多方式,比如引入真实生活中所需的自然约束。

IK-solution.png

由于只能数值求解,雅可比矩阵只针对一个特定的骨架构型。IK 的根节点不一定是整个骨架的根节点,可以将多个 IK 求解器应用到骨架中相互独立的部分。

IK-framework.png

通常将 FK 和 IK 结合起来可以实现骨架动画。许多常见的动作展现出熟悉的关节相互运动模式,这可以帮助快速创建自然运动甚至是使用一个剪辑库。动画制作者根据人物的物理参数调节一般结果,然后赋予其更多的个性。

骨架位置的变化可以看作是一类特殊的、作用在人物皮肤上的形变器(deformer)。通过为每个皮肤顶点(skin vertex)赋予一个关节——刚性皮肤(rigid skinning),或多个关节——柔性皮肤(smooth skinning),作为驱动,可以将运动传递到曲面上。

对于刚性皮肤,皮肤顶点冻结在相应关节的局部空间,顶点运动与关节相同,具体位置由标准的 FK 程序决定,这类皮肤无法模拟呼吸或肌肉动作。对于柔性皮肤,多个关节可通过某些权重影响一个皮肤顶点。顶点位移由关节位移加权得到 d=widi\vec{d}=\sum w_{i}\vec{d}_{i}。归一化权重和最常见,但并不是必须的。

rigid-smooth.png

面部动画

根据人体的解剖学结构可知,骨架适合肢体动画,但不适合面部动画。面部需要高分辨率几何模型,甚至是皮肤反射属性和纹理——如果需要照片级真实度。

相比于直接对顶点做关键帧或是用物理动画技术来模拟肌肉的行为,有一些更专业、更高层次的方法可以实现面部动画。静态面部特征可以使用一组相对较少的构象参数(conformational parameters)来描述,它可用于对一个普通的面部模型变形(morph),使其有个性,比如:整体尺寸、眼到前额的距离、鼻子长度、下巴宽度等。动态特征由另一组表情参数(expressive parameters)来描述,比如:头部的刚体转动、双眼间距、特征点相对于静态位置的移动等。将这些参数组合起来调节,可以得到大部分有趣的表情,还可以对它们使用关键帧动画。使用表情参数可以生成一组用于表达常见情绪的表情,以实现高层次的控制,也可以将这些关键帧混合起来表达更复杂的情绪。面部动作编码系统(Facial Action Coding System,简称 FACS)将动态面部表情直接分解为基本动作——动作单元(action units,简称 AUs)——之和,而不是用静态表情序列来表达动态的。AUs 基于大量的心理学研究,包括:皱眉、皱鼻子、伸嘴唇等,将 AUs 组合起来可用于合成所需的表情。

动作捕捉

记录真实世界的演员动作并将其应用到计算机生成的人物模型上,这一技术称为动作捕捉(motion capture,简称 MC),主要分两类:电磁、光学。

电磁动作捕捉中,传感器直接测量三维空间中的位置和方向,通常可实时提供捕捉结果。它的劣势有:设备成本高,附近金属物体干扰,传感器和电池的尺寸明显,这是捕捉大幅度动作的障碍。

光学动作捕捉中使用的是彩色小标志,在大多数基本布置中,运动由两台校准摄像机记录,简单的三角定位可用于提取标志位置。用于精确跟踪视频中多个标志的更先进的计算机视觉算法的计算成本较高,因此通常是离线处理。光学式通常没有电磁式健壮,常见的问题有:标志遮挡,标志错误识别,图像噪声等。引入更多的相机从不同方向观测,可以同时改善精度和健壮性,但也提升了成本,增加了处理数据的时间。但随着计算能力的提升和计算机视觉算法的发展,光学动作捕捉更具吸引力。光学动作捕捉适合微妙的面部动作,也可用于除了人之外的其它物体,像动物、树枝等。

optical-MC.png

跟踪的位置通常选在关节附近,但一般位于皮肤上,而不是关节,因此需要额外处理,将记录位置转换为关节位置,比如在肘关节两侧放两个标志,然后通过平均得到更好的关节位置。如果不做额外处理,由于关节位置偏移、固有噪声、测量精度不足等,可能会产生非常明显的失真。由于运动过程中的精度问题,还可能会产生类似滑步的问题。大多数这种问题都可以通过逆向运动学修正。

将记录下的运动应用到不同的人物模型上,即编辑 MC 数据,必须满足必要的约束并保持外观整体自然。通常,想要的变化越多,质量就越难以维持。一个解决方法是,记录大量的动作,再将这个库中的短片拼接起来得到想要的结果。按照制作者的需要调整记录下来的动作一直是动作捕捉技术的劣势。

物理动画

由于相对而言比较复杂,而且成本较高,因此物理动画常用于其它技术不可用或不能提供足够真实结果的情形,主要例子有:流体动画、布料仿真、刚体运动、弹性物体的精确形变等。

cloth-simulation.png

有限差分法(finite difference approach)是概念上最简单、最流行的一类算法,已经广泛应用到动画中绝大多数微分方程。这一方法中的关键想法是用差分方程(difference equation)代替微分方程。连续定义域用一组有限点集来表示,每个导数用格点处函数值的近似值代替:

df(t)dtΔfΔt=f(t+Δt)f(t)Δtf(x,t)xΔfΔx=f(x+Δx,t)f(x,t)Δx\begin{gathered} \frac{df(t)}{dt} \approx \frac{\Delta f}{\Delta t} = \frac{f(t + \Delta t) - f(t)}{\Delta t} \\ \frac{\partial f(x, t)}{\partial x} \approx \frac{\Delta f}{\Delta x} = \frac{f(x + \Delta x, t) - f(x, t)}{\Delta x} \end{gathered}

上式只是一种近似方式,除此之外还有很多其它近似方式。

指定初始条件(initial condition)和边界条件(boundary condition)后,通过求解差分方程组可以让系统以步长 Δt\Delta t 向前演化。

显式格式(explicit scheme)是指,其它值都取在当前时刻,唯一未知的值 f(t+Δt)f(t+\Delta t) 通过这些已知值表示。隐式格式(implicit scheme)将当前值和后一步的值混合起来,比如 fxf(x+Δx,t+Δt)f(x,t+Δt)Δx\frac{\partial f}{\partial x}\approx\frac{f(x+\Delta x,t+\Delta t) - f(x,t+\Delta t)}{\Delta x},此时必须求解代数方程组。

差分格式的选择影响算法的各个方面。最明显的就是精度(accuracy)。对于有限步长,导数的不同近似方式会产生不同的效果。差分格式的稳定性(stability)就是数值误差随时间的增长速度。虽然可以接受数值解是不精确的,但是不稳定的结果没有任何意义,因此完全应该避免不稳定的格式。通常显式格式要么不稳定,要么在步长较大时变得不稳定,而隐式格式一般无条件稳定。隐式格式允许更大的步长,因而更少的步数;而显示格式本身是简洁的。

stability.png

很多时候,只是计算方程中的必要项已经是一个困难且耗时的任务。比如,在刚体或布料仿真中,大多数作用在系统上的力源于物体碰撞。因此,动画中每一步都需要求解一个纯几何的碰撞检测问题。此时,那些计算受力次数较少的格式可能显著地节约计算资源。

物理动画本身也有局限性:难以控制动画结果。制作者一般只能调整初始条件,物理参数,或人为引入一项来修改方程。这需要深入理解其中的物理和数值方法。

程序式动画

在制作者指导下,用一个数学函数精确地输出所需运动,称为程序式动画(procedural animation),物理动画是一个例子。程序式动画不一定非要解方程,也可以直接选定某个数学函数来模拟动画。对这些函数加入时间依赖,可以更容易地生成某些复杂动画,同时保证视觉质量。

移动图案生成函数(pattern-generating function)的参数可以得到一个依赖时间的版本,比如匀速运动的噪声:timenoise(x,t)=noise(x+vt)\text{timenoise}(\vec{x},t)=\text{noise}(\vec{x}+\vec{v}t)。另一种方法是对图案生成函数的参数做动画,它尤其适用于外观随时间显著变化的情形。

有些程序式动画的时间依赖更具整体性。最简单的元胞自动机(cellular automata)作用在二维矩形网格上,用户提供一定的规则便可以生成随时间变化的图案。英国数学家 John Conway 在 1970 年发明的二维生命游戏元胞自动机的规则如下:

  1. 恰有三个邻居存活的死元胞将会变成活元胞;
  2. 有两个或三个活的邻居的活元胞仍然存活;
  3. 其它情况下,元胞将要死去或仍然死去。

更复杂的自动机同时作用在多个可能是浮点数的三维网格上,它们可用于模拟云的动力学和其它的气体现象,或是生物系统。然而,程序式技术的常见问题是:如何构造新函数或调节已有函数的参数以生成想要的行为。

Game-of-Life.png

最早由 Astrid Lindenmayer 提出的 L-系统(L-systems)技术,最初用于描述生物体。它基于语法(grammars)和一组迭代规则来重写符号串。符号分两类:终结符(terminal symbols)、非终结符(nonterminal symbols)。终结符用于表示那些想要用语法表示的元素。基于它们的含义,可以描述树、树枝的结构,建筑物、整个城市,或是编程语言、自然语言等。动画中,L-系统常用于表示植物,相应的终结符就是几何建模系统的指令。非终结符没有直接的含义,最终用终结符重写。

从一个特定的非终结符开始,每次对当前字符串应用语法规则,比如将当前所有非终结符进行替换,最终得到一个只有终结符的字符串,这些表示建模指令的字符串用于输出真实几何,不断替换的过程就是整个动画。

比如,考虑如下的终结符:

  • @@:在当前位置放一个叶子;
  • ff:将当前位置向前移动几个单位;
  • ++:将当前方向绕 Z 轴转动 60 度;
  • [[:将当前位置和方向入栈;
  • ]]:将当前位置和方向出栈。

非终结符:

  • SAS\rightarrow A
  • A[+B]fAA\rightarrow [+B]fA
  • ABA\rightarrow B
  • BfBB\rightarrow fB
  • Bf@B\rightarrow f@

假定起始符为 SS,可按语法规则得到如下的字符串序列:

SA[+B]fA[+fB]f[+B]fA[+ff@]f[+fB]fB[+ff@]f[+ff@]ff@\begin{aligned} S &\mapsto A \mapsto [+B]fA \mapsto [+fB]f[+B]fA \\ &\mapsto [+ff@]f[+fB]fB \mapsto [+ff@]f[+ff@]ff@ \end{aligned}

这样的 L-系统可以描述植物生长:

L-systems.png

同一个非终结符可以有多个不同的替换规则,选择哪个可以依赖于附近的字符——语境敏感性(context-sensitivity),也可以是随机的——随机 L-系统(stochastic L-systems)。复杂的规则可能还会和环境交互,比如修剪成特定形状;还可以有与符号相关联的参数,控制所发出的几何命令。

群体动画

很多时候,都需要群体中每个个体在动态环境中的行为协调一致。如果只有几个物体,制作者可使用 AI 系统,基于某些高层级的目标,自动为每个物体制定即时任务,规划它们的运动,这种物体也称为 autonomous objects

但是,随着群体规模增长,只有有限的个体智力才能让群体表现出协调的运动。这种 flocking 是一种涌现行为(emergent behavior),当只有近邻个体间存在有限相互作用时可以产生。任何时刻个体——也称为 boid(bird-oid object)——的运动,是多种——通常是对立的——趋势平衡后的结果。首先是外力 FF,在外力作用下物体新的速度:

vnewphysics=vold+FΔtm\vec{v}_{\text{new}}^{\text{physics}} = \vec{v}_{\text{old}} + \frac{F\Delta t}{m}

其次,boid 需要对整个环境以及其它个体做出响应。碰撞规避(collision avoidance)就是主要的响应结果之一。由于个体的视场有限,因此只需关心附近的情况。通常最简单的策略是,在每个个体附近设置一个排斥力场,由此给出了速度向量 vnewcol_avoid\vec{v}_{\text{new}}^{\text{col\_avoid}}。与其它个体的相互作用可通过同时施加不同的转向行为来模拟,这产生了多个额外的速度向量 vnewsteer\vec{v}_{\text{new}}^{\text{steer}};最常见的有,避免碰撞,保证群体凝聚力(flock cohesion),调节 boid 的速度与附近的平均朝向对齐。最后,还需要速度向量 vnewgoal\vec{v}_{\text{new}}^{\text{goal}} 以实现所需的全局目标。

一旦所有 vnew\vec{v}_{\text{new}} 都已经确定,就可以基于优先级得到最终的速度向量。通常,碰撞规避和速度匹配的优先级最高。一个 boid 能使用的加速度总量是固定的,根据优先级依次为每个驱动力分配其中一部分;可用的加速度用完后,优先级更低的驱动力可以直接忽略。同时还应满足真实物体的物理限制,比如当加速度或速度过大时截断为真实值。依个体的内部复杂度,最后可能还需要把速度向量转为特定参数以控制 boid 的运动。

boid-control.png

粒子系统(particle systems)是一种更简单,但仍然很有用的群体控制。系统中粒子数目通常比 boid 数目大得多。而且,因为动画过程中每一步都会有新的粒子生成,旧的粒子湮灭,总粒子数会随着时间涨落。粒子间通常完全独立,与环境的相互作用仅通过外力以及与其它物体的碰撞,而非粒子间碰撞规避。动画中每一步,都会先生成一些新的粒子并赋予初始参数,消灭掉一些旧的粒子,然后对存留的粒子计算必要的受力并更新速度和位置。

粒子系统所有参数,如粒子总数、粒子寿命、粒子的初始速度和位置,均由制作者直接控制。主要应用包括:烟花、爆炸、液体喷雾、烟与火、以及其它没有清晰边界的模糊物体和现象。为了实现真实外观,还需对所有参数引入随机性。控制粒子系统的运动通常需要生成特定的力,比如:一旦到达某一位置就沿特定方向吹风,加入某些吸引中心等。易于控制、实现简单是粒子系统的主要优势,而真实度一般不及物理动画。

particle-systems.png