- 在图形学中可以理解为对建模以及几何的拓展,在不同时间有不同的几何形体,把三维的模型延申到了时间的维度上 An extension of modeling, Represent scene models as a function of time
- 按顺序播放一系列图片就形成了动画 Output: sequence of images that when viewed sequentially provide a sense of motion
- 电影通常是每秒24帧 Film: 24 frames per second
- 视频通常每秒30帧就比较流畅,游戏可能需要60帧甚至对连贯性要求比较高达到144帧 Video (in general): 30 fps
- VR设备要达到每秒90帧才不会产生眩晕感 Virtual reality: 90 fps
- 动画的历史:壁画->圆盘播放-> 第一部电影:这里认为拍摄马奔跑是第一部电影-> 第一部完整长度手绘动画电影《白雪公主》-> 计算机动画 里程碑式的电影《侏罗纪公园》-> 第一部全CG完整长度动画电影《玩具总动员》
1 动画分类
1.1 关键帧动画(Keyframe)
在关键的位置上制作一帧画面,而中间的过程采用自动生成的方式形成动画

关键帧动画的技术问题在于如何在关键帧之间插值,线性的插值通常不够好,使用曲线/样条来控制能达到比较好的效果 Linear interpolation usually not good enough, Recall splines for smooth / controllable interpolation

1.2 物理模拟(Physical Simulation)
已知物体质量,给物体应用一个力,则可以算出它的加速度,再引入时间,就能算当前时刻速度、位置等一系列信息,然后就可以动态的更新下一时刻的信息从而产生动画了。

物理仿真/模拟背后的核心思想就是构建物理模型,分析受力,从而算出某时刻的加速度、速度、位置等息。只要能够正确建立物理模型,一定可以得出正确的物理模拟结果。


1.3 质点弹簧系统(Mass Spring System)


简单、理想化的弹簧(帮助理解)
- 没有长度
- 弹簧拉多长就产生多大的力
- 受力计算方法如下,只有将质点向内拉的力,ks 为弹性系数。

正常的有长度的弹簧
- 弹簧被拉长会产生向内收缩的力,被压短则产生外推力
- 弹簧常态长度为 l,受力大小依然形变大小成正比,计算方式如下

存在的问题:永远震荡不会停止,动能与势能不停转变,没有损耗
(1)简单的引入阻尼(错误方法)
- 介绍如下记号,打点表示对时间 t 求导,位移x求导1次是速度,求导2次为加速度。(简单物理)

简单的阻尼表示:与速度反向相反,在数学中的表示如下,其中kd 为阻尼系数,

引入简单阻尼产生的bug:他会减慢一切运动。
(理解:我们引入阻尼真正的目的是让震荡的弹簧不会永远震荡下去,会因为阻尼的存在而停下。目前引入的阻尼确实能达到目的,但还会阻止一个处于常态的弹簧的一切非震荡运动。比如弹簧两端相对静止,但是处于自由落体状态,都有向下的速度,则此阻尼力会使他落得越来越慢,这不正常)
(2)引入弹簧内部阻尼(正确方法)
限制阻尼只发生在弹簧内部,只作用于因弹簧形变引起的运动。
未达目的,必须引入相对运动,公式更改如下(注意这里算阻尼跟伸缩长度没关系)

| 参数 | 说明 |
|---|---|
| kd | 阻尼系数 |
| \[ \frac{\boldsymbol{b} – \boldsymbol{a}}{\|\boldsymbol{b} – \boldsymbol{a}\|} \] | 单位向量,表方向,a指向b |
| \[ \frac{\boldsymbol{b} – \boldsymbol{a}}{\|\boldsymbol{b} – \boldsymbol{a}\|} \cdot (\dot{\boldsymbol{b}} – \dot{\boldsymbol{a}}) \] | 左边是单位向量ab,右边是b点相对于a的速度向量,点乘表示把速度投影到ab方向上(注意是标量了!),投影目的是:防止两端相对静止,一个球绕另一个球做圆周运动而出现的阻尼。 |
上面只考虑一节弹簧,我们也得考虑弹簧组合起来的结构
- 假设要模拟一块布,简单使用方形的连接方式不能很好的表现:
- 斜向拉时,该结构不能抵抗切变的力,会发生形变 This structure will not resist shearing
- 如果不是平面内的力,布会有抵抗的性质(比如布很难完美对折),然而这种结构显然不会发生抵抗 This structure will not resist out-of-plane bending…

