动画与模拟(基本概念、质点弹簧系统、运动学)
前面的章节主要研究“某一帧画面如何生成”。从这一章开始,我们把时间放进图形学里:物体不再只是摆在某个位置,而是会移动、变形、碰撞、摆动和响应外力。
动画与模拟的共同目标都是让画面随时间变化。区别在于,动画更强调由创作者控制运动结果,模拟更强调由规则、力和约束推导运动结果。实际项目里两者经常混合:角色走路可以由关键帧控制,衣服、头发和尘土则由物理模拟补充。
时间是动画的坐标轴
Section titled “时间是动画的坐标轴”静态渲染里,我们只关心某个时刻的场景状态。动画里,场景状态变成了时间的函数:
位置 = f(时间)旋转 = f(时间)形状 = f(时间)材质参数 = f(时间)相机参数 = f(时间)因此动画系统首先要回答:在任意时间 t,场景中每个对象的状态是什么?
常见时间单位包括秒、帧和时间步长。秒是物理时间,帧是显示或渲染序列中的离散采样,时间步长则是模拟系统每次推进的间隔。
帧率 24 FPS:每秒显示 24 帧帧率 60 FPS:每秒显示 60 帧时间步长 1/60 秒:模拟每次推进约 0.0167 秒动画不是连续时间本身,而是对连续运动的一系列采样。采样越稀疏,越容易出现跳动、穿透和不稳定;采样越密,计算成本越高。
动画与模拟的关系
Section titled “动画与模拟的关系”动画和模拟都能生成运动,但它们的思考方式不同。
- 关键帧动画。 创作者指定若干关键时刻的状态,系统在中间插值。
- 程序动画。 由函数、噪声、规则或状态机生成运动。
- 物理模拟。 根据力、质量、速度、约束和碰撞推进状态。
- 混合动画。 先由动画给出目标,再由模拟产生二级运动和细节。
角色动画常常是关键帧、骨骼和程序控制的组合;布料、头发、软体和粒子效果则更依赖模拟。好的图形系统不会把二者割裂,而是让创作者能在“可控”和“自然”之间调节。
运动学:只描述运动
Section titled “运动学:只描述运动”运动学只描述物体如何运动,不关心为什么这样运动。它关注位置、速度、加速度、旋转和插值。
最基本的一维运动可以写成:
速度 = 位置随时间的变化率加速度 = 速度随时间的变化率如果已知初始位置、初始速度和恒定加速度,可以得到:
x(t) = x0 + v0 t + 1/2 a t²v(t) = v0 + a t这类公式适合描述抛体、匀速移动、镜头推拉和简单弹道。它们很直观,但前提是运动规律已经被写死了。
插值:连接关键帧
Section titled “插值:连接关键帧”关键帧动画的核心是插值。创作者只设置少量关键状态,系统负责补出中间状态。
常见插值方式包括:
- 线性插值。 速度恒定,简单直接,但容易显得机械。
- 缓入缓出。 开始和结束更慢,中间更快,符合很多自然运动。
- 样条曲线。 用曲线控制运动轨迹和速度变化,适合更复杂的动画。
- 球面线性插值。 常用于旋转插值,避免欧拉角插值带来的奇怪路径。
线性插值可以写成:
lerp(a, b, t) = (1 - t) a + t b其中 t 从 0 到 1。这个公式看起来简单,但在图形学里到处出现:位置、颜色、权重、纹理采样、形变混合都离不开它。
动力学:由力推导运动
Section titled “动力学:由力推导运动”动力学关心“为什么会这样运动”。最经典的关系是牛顿第二定律:
F = m a也就是:
加速度 = 合力 / 质量模拟系统通常保存每个对象的状态:
状态 = 位置、速度、质量、受力、约束每个时间步中,系统会计算力,再把力转成加速度,然后更新速度和位置。这个过程叫时间积分。
- 读取当前状态。
取得物体的位置、速度、质量和约束信息。 - 计算外力和内力。
外力可以是重力、风力和用户输入;内力可以是弹簧力、阻尼力和形变恢复力。 - 由力得到加速度。
根据a = F / m把合力转成运动变化趋势。 - 推进速度和位置。
使用一个积分方法把状态从当前时间推进到下一时间。 - 处理约束与碰撞。
修正穿透、限制距离、保持关节或固定点。 - 把结果交给渲染器。
新状态成为下一帧画面的输入。
质点系统把物体简化成一组没有体积的点。每个点有位置、速度和质量。虽然单个质点很简单,但大量质点组合起来可以描述很多现象:
- 粒子效果,如火花、烟尘、雨滴和雪花。
- 软体或布料的离散节点。
- 头发、绳索和链条上的采样点。
- 群体运动中的个体位置。
质点系统的关键是:每个质点如何受力,以及质点之间是否有连接或约束。
质点弹簧系统用弹簧连接质点。弹簧会试图恢复到自己的静止长度,拉长时往回拉,压短时往外推。
常见弹簧力可以写成:
弹簧力 = -k (当前长度 - 静止长度) 方向其中 k 是弹簧刚度。k 越大,弹簧越硬;k 越小,弹簧越软。
但只有弹簧力会让系统不停振荡,因此通常还要加入阻尼。阻尼会消耗运动能量,让摆动逐渐停下来:
阻尼力 ≈ -c 速度其中 c 是阻尼系数。阻尼太小会抖动很久,阻尼太大则会显得迟钝甚至像被粘住。
交互辅助:质点、弹簧与时间步长
Section titled “交互辅助:质点、弹簧与时间步长”下面的小程序用一个固定点和一个可运动质点演示弹簧模拟。你可以调节刚度、阻尼和时间步长,观察系统如何在稳定、柔软和抖动之间变化。
可以尝试:
- 增大刚度,观察质点更快被拉回,但也更容易高频振荡。
- 减小阻尼,观察系统持续摆动;增大阻尼,观察能量更快消失。
- 增大时间步长,观察模拟更省计算但更容易不稳定。
- 拖动“外力”滑块,观察同一个弹簧系统如何响应不同扰动。
稳定性与时间步长
Section titled “稳定性与时间步长”模拟的难点不只是公式对不对,还包括时间步长是否合适。连续世界中的力是持续变化的,但计算机只能离散推进:
当前状态 → 经过 Δt → 下一状态如果 Δt 太大,系统可能跳过重要变化。弹簧可能越抖越大,碰撞可能直接穿过物体,角色可能出现不自然的颤动。
因此实时图形系统常用固定时间步来更新物理,再把渲染帧插在物理结果之间。这样即使显示帧率波动,模拟也更容易保持一致。
约束用于限制系统状态。它不一定直接来自某个力,而是规定某些条件必须满足。
常见约束包括:
- 两个质点之间的距离保持在某个范围。
- 某个点固定在空间中。
- 角色关节只能在有限角度内旋转。
- 物体不能穿过地面或墙面。
- 布料边缘挂在衣架或角色身体上。
约束让模拟更可控,也更接近实际需求。很多时候,项目里追求的不是完全真实,而是看起来合理、稳定、可导演。
碰撞处理负责发现物体是否相交,并把它们分开或改变速度。它通常分成两个阶段:
- 碰撞检测。 判断哪些对象可能接触。
- 碰撞响应。 根据接触点、法线、摩擦和弹性调整运动。
碰撞非常容易影响稳定性。一个小小的穿透如果没有处理好,可能在下一帧被放大成弹飞、抖动或卡住。因此很多实时系统会使用简化碰撞体,例如球、盒子、胶囊和粗略网格,而不是直接用完整渲染模型做物理碰撞。
动画系统中的层级
Section titled “动画系统中的层级”复杂对象通常有层级结构。角色有骨骼,机械臂有关节,车辆有车身和轮子。父节点运动会带动子节点运动,这和前面学过的模型变换矩阵直接相关。
例如角色手掌的位置不仅取决于手掌自己的局部变换,还取决于小臂、上臂、肩膀和身体的变换。动画系统会沿层级把局部变换组合成世界变换,再交给渲染器绘制。
这说明前面学过的变换、矩阵和坐标系并没有结束,它们是动画系统的基础设施。
本章把图形学从静态画面推进到了随时间变化的场景:
- 动画把场景状态看作时间的函数。
- 关键帧动画通过插值连接少量创作者指定的状态。
- 运动学描述“如何运动”,动力学解释“为什么运动”。
- 物理模拟通常根据力、质量、速度和约束逐步更新状态。
- 质点弹簧系统能用简单规则表现弹性、摆动、绳索、布料和软体的基础行为。
- 刚度、阻尼和时间步长共同影响模拟的观感与稳定性。
- 约束和碰撞让模拟结果更符合实际场景需求。
下一章会继续深入模拟背后的数值问题:如何求解常微分方程,为什么显式欧拉方法容易发散,刚体和流体又如何在同一套“状态随时间变化”的思想下被建模。