Skip to content

动画与模拟(基本概念、质点弹簧系统、运动学)

前面的章节主要研究“某一帧画面如何生成”。从这一章开始,我们把时间放进图形学里:物体不再只是摆在某个位置,而是会移动、变形、碰撞、摆动和响应外力。

动画与模拟的共同目标都是让画面随时间变化。区别在于,动画更强调由创作者控制运动结果,模拟更强调由规则、力和约束推导运动结果。实际项目里两者经常混合:角色走路可以由关键帧控制,衣服、头发和尘土则由物理模拟补充。

静态渲染里,我们只关心某个时刻的场景状态。动画里,场景状态变成了时间的函数:

位置 = f(时间)
旋转 = f(时间)
形状 = f(时间)
材质参数 = f(时间)
相机参数 = f(时间)

因此动画系统首先要回答:在任意时间 t,场景中每个对象的状态是什么?

常见时间单位包括秒、帧和时间步长。秒是物理时间,帧是显示或渲染序列中的离散采样,时间步长则是模拟系统每次推进的间隔。

帧率 24 FPS:每秒显示 24 帧
帧率 60 FPS:每秒显示 60 帧
时间步长 1/60 秒:模拟每次推进约 0.0167 秒

动画不是连续时间本身,而是对连续运动的一系列采样。采样越稀疏,越容易出现跳动、穿透和不稳定;采样越密,计算成本越高。

动画和模拟都能生成运动,但它们的思考方式不同。

  • 关键帧动画。 创作者指定若干关键时刻的状态,系统在中间插值。
  • 程序动画。 由函数、噪声、规则或状态机生成运动。
  • 物理模拟。 根据力、质量、速度、约束和碰撞推进状态。
  • 混合动画。 先由动画给出目标,再由模拟产生二级运动和细节。

角色动画常常是关键帧、骨骼和程序控制的组合;布料、头发、软体和粒子效果则更依赖模拟。好的图形系统不会把二者割裂,而是让创作者能在“可控”和“自然”之间调节。

动画与模拟的基本链路

运动学只描述物体如何运动,不关心为什么这样运动。它关注位置、速度、加速度、旋转和插值。

最基本的一维运动可以写成:

速度 = 位置随时间的变化率
加速度 = 速度随时间的变化率

如果已知初始位置、初始速度和恒定加速度,可以得到:

x(t) = x0 + v0 t + 1/2 a t²
v(t) = v0 + a t

这类公式适合描述抛体、匀速移动、镜头推拉和简单弹道。它们很直观,但前提是运动规律已经被写死了。

关键帧动画的核心是插值。创作者只设置少量关键状态,系统负责补出中间状态。

常见插值方式包括:

  • 线性插值。 速度恒定,简单直接,但容易显得机械。
  • 缓入缓出。 开始和结束更慢,中间更快,符合很多自然运动。
  • 样条曲线。 用曲线控制运动轨迹和速度变化,适合更复杂的动画。
  • 球面线性插值。 常用于旋转插值,避免欧拉角插值带来的奇怪路径。

线性插值可以写成:

lerp(a, b, t) = (1 - t) a + t b

其中 t 从 0 到 1。这个公式看起来简单,但在图形学里到处出现:位置、颜色、权重、纹理采样、形变混合都离不开它。

动力学关心“为什么会这样运动”。最经典的关系是牛顿第二定律:

F = m a

也就是:

加速度 = 合力 / 质量

模拟系统通常保存每个对象的状态:

状态 = 位置、速度、质量、受力、约束

每个时间步中,系统会计算力,再把力转成加速度,然后更新速度和位置。这个过程叫时间积分。

  1. 读取当前状态。
    取得物体的位置、速度、质量和约束信息。
  2. 计算外力和内力。
    外力可以是重力、风力和用户输入;内力可以是弹簧力、阻尼力和形变恢复力。
  3. 由力得到加速度。
    根据 a = F / m 把合力转成运动变化趋势。
  4. 推进速度和位置。
    使用一个积分方法把状态从当前时间推进到下一时间。
  5. 处理约束与碰撞。
    修正穿透、限制距离、保持关节或固定点。
  6. 把结果交给渲染器。
    新状态成为下一帧画面的输入。

质点系统把物体简化成一组没有体积的点。每个点有位置、速度和质量。虽然单个质点很简单,但大量质点组合起来可以描述很多现象:

  • 粒子效果,如火花、烟尘、雨滴和雪花。
  • 软体或布料的离散节点。
  • 头发、绳索和链条上的采样点。
  • 群体运动中的个体位置。