- 为了优化,加入斜对角线的连线
- 这种结构可以抵抗切边的力但具有各向异性 This structure will resist shearing but has anisotropic bias
- 依然不能抵抗非平面内的力 This structure will not resist out-of-plane bending either…

- 继续优化
- 另一个对角线拉也能抵抗切变 This structure will resist shearing, Less directional bias
- 依然不能抵抗非平面内的力 This structure will not resist out-of-plane bending either…

- 使用跳跃连接
- 可以抵抗剪切 This structure will resist shearing, Less directional bias.
- 也可以抵抗非平面的力(红色弹簧间的力是很弱的) This structure will resist out-of-plane bending, Red springs should be much weaker

质点弹簧系统在一定程度上可以模拟布料,但也有其他方法如有限元法,可以模拟力的传导 Finite Element Method (FEM)
- 建模一堆微小粒子,定义每个粒子会受到的力(粒子之间的力、来自外部的力、碰撞等)。内部的有:
- 引力与斥力 Attraction and repulsion forces
- 重力、电磁力… Gravity, electromagnetism
- 弹簧力、推力… Springs, propulsion, …
- 阻力 Damping forces
- 摩擦力、空气阻力、粘滞力… Friction, air drag, viscosity, …
- 碰撞 Collisions
- 引力与斥力 Attraction and repulsion forces
粒子系统也可以描述群体之中的个体,如鸟群中可以定义个体的运动属性,通过粒子模拟来解出来结果,其他还有分子结构、人群等

1.4 运动学 Kinematics
1.4.1 正向运动学 Forward Kinematics
- 骨骼的连接方式
骨骼系统
- 拓扑结构
- 关节相互的几何联系
- 树状结构
关节类型
- Pin(滑车关节):平面内旋转
- Ball(球窝关节):一部分空间内旋转
- Prismatic joint(导轨关节):允许平移

例:由两部分骨骼构成的2D的手臂
第一部分旋转θ1,第二部分旋转θ2,问端点p的位置在哪?
- 计算方法非常简单,说明只要定义好骨骼连接方式,任何时刻只要知道角度,就能算出尖端p应该停在哪里

正向运动学的缺点是使用太过于物理,艺术家处理时需要更直观的拖拽末端的方法
1.4.2 逆向运动学 Inverse Kinematics(IK)
正向运动学:通过骨骼旋转角度计算尖端位置
逆向运动学:通过控制 尖端位置,反算出应该旋转多少

例:依然是最简单的2部分组成的手臂
知道p点位置,反算骨骼角度

逆运动学存在的问题
- 解并不唯一,两种不同的角度组合方式都能让p处于目标位置

- 并不是一定有解,即会有无解的情况,如下图尖端活动范围有限

人们会采用很多优化方法(如梯度下降)去解决逆向运动学产生的问题,而不是真的求角度

1.5 绑定 Rigging
绑定是一种更高级别的角色控制方法,允许更快速和直观地修改姿势、变形、表情等
- 在角色身体、脸部等位置创造一系列控制点,艺术家通过调整控制点的位置,带动脸部其他从点移动,从而实现表情变化,动作变化等。

不同的两个动作可以使用Blend Shapes插值两个动作
1.6 动作捕捉 Motion Capture
动作捕捉能够得到真实的动作,但是操作复杂、成本高、有时难以捕捉到好的数据、不能处理夸张的动画效果

