本节摘要:刚体实时引擎的默认积分是半隐式欧拉:先用当前力更新速度,再用新速度更新位置。布料和绳索常用位置 Verlet 或 XPBD,把约束直接写在位置上。隐式欧拉能吃大步长,但每步要解线性系统,且和冲量求解器叠在一起时容易发黏。按物体类型选加法,不要全局一只积分器打天下。
阅读完本节,你应当能够:
半隐式(也称辛欧拉、半显式)是:
v = v + a(x) * dt x = x + v * dt # 用已经更新的 v
相对纯显式,位置用了新速度。对简谐振子,它接近辛方法,能量在真值附近振荡而不是单调爬。圆轨道走成多边形,半径漂移远小于显式。计算量相同。刚体重力、风力、简单弹簧,用它就对。
角速度同样:先 w += Iinv * tau * dt,再 q += 0.5 * omega(q,w) * dt。有人先积分位置再算碰撞,冲量改速度后位置已经用旧速度走过,接触会少修一帧。标准顺序是:力积分速度 → 碰撞与约束改速度 → 用约束后的速度写位置。也就是把“x += v*dt”放到求解器之后。这仍叫半隐式家族,只是中间插入了冲量。
| 积分器 | 存储 | 能量 | 加约束 | 典型用途 |
|---|---|---|---|---|
| 半隐式欧拉 | x,v | 微振荡 | 冲量改 v 很自然 | 刚体、角色胶囊 |
| 位置 Verlet | x, x_prev | 近守恒 | 直接改 x,速度是派生量 | 布料、绳、头发 |
| 速度 Verlet | x,v | 近守恒 | 力要可重入 | 分子动力学、粒子 |
| 隐式欧拉 | x,v 加矩阵 | 过阻尼 | 每步解系统 | 软体、头发大步长 |
| RK4 | 多份中间力 | 准 | 约束难插 | 光滑力场、非实时 |
我做刚体堆箱子时用半隐式加顺序冲量;做旗帜用 Verlet/XPBD。两者在同一世界共存时,布料粒子不要当刚体积分,刚体不要当 Verlet 点,用接触把两套状态桥接。硬桥接会抖,第 5 章再谈。
Verlet 写法:
x_new = x + (x - x_prev) + a * dt * dt x_prev = x x = x_new
速度不显式存储,v = (x - x_prev) / dt。约束把点拉到满足长度后,速度自动变。这就是位置基动力学的老家。缺点:变 dt 时 (x-x_prev) 仍是旧步长的位移,必须按 dt 比例缩放,否则加速卡顿会把布料射出去。固定步再次成为前提。
隐式欧拉用未来加速度:v_new = v + a(x_new) * dt。弹簧力依赖未来位置,于是变成线性或非线性方程。软体网格几百点,每步解稀疏系统,才能在 1/30 秒步长下不炸。税是:实现复杂、每步迭代、过阻尼(运动发黏、高频被吃掉)。电影布料常用;游戏旗帜未必值得。
隐式不等于无条件稳定到可以乱取 dt。接触、碰撞、非光滑摩擦仍会限制步长。把 dt 开到 1/10 秒,角色会穿透台阶。隐式解决的是“软弹簧刚度高”的刚性 ODE,不是隧道效应。
XPBD 把约束刚度从迭代次数里拆出来,用柔度参数,迭代不够时变软而不是变硬到爆炸。游戏布料这几年多走这条,而不是隐式 FEM。选型时问:我要材料准确性,还是要 60Hz 好看?前者隐式 FEM,后者 XPBD。
💡 关键直觉:半隐式是“速度先行”,Verlet 是“位置先行”。冲量约束喜欢速度先行;长度约束喜欢位置先行。选积分器等于选你的约束主要写在哪一层。
子步:一次逻辑步里把 dt 切成 k 份,积分-碰撞-求解循环 k 次。对堆叠稳定性极有效,成本近似线性。很多引擎对外 60Hz,对内 2 到 4 子步。比把求解器迭代从 10 加到 40 更管用,因为子步会重新生成接触。只加迭代、不子步,接触法向还是这一帧开头那份,高速旋转时会过时。
隐式积分自带数值阻尼,再加鲍姆加特位置修正,物体像泡在糖浆里。表现:箱子放下后要半秒才“坐实”,关节电机推不动。对策:刚体走半隐式;软体走隐式或 XPBD,但位置修正系数分开调;不要全局一个 beta。
冲量求解后不要再跑一遍完整隐式系统去“光滑”,除非你清楚在解什么。常见错误是:半隐式预测 → PGS → 又用罚弹簧隐式拉一次。两套接触模型互殴,能量和穿透指标都会撒谎。
⚠️ 常见坑:Verlet 物体和刚体碰撞时,用刚体那套恢复系数直接改 Verlet 速度,再写回 x_prev,符号一错布料爆炸。应把接触写成 XPBD 的不等式约束,或把碰撞点当刚体接触只推刚体。
调试清单:
RK4 什么时候用?几乎不用在带接触的刚体世界。用于:载具悬挂的光滑力、自定义轨道摄像机、非碰撞粒子力场。一旦有非光滑碰撞,高阶方法的中间点没有良好定义的接触力,精度优势消失。
把第 2.2 节的弹簧实验换成半隐式和 Verlet 各跑一次。半隐式能量振荡;Verlet 更平。再把 dt 加倍,看谁先坏。你会得到自己的选型表,而不是抄一句“用 Verlet 更稳”。稳的定义依赖你的约束写在速度还是位置。
重力在 Verlet 里是 a*dt*dt 加到位置,数量级容易写错成 a*dt。单位测试:自由质点 1 秒下落距离应接近 0.5gt^2。半隐式因速度先行,自由落体位置会略大于解析值,游戏里可忽略。若差出一个 dt 因子,屏幕上物体第一帧就消失,那是乘法写错,不是稳定性。
多种积分器共存时,时间戳必须同一固定 dt。不要刚体 60Hz、布料 30Hz 还共用一个接触池,除非你明确做了多速率耦合。多速率是高级话题:慢系统用大步,快系统子步,接触在细步上生成。第一版引擎用全球一种 dt 加子步,够了。
刚体半隐式、布料 Verlet 同屏时,接触点一侧有 v 一侧只有派生速度。要把布料顶点速度在碰撞前算出来,冲量改完再写回 x_prev,使 x_prev = x - v*dt。写反则布料下一帧朝错误方向飞。先做“刚体球撞静态布”再做“布盖动态箱”,单向比双向容易验。
隐式软体与刚体 PGS 同屏,时间步必须同一固定 dt 或明确多速率。多速率时细步上冻结刚体位置,只把冲量累积到刚体,粗步再一次施加。第一版不做多速率,布料用子步、刚体也跟着子步,贵但正确。
RK4 中间点没有接触力的良好定义。有人把接触只加在整步上、中间点当无碰撞,高速时等于关闭接触。不要混。光滑力场(风力、向心力演示)可以用 RK4,接触物体不要用。
检查表:关掉碰撞弹簧能量有界;自由落体 1 秒位移接近 4.9 米;Verlet 变 dt 必须缩放 x-x_prev。三条不过,积分器没毕业。
让位置用约束后的速度走,接触才来得及在这一步起作用。先写位置再求解,等于这一帧已经穿过去了,求解只改速度,画面上仍穿。顺序是半隐式家族的工程形态。
必须。x-x_prev 带旧 dt 的量纲。固定步则无此问题。这是又一个固定步的理由。
子步会刷新接触,高速旋转时更值。只加迭代,法向还是步初那份。先子步 2,再看迭代。手机上子步也费电,用分析器。
同一 dt,布料子步时刚体跟着。接触先单向。双向冲量还回放在软体稳之后。多速率耦合是进阶,先别做。
这一帧已经穿了。位置用约束后速度写。
布料射出。固定步可避免。
糖浆。刚体不要隐式,软体分开调 beta。
能量指标撒谎。选一套。
下一帧飞。派生速度要自洽。
第一版够。多速率以后再说。
中间点接触力无良好定义。接触物体不用 RK4。
第一帧消失。自由质点 1 秒 4.9 米验收。
对账数字比观感先行。静止支撑冲量是否接近质量乘重力乘 dt,自由落体一秒是否接近四点九米,两球对心是否交换速度。「03-半隐式Verlet与隐式积分」相关实现先过对账,再谈好看。 本条对应「03-半隐式Verlet与隐式积分」第 1 号备忘,和相邻条目不要合并成一句空话,分开验收。
「03-半隐式Verlet与隐式积分」出抖动时按清单:时间步是否固定、流形点是否闪、法向是否跳、恢复系数是否在静止时仍生效、bias 是否过大、迭代是否其实在清零热启动。不要先把摩擦系数调到十。 本条对应「03-半隐式Verlet与隐式积分」第 2 号备忘,和相邻条目不要合并成一句空话,分开验收。
空间结构必须在世界系。把「03-半隐式Verlet与隐式积分」绑到摄像机或屏幕格子,逻辑和确定性都会在转视角时崩。渲染剔除不能代替宽相,看不见的物体仍能被撞到。 本条对应「03-半隐式Verlet与隐式积分」第 3 号备忘,和相邻条目不要合并成一句空话,分开验收。
「03-半隐式Verlet与隐式积分」的预分配策略:按关卡上限留容量,超出打日志并降级,不要在 step 中途默默扩容。扩容是卡顿尖峰,也是回放时分配顺序不同导致的潜在分叉源。 本条对应「03-半隐式Verlet与隐式积分」第 4 号备忘,和相邻条目不要合并成一句空话,分开验收。
把「03-半隐式Verlet与隐式积分」相关的失败做成最小复现:关掉渲染特效、关掉音频、只留固定 dt 的 step 和一份输入日志。能在十秒内重放出来的 bug 才叫被抓住。不能重放就先补录制,再谈修。很多人在这一步省时间,后面用几天陪着偶发抖动。 本条对应「03-半隐式Verlet与隐式积分」第 5 号备忘,和相邻条目不要合并成一句空话,分开验收。
下一章进入碰撞。积分给出可能非法的预测位置,宽相和窄相负责把“谁和谁可能碰上”变成接触数据。