Simulation

自动关联目录:Simulation

物理仿真回答的是:给定当前状态,下一时刻的状态是什么。

统一的数学框架

几乎所有物理仿真都在解同一类方程——常微分方程(ODE):

ddt(xv)=(vM−1f(x,v,t))\frac{d}{dt}\begin{pmatrix} \mathbf{x} \\ \mathbf{v} \end{pmatrix} = \begin{pmatrix} \mathbf{v} \\ \mathbf{M}^{-1}\mathbf{f}(\mathbf{x}, \mathbf{v}, t) \end{pmatrix}

符号含义
x\mathbf{x}位置(所有粒子的)
v\mathbf{v}速度
M\mathbf{M}质量矩阵
f\mathbf{f}合力(重力、弹力、压力、阻尼…)

不同的仿真对象,差别只在f\mathbf{f} 和M\mathbf{M} 的形式:

对象f\mathbf{f} 的来源章节
质点弹簧弹簧力 + 阻尼质点-弹簧系统
刚体外力 + 约束(角动量、转动惯量)刚体
柔体(有限元)弹性内力(应力-应变)柔体
流体压力 + 粘性 + 重力(Navier-Stokes)流体

时间积分:仿真的核心

连续方程无法解析求解(除非极简单),所以离散化成一步步推进。

x[1]=x[0]+Δt v[0],v[1]=v[0]+Δt M−1f[0]\mathbf{x}^{[1]} = \mathbf{x}^{[0]} + \Delta t \, \mathbf{v}^{[0]}, \quad \mathbf{v}^{[1]} = \mathbf{v}^{[0]} + \Delta t \, \mathbf{M}^{-1}\mathbf{f}^{[0]}

这是显式欧拉(Forward Euler)。

三种基本积分器

方法更新公式稳定性精度
显式欧拉x[1]=x[0]+Δt v[0]\mathbf{x}^{[1]} = \mathbf{x}^{[0]} + \Delta t\,\mathbf{v}^{[0]}
v[1]=v[0]+Δt M−1f[0]\mathbf{v}^{[1]} = \mathbf{v}^{[0]} + \Delta t\,\mathbf{M}^{-1}\mathbf{f}^{[0]}
差(会爆炸)一阶
隐式欧拉x[1]=x[0]+Δt v[1]\mathbf{x}^{[1]} = \mathbf{x}^{[0]} + \Delta t\,\mathbf{v}^{[1]}
v[1]=v[0]+Δt M−1f[1]\mathbf{v}^{[1]} = \mathbf{v}^{[0]} + \Delta t\,\mathbf{M}^{-1}\mathbf{f}^{[1]}
好(无条件稳定)一阶
半隐式(Symplectic)欧拉v[1]=v[0]+Δt M−1f[0]\mathbf{v}^{[1]} = \mathbf{v}^{[0]} + \Delta t\,\mathbf{M}^{-1}\mathbf{f}^{[0]}
x[1]=x[0]+Δt v[1]\mathbf{x}^{[1]} = \mathbf{x}^{[0]} + \Delta t\,\mathbf{v}^{[1]}
好(能量有界)一阶

半隐式欧拉是实时仿真的默认选择——只是把"先更新位置"改成"先更新速度",稳定性天差地别。

为什么显式欧拉会爆炸:

弹簧: f = -k(x - L)
显式欧拉: v += dt * (-k/m)(x - L)
每次迭代把能量放大一点
→ 刚度 k 大或 dt 大时,指数发散

稳定性条件(显式方法):

Δt≤2ωmax⁡或Δt≤cmk\Delta t \le \frac{2}{\omega_{\max}} \quad \text{或} \quad \Delta t \le c\sqrt{\frac{m}{k}}

刚度越大(kk 大),允许的时间步越小。这就是"硬弹簧难仿真"的原因。

隐式方法为什么稳定

v[1]=v[0]+Δt M−1f(x[1])\mathbf{v}^{[1]} = \mathbf{v}^{[0]} + \Delta t\,\mathbf{M}^{-1}\mathbf{f}(\mathbf{x}^{[1]})

注意:f\mathbf{f} 在未知的新位置求值 → 变成方程而不是显式更新。

一阶近似(泰勒展开):