众多动捕设备中最常用的还是光学动捕设备
- 最重要的被遮挡问题,可以通过增加摄像机等方法避免

- 得到的数据是每个控制点在三维空间中的运动数据

阿凡达面部动捕

1.7 动画生产流程

2. 仿真模拟 Simulation
2.1 单粒子模拟 Single Particle Simulation
- 假设粒子的运动由速度矢量场决定,速度场是关于位置和时间的函数:v(x, t)
- 速度场:定义质点在任何时刻在场中任何位置的速度。给个位置和时间,就能知道速度

- 计算速度场内的粒子位置需要求解一阶常微分方程 Computing position of particle over time requires solving a first-order ordinary differential equation(ODE)

2.1.1 欧拉方法 Euler’s Method (a.k.a. Forward Euler, Explicit Euler)
- 简单迭代方法,用上一时刻的信息推导这一时刻的信息

很常用、非常不准确、非常不稳定
- 不准确性:
- 如果采用较大的Δt,误差会很大,并且误差会积累
- 可以通过缩小Δt来控制误差

- 不稳定性
- 与误差无关,不管取多小的时间差,最后的结果都会和正确结果相差无限远。
- 假设有一个圆形速度场,速度一直是垂直于直径的,按理来说粒子在这里会一直做圆周运动不会飞出去,但是用欧拉方法,不管选择多小的步长Δt,粒子都会飞出去

假设如下速度场,粒子初始点在下方,会被下方速度场推到右上方,又被上方的速度场推向右下方,如此交替往复。
但是这个速度场,这样的粒子初始位置,正确的路径应该是沿着速度场,缓慢向中间靠拢,最后收敛于中间

- 使用数值方法解微分方程都会出现问题
- 对于不准确性,在模拟中会产生错误,但是在渲染中其实并不太在意这种误差 Errors at each time step accumulate. Accuracy decreases as simulation proceeds. Accuracy may not be critical in graphics applications
- 对于不稳定性,可能会加剧错误,在模拟中不能被忽视 Errors can compound, causing the simulation to diverge even when the underlying system does not
2.1.2 改善不稳定性
2.1.2.1 中点法 Midpoint method
- 先计算a点 Position-based / Verlet integration
- 取原点和a点的中点b Compute derivative at midpoint of Euler step (b)
- 使用中点b的速度更新位置 Update position using midpoint derivative (c)

$$ \begin{cases} x_{mid} = x(t) + \Delta t/2 \cdot v(x(t), t) \\ x(t + \Delta t) = x(t) + \Delta t \cdot v(x_{mid}, t) \end{cases} $$
2.1.2.2 变形欧拉法(Modified Euler)
取开始和结束位置的速度的平均速度,再计算最终位置
$$ \begin{align*} \boldsymbol{x}^{t+\Delta t} &= \boldsymbol{x}^t + \frac{\Delta t}{2} \left( \dot{\boldsymbol{x}}^t + \dot{\boldsymbol{x}}^{t+\Delta t} \right) \\ \dot{\boldsymbol{x}}^{t+\Delta t} &= \dot{\boldsymbol{x}}^t + \Delta t \, \ddot{\boldsymbol{x}}^t \\ \boldsymbol{x}^{t+\Delta t} &= \boldsymbol{x}^t + \Delta t \, \dot{\boldsymbol{x}}^t + \frac{(\Delta t)^2}{2} \ddot{\boldsymbol{x}}^t \end{align*} $$
2.1.2.3 自适应改变步长 Adaptive step size
- 基于误差估计,自动选择步长Δt
- 非常实用
- 但是可能会让步长变得特别小
实现步骤
- 先用步长 T 做一次欧拉计算 XT
- 再用步长 T/2 做两次欧拉得到 XT/2
- 比较两次位置误差error ||XT – XT/2||
- if(error > threshold) 减少步长,重复上面步骤
所以如果误差error很大,就会往下细分,直到误差够小为止,这样可以模拟出比较好的效果如右图。拐弯的地方一般步长会比较小