质点系统的关键是:每个质点如何受力,以及质点之间是否有连接或约束。

质点弹簧系统用弹簧连接质点。弹簧会试图恢复到自己的静止长度,拉长时往回拉,压短时往外推。

常见弹簧力可以写成:

弹簧力 = -k (当前长度 - 静止长度) 方向

其中 k 是弹簧刚度。k 越大,弹簧越硬;k 越小,弹簧越软。

但只有弹簧力会让系统不停振荡,因此通常还要加入阻尼。阻尼会消耗运动能量,让摆动逐渐停下来:

阻尼力 ≈ -c 速度

其中 c 是阻尼系数。阻尼太小会抖动很久,阻尼太大则会显得迟钝甚至像被粘住。

交互辅助:质点、弹簧与时间步长

Section titled “交互辅助:质点、弹簧与时间步长”

下面的小程序用一个固定点和一个可运动质点演示弹簧模拟。你可以调节刚度、阻尼和时间步长,观察系统如何在稳定、柔软和抖动之间变化。

固定点通过弹簧拉住质点。刚度决定恢复力强弱,阻尼决定能量消失速度,时间步长决定每次模拟推进的跨度。

可以尝试:

  • 增大刚度,观察质点更快被拉回,但也更容易高频振荡。
  • 减小阻尼,观察系统持续摆动;增大阻尼,观察能量更快消失。
  • 增大时间步长,观察模拟更省计算但更容易不稳定。
  • 拖动“外力”滑块,观察同一个弹簧系统如何响应不同扰动。

模拟的难点不只是公式对不对,还包括时间步长是否合适。连续世界中的力是持续变化的,但计算机只能离散推进:

当前状态 → 经过 Δt → 下一状态

如果 Δt 太大,系统可能跳过重要变化。弹簧可能越抖越大,碰撞可能直接穿过物体,角色可能出现不自然的颤动。

因此实时图形系统常用固定时间步来更新物理,再把渲染帧插在物理结果之间。这样即使显示帧率波动,模拟也更容易保持一致。

约束用于限制系统状态。它不一定直接来自某个力,而是规定某些条件必须满足。

常见约束包括:

  • 两个质点之间的距离保持在某个范围。
  • 某个点固定在空间中。
  • 角色关节只能在有限角度内旋转。
  • 物体不能穿过地面或墙面。
  • 布料边缘挂在衣架或角色身体上。

约束让模拟更可控,也更接近实际需求。很多时候,项目里追求的不是完全真实,而是看起来合理、稳定、可导演。

碰撞处理负责发现物体是否相交,并把它们分开或改变速度。它通常分成两个阶段:

  • 碰撞检测。 判断哪些对象可能接触。
  • 碰撞响应。 根据接触点、法线、摩擦和弹性调整运动。

碰撞非常容易影响稳定性。一个小小的穿透如果没有处理好,可能在下一帧被放大成弹飞、抖动或卡住。因此很多实时系统会使用简化碰撞体,例如球、盒子、胶囊和粗略网格,而不是直接用完整渲染模型做物理碰撞。

复杂对象通常有层级结构。角色有骨骼,机械臂有关节,车辆有车身和轮子。父节点运动会带动子节点运动,这和前面学过的模型变换矩阵直接相关。

例如角色手掌的位置不仅取决于手掌自己的局部变换,还取决于小臂、上臂、肩膀和身体的变换。动画系统会沿层级把局部变换组合成世界变换,再交给渲染器绘制。

这说明前面学过的变换、矩阵和坐标系并没有结束,它们是动画系统的基础设施。

本章把图形学从静态画面推进到了随时间变化的场景:

  1. 动画把场景状态看作时间的函数。
  2. 关键帧动画通过插值连接少量创作者指定的状态。
  3. 运动学描述“如何运动”,动力学解释“为什么运动”。
  4. 物理模拟通常根据力、质量、速度和约束逐步更新状态。
  5. 质点弹簧系统能用简单规则表现弹性、摆动、绳索、布料和软体的基础行为。
  6. 刚度、阻尼和时间步长共同影响模拟的观感与稳定性。
  7. 约束和碰撞让模拟结果更符合实际场景需求。

下一章会继续深入模拟背后的数值问题:如何求解常微分方程,为什么显式欧拉方法容易发散,刚体和流体又如何在同一套“状态随时间变化”的思想下被建模。