f[1]≈f[0]+∂f∂xΔx\mathbf{f}^{[1]} \approx \mathbf{f}^{[0]} + \frac{\partial \mathbf{f}}{\partial \mathbf{x}} \Delta \mathbf{x}

代入得到线性系统:

(1Δt2M−∂f∂x)Δx=1ΔtMv[0]+f[0]\left(\frac{1}{\Delta t^2}\mathbf{M} - \frac{\partial \mathbf{f}}{\partial \mathbf{x}}\right)\Delta \mathbf{x} = \frac{1}{\Delta t}\mathbf{M}\mathbf{v}^{[0]} + \mathbf{f}^{[0]}

代价:每步要解一个线性系统(矩阵可能很大)。 收益:无条件稳定——不管Δt\Delta t 多大都不炸。

这是实时物理引擎的核心权衡:显式快但不稳,隐式稳但慢。

更高阶的方法

方法精度代价
欧拉一阶1 次力计算
中点法 / RK2二阶2 次
RK4四阶4 次
Verlet二阶1 次(特殊结构)

Verlet 积分(分子动力学常用,游戏中也常见):

x[1]=2x[0]−x[−1]+Δt2M−1f[0]\mathbf{x}^{[1]} = 2\mathbf{x}^{[0]} - \mathbf{x}^{[-1]} + \Delta t^2 \mathbf{M}^{-1}\mathbf{f}^{[0]}

不显式存速度(从位置差推出)。辛积分,长期能量守恒好。

仿真的三阶段循环

每个时间步:

1. 计算力 f(x, v, t)
     重力、弹簧力、压力、碰撞力、用户施加的力...

2. 时间积分
     v ← v + dt · M⁻¹ f
     x ← x + dt · v

3. 处理约束 / 碰撞
     不可穿透、关节、摩擦、边界...

第 3 步常常是最难的——它决定了仿真是否"看起来对"。

约束处理

三类方法

方法思路优点缺点
惩罚力(Penalty)违反约束就加一个强力拉回简单刚度大 → 不稳定,需要小时间步
投影(Projection)先自由更新,再把位置投影回约束流形稳定需要求解,可能引入能量
拉格朗日乘子约束作为额外的力(f+JTλ\mathbf{f} + \mathbf{J}^T\lambda)精确要解 KKT 系统

Position Based Dynamics(PBD) 是现代实时物理的主流:直接用投影法,不用力。

PBD 的一步:
  1. 预测位置: p ← x + dt·v
  2. 迭代若干次: 对每个约束 C(p) = 0,直接修改 p 使其满足
  3. 更新速度: v ← (p - x) / dt
  4. x ← p

PBD 的优点:

  • 无条件稳定(因为不经过力,没有刚度问题)
  • 直观(约束就是"把点挪到哪里")
  • 可控(迭代次数 = 质量/性能旋钮)

PBD 的缺点:

  • 不是物理正确的(刚度依赖迭代次数和时间步)
  • 收敛慢(软约束需要很多次迭代)

XPBD(Extended PBD,2016)修正了刚度依赖时间步的问题,是现代引擎(UE 的 Chaos、NVIDIA 的 Flex)的基础。

本目录

章节内容
质点-弹簧系统最基础的弹性模型,布料/头发的起点
刚体位置 + 旋转、转动惯量、碰撞响应
柔体有限元、超弹性材料、共旋转线性化
流体SPH、网格法、Navier-Stokes
碰撞检测宽相/窄相、GJK、空间划分

实时仿真的工程约束

预算: 16 ms 里要完成物理 + 渲染 + 游戏逻辑
物理通常只能占 2~5 ms

固定时间步: 通常用 1/60 s,且要"追赶"(accumulator)
  while (accumulator >= dt) { step(dt); accumulator -= dt; }
  渲染时用插值避免抖动

子步(substepping): 快速运动的物体需要更小的步
  UE5 Chaos: 每帧 1~8 个子步

固定时间步是必须的:用可变的Δt\Delta t 会让仿真行为不确定(同样的输入得到不同结果),对游戏(需要确定性)和网络同步是灾难。

参考

  • GAMES103:基于物理的计算机动画入门(刘利刚)
  • 《Physically Based Modeling》(Pixar 课程笔记,经典入门)
  • Müller et al., Position Based Dynamics(2007)
  • Macklin et al., XPBD(2016)
  • 《Computer Animation: Algorithms and Techniques》(Parent)