2.1.2.4 隐式(后向)的欧拉方法 Implicit(Backward) Euler methods
- 跟欧拉方法唯一的区别是:用下一个时刻的速度和加速度来计算下一个时刻的位置和速度
- 求解的时候并不好求,需要使用寻根算法如牛顿法 Use root-finding algorithm, e.g. Newton’s method
- 该方法可以提供比较好的稳定性 Offers much better stability

怎么定义稳定性?
局部截断误差(local truncation error):每一步的误差
全局累积误差(total accumulated error):总的累积误差
but上面两个数值并不重要,重要的是步长h 跟 误差的关系(用阶来描述)
PS:时间复杂度里的大 O 描述“算得多快/多慢”,数值分析里的大 O 描述“误差降得多快”。数学记号一样,但比较的是不同对象,极限方向也不同。

隐式的欧拉方法是1阶,阶数越高代表方法越好
局部误差:O(h)
全局误差:\(O(h^2)\)
O(h)意思是一阶,步长Δt变为原来的一半,误差也变成原来的一半
\(O(h^2)\)则表示,步长变为原来的一半,误差变成原来的四分之一
下面介绍一种更好的4阶的方法
2.1.2.5 龙格库塔系列的方法 Runge-Kutta Families
这类方法擅长解非线性的常微分方程,其中一个四阶的方法RK4方法应用最广 Especially good at dealing with non-linearity, It’s order-four version is the most widely used, a.k.a. RK4

2.2 刚体模拟 Rigid Body Simulation
- 刚体的运动不会发生形变,内部所有的点都按照同种方式运动,也相当于一个粒子,但是计算时会注意更多物理量 Similar to simulating a particle; Just consider a bit more properties

2.3 流体模拟 Fluid simulation
- 这里介绍一种简单的Position-Based的流体模拟的方法
- 假设水由许多刚体小球组成的 Assuming water is composed of small rigid-body spheres
- 假设水是不能被压缩的(即任何位置密度一致)
- 所以,只要某处的密度发生了变化,就应该通过改变粒子的位置来“修正” So, as long as the density changes somewhere, it should be “corrected” via changing the positions of particles
- 为了修正,我们要知道一个小球位置的变化对其周围密度的影响,即任何一个点的密度梯度(也就是导数) You need to know the gradient of the density anywhere w.r.t. each particle’s position
- 这里就是机械学习中的梯度下降
- 修正位置的过程是一个梯度下降的过程 Update? Just gradient descent!
- 这样简单的模拟最后会一直运动停不下来,我们可以人为的加入一些能量损失
- 在模拟大量物体运动的两种思路:拉格朗日与欧拉方法 Two different views to simulating large collections of matters: Lagrangian vs. Eulerian:
- 拉格朗日法(质点法):类似于模拟水,我们认为水是许多不同刚体水滴组成,模拟好每一个水滴,知道每一个在某个时刻应该在什么位置,即可很好的模拟水。
- 欧拉方法(网格法),不同于之前的解微分方程的欧拉方法,这里是指把空间分成一个个网格,考虑一个网格随着时间不同的变化

- 现在也有一种混合方法 Material Point Method (MPM):粒子将属性传递给网格,模拟的过程在网格力做,然后把结果插值回粒子 particles transfer properties to the grid, grid performs update, then interpolate back to particles

3.下一步应该怎么学
- 有能力手写渲染器或掌握一个API了
- 如果想深入学习实时渲染去看《real time rendering》,对照任何OpenGL书就可以学下去
- 如果想学习DirectX,怎样和实时光线追踪结合,或者Vulkan搞一些跨平台的东西都差不多
- 几何部分需要深厚的数学基础:微分几何、离散微分几何、拓扑、流形等等知识
- 光线传播还有实时高质量渲染GAMES202
- 模拟部分有胡渊鸣的GAMES201
Reference







