Simulation
自动关联目录:Simulation
物理仿真回答的是:给定当前状态,下一时刻的状态是什么。
统一的数学框架
几乎所有物理仿真都在解同一类方程——常微分方程(ODE):
| 符号 | 含义 |
|---|---|
| 位置(所有粒子的) | |
| 速度 | |
| 质量矩阵 | |
| 合力(重力、弹力、压力、阻尼…) |
不同的仿真对象,差别只在 和 的形式:
| 对象 | 的来源 | 章节 |
|---|---|---|
| 质点弹簧 | 弹簧力 + 阻尼 | 质点-弹簧系统 |
| 刚体 | 外力 + 约束(角动量、转动惯量) | 刚体 |
| 柔体(有限元) | 弹性内力(应力-应变) | 柔体 |
| 流体 | 压力 + 粘性 + 重力(Navier-Stokes) | 流体 |
时间积分:仿真的核心
连续方程无法解析求解(除非极简单),所以离散化成一步步推进。
这是显式欧拉(Forward Euler)。
三种基本积分器
| 方法 | 更新公式 | 稳定性 | 精度 |
|---|---|---|---|
| 显式欧拉 | 差(会爆炸) | 一阶 | |
| 隐式欧拉 | 好(无条件稳定) | 一阶 | |
| 半隐式(Symplectic)欧拉 | 好(能量有界) | 一阶 |
半隐式欧拉是实时仿真的默认选择——只是把"先更新位置"改成"先更新速度",稳定性天差地别。
为什么显式欧拉会爆炸:
弹簧: f = -k(x - L)
显式欧拉: v += dt * (-k/m)(x - L)
每次迭代把能量放大一点
→ 刚度 k 大或 dt 大时,指数发散稳定性条件(显式方法):
刚度越大( 大),允许的时间步越小。这就是"硬弹簧难仿真"的原因。
隐式方法为什么稳定
注意: 在未知的新位置求值 → 变成方程而不是显式更新。
一阶近似(泰勒展开):
代入得到线性系统:
代价:每步要解一个线性系统(矩阵可能很大)。 收益:无条件稳定——不管 多大都不炸。
这是实时物理引擎的核心权衡:显式快但不稳,隐式稳但慢。
更高阶的方法
| 方法 | 精度 | 代价 |
|---|---|---|
| 欧拉 | 一阶 | 1 次力计算 |
| 中点法 / RK2 | 二阶 | 2 次 |
| RK4 | 四阶 | 4 次 |
| Verlet | 二阶 | 1 次(特殊结构) |
Verlet 积分(分子动力学常用,游戏中也常见):
不显式存速度(从位置差推出)。辛积分,长期能量守恒好。
仿真的三阶段循环
每个时间步:
1. 计算力 f(x, v, t)
重力、弹簧力、压力、碰撞力、用户施加的力...
2. 时间积分
v ← v + dt · M⁻¹ f
x ← x + dt · v
3. 处理约束 / 碰撞
不可穿透、关节、摩擦、边界...第 3 步常常是最难的——它决定了仿真是否"看起来对"。
约束处理
三类方法
| 方法 | 思路 | 优点 | 缺点 |
|---|---|---|---|
| 惩罚力(Penalty) | 违反约束就加一个强力拉回 | 简单 | 刚度大 → 不稳定,需要小时间步 |
| 投影(Projection) | 先自由更新,再把位置投影回约束流形 | 稳定 | 需要求解,可能引入能量 |
| 拉格朗日乘子 | 约束作为额外的力() | 精确 | 要解 KKT 系统 |
Position Based Dynamics(PBD) 是现代实时物理的主流:直接用投影法,不用力。
PBD 的一步:
1. 预测位置: p ← x + dt·v
2. 迭代若干次: 对每个约束 C(p) = 0,直接修改 p 使其满足
3. 更新速度: v ← (p - x) / dt
4. x ← pPBD 的优点:
- 无条件稳定(因为不经过力,没有刚度问题)
- 直观(约束就是"把点挪到哪里")
- 可控(迭代次数 = 质量/性能旋钮)
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 个子步固定时间步是必须的:用可变的 会让仿真行为不确定(同样的输入得到不同结果),对游戏(需要确定性)和网络同步是灾难。
参考
- GAMES103:基于物理的计算机动画入门(刘利刚)
- 《Physically Based Modeling》(Pixar 课程笔记,经典入门)
- Müller et al., Position Based Dynamics(2007)
- Macklin et al., XPBD(2016)
- 《Computer Animation: Algorithms and Techniques》(Parent)
许可协议:CC BY
作者:Davids
本文链接:https://hustjjd.github.io/848745ab.html
更新于:2026年10月10日