物理引擎集成的工程取舍 2026
在 PhysX、Chaos、Jolt、Bullet 并存的工程现实里,真正面对的不是选哪个,而是如何把宽窄相、约束求解、deterministic 仿真、Job System 集成做成可观测、可回放、可横向替换的统一架构。
约 28 分钟阅读8,151 字3 次阅读博主

在 PhysX、Chaos、Jolt、Bullet 并存的工程现实里,真正面对的不是选哪个,而是如何把宽窄相、约束求解、deterministic 仿真、Job System 集成做成可观测、可回放、可横向替换的统一架构。

一句话摘要:在 PhysX / Chaos / Jolt / Bullet 四种主流物理引擎并存的工程现实里,游戏引擎开发者真正面对的不是"选哪个",而是"如何把宽窄相、约束求解、deterministic 仿真、Job System 集成这四件事做成可观测、可回放、可横向替换的统一架构"。
游戏引擎里最容易被低估的子系统是什么?不是渲染管线——那是被各家引擎文档反复拆解、Profiler 截图占满 GDC talk 的"明星";也不是动画系统——骨骼蒙皮、State Machine、IK 都有专属优化指南;甚至不是 AI——行为树、Utility AI、GOAP 早就是公开课标配。真正最隐蔽的子系统是物理引擎。
这种隐蔽性体现在三个层面。第一,物理引擎在 GDC talk 里几乎没有深度专题:从 2018 到 2025 年,GDC Vault 上关于物理引擎的深度 talk 数量不到渲染的 1/10,大多数 talk 集中在 PhysX / Havok 的"How to use"层面,缺少"如何集成"与"如何取舍"的工程视角。第二,物理引擎的失败模式极其昂贵:一个不正确的约束求解参数可以让玩家在高负载场景掉帧 30%,但这种掉帧不会出现在任何 frame debugger 的红色警告里——它只是"游戏变得卡了"。第三,物理引擎的 determinism 问题是分布式仿真、回放、反作弊系统的地基:一个 floating point round-off 的微小差异可以让 60Hz 服务器与客户端的物理仿真在 30 秒后产生肉眼可见的错位,而这种错位的根因要回到 IEEE 754 的 denormal number 处理规则里去找。
这篇文章的动机,是想从工程集成而非算法推导的角度,把物理引擎在现代游戏引擎里的角色拆开来讲清楚。我们会从宽窄相算法、约束求解、deterministic 仿真、多线程集成、四引擎横评五个层面,讨论为什么 PhysX、Chaos、Jolt、Bullet 在 2026 年并存——它们不是简单的"新旧替代"关系,而是不同的工程优先级映射到不同的物理引擎实现。
要把"物理引擎集成"这件事讲清楚,先得把"物理引擎本身"做一次形式化拆解。在工程层面,任何一个物理引擎都包含四个基本组件,这四个组件的边界决定了引擎的能力上限。
第一元组:Broad Phase(粗筛)。给定场景里 N 个碰撞体,粗筛的目标是用 O(N log N) 或更低复杂度快速排除"显然不会碰撞"的物体对,只把可能碰撞的对交给下一步窄相。粗筛算法在 2026 年主要有四种实现:BVH(AABB tree、OBB tree、KD-tree)、Sweep and Prune、Morton code + spatial hashing、GPU broad phase(NVIDIA PhysX 5 的 GPU broad phase、Jolt 的 experimental GPU broad phase)。粗筛的工程取舍主要看场景规模:静态场景(地形、关卡几何)用 BVH 极优;动态物体密集(MOBA 战场、FPS 室内)用 Sweep and Prune 更稳;超大世界(开放世界 MMO、Survival)则需要 hybrid 方案(地形 BVH + 动态 Sweep)。
第二元组:Narrow Phase(精算)。给定粗筛过滤出来的碰撞体对,精算需要精确计算穿透深度、接触点、接触法线。精算的核心是 GJK(Gilbert-Johnson-Keerthi)+ EPA(Expanding Polytope Algorithm)两个算法:GJK 给出"两凸体最近距离",EPA 在 GJK 基础上扩展给出"穿透深度和方向"。这两个算法对凸体(球、盒、胶囊、凸多面体)完美适用,但对凹体(地形 mesh、不规则石块)需要凸分解作为预处理。凸分解的成本非常高(HACD、V-HACD 都要几秒到几十秒),这是为什么大多数引擎都把"凹体碰撞"做成"多个凸体集合"的工程妥协。
第三元组:Constraint Solver(约束求解器)。求解器把窄相输出的接触点变成"满足不穿透约束 + 摩擦约束 + 关节约束"的最终速度与位置更新。求解器在 2026 年的主流选择是 Sequential Impulse(Erin Catto 的 Box2D 路径)、PGS(Projected Gauss-Seidel)、TGS(Temporal Gauss-Seidel,PhysX 5 默认)、Guendelman-Bridson(ODE 路径)。求解器的工程取舍极其微妙:顺序脉冲实现简单但迭代收敛慢,PGS / TGS 实现复杂但可以在 10-20 次迭代内收敛到工程可接受的精度,Guendelman-Bridson 在大约束集(关节链、布娃娃)上仍有优势。
第四元组:Scene Query(场景查询)。Raycast、Sweep test、Overlap query 是物理引擎对外暴露的查询接口,是 AI 寻路、武器弹道、视野检测、技能命中检测的底层依赖。Scene Query 在工程上常常被低估:一个被 AI 每帧调 1000 次的 SphereOverlap 查询,自身的固定开销(查询调度 + SIMD 化 + cache 命中率)可以占到 AI 总耗时的 30%。Scene Query 的优化空间在于查询调度器(批处理、空间复用)和 SIMD 化(8 个查询一次处理)。
把这四元组的形式化拎出来,后面的章节就有共同的词汇。每个引擎在四元组上的取舍不同,这正是 PhysX、Chaos、Jolt、Bullet 并存的原因。
宽相算法的工程选择,本质上是"场景规模 vs. 动态频率 vs. 内存预算"三维空间的取舍。在 2026 年的工程实践里,没有一种宽相算法能赢者通吃,每种算法都在某个工程象限里占优。
BVH(Bounding Volume Hierarchy)。BVH 的核心思想是把场景静态部分用树形结构组织(根节点包整个场景,叶子节点包单个碰撞体),查询时通过剪枝快速跳过不相关的子树。BVH 在静态地形、关卡几何上极优:一次构建(关卡加载时)、N 次查询(N 是帧率×查询数)、查询复杂度 O(log N)。但 BVH 在动态物体上成本极高——每帧重建 BVH 是不现实的,必须用 incremental update(只在物体 AABB 变化时更新对应子树)。Unreal Engine 5 Chaos 的静态场景 BVH 用高度优化的 incremental update,可以在 10ms 内处理 100k 静态碰撞体的动态更新。但 Chaos 在动态物体宽相上反而退回到 Sweep and Prune。
Sweep and Prune(SAP)。SAP 把所有碰撞体沿某个轴(通常是 X 轴)排序,然后扫描排序后的列表,找到 AABB 重叠的对。SAP 的优势是对动态物体友好:插入 / 删除 / AABB 变化的成本是 O(N) 或 O(N log N),比 BVH 的 incremental update 更便宜。SAP 的劣势是对静态 + 动态混合场景不友好:静态物体不动但要参与每帧扫描,浪费预算。这也是为什么 Chaos 把 SAP 用于动态、同时 BVH 用于静态。SAP 的另一个工程坑是轴选择:沿 X 轴扫描在所有物体大致朝同一方向运动时效率高(排好序几乎不变),但如果场景物体运动方向无规律(混乱战场),SAP 的排序更新会成为热点。
Spatial Hashing(空间哈希)。空间哈希把世界划分成固定大小的格子(典型 5-10m 一格),用 hash map 存每个格子里的碰撞体。空间哈希的优势是实现极简 + 缓存友好(同一格的碰撞体在内存里连续),劣势是格子大小需要精心调优:太大则单格内候选过多(退化为 O(N²)),太小则格子数过多(浪费 hash map 开销)。空间哈希在 indie 引擎里非常流行(Bullet、一些自研引擎),但在 AAA 工程里被 BVH + SAP 取代。
GPU Broad Phase。PhysX 5(2022 年发布)和 Jolt(2024 年 experimental)都开始支持 GPU 宽相。GPU 宽相的核心思想是把所有碰撞体的 AABB 上传到 GPU,用 compute shader 做并行扫描。GPU 宽相的工程价值在于当 N 极大(>10k)时可以做到 CPU 宽相 5-10 倍加速,但工程门槛极高:需要异步上传(AABB 每帧变化)、需要 query 结果回读(异步)、需要 fallback 到 CPU 宽相(集成阶段不可避免)。Jolt 的实验性 GPU 宽相在 2024 年实测中,对纯 dynamic 物体场景(>10k dynamic)可以做到 4× 加速,但在静态 + 动态混合场景反而更慢(数据搬运开销>计算收益)。
宽相的工程取舍是:静态 BVH + 动态 SAP + GPU fallback 是 2026 年的事实标准,几乎所有 AAA 引擎都用这个 hybrid 模式。
窄相算法在工程层面比宽相更稳定——GJK + EPA 几乎是所有现代物理引擎的事实标准,没有"五种窄相算法并立"的现象。窄相的工程取舍主要在精度 vs. 速度和凸 vs. 凹两个维度。精度上,GJK 的 early-out 可以在物体明显不接触时(<5% penetration tolerance)立即返回,这对远距离碰撞体对极有用。凸 vs. 凹上,凸分解是关键工程妥协:HACD(基于表面凸分解)输出的凸包数量少但单个凸包大,V-HACD(基于体积凸分解)输出的凸包数量多但单个凸包小,选择取决于窄相 GJK 的次数预算——凸包越多,GJK 调用次数越多,反之亦然。
约束求解器是物理引擎的"大脑",决定了一个引擎的"手感"。在工程层面,求解器的核心取舍不在"算法新颖度",而在迭代次数、稳定性、determinism 三个维度的工程权衡。
Sequential Impulse(顺序脉冲)。Erin Catto 在 Box2D 2006 年首发的求解范式,核心思想是把约束分解成一个个独立的"冲量应用",按顺序迭代求解。Sequential Impulse 的优势是实现简单(几百行代码)+ deterministic 友好(无矩阵分解,顺序固定)。劣势是收敛慢:对刚性约束(锁链、布娃娃)需要 30-50 次迭代才能稳定,这种迭代次数在 60Hz 物理步下要 2-5ms 的预算。Box2D 至今仍是 Sequential Impulse 的代表,这也是 2D 物理的工程标准。
PGS(Projected Gauss-Seidel)。PGS 把约束求解看成"对线性不等式系统做迭代投影",数学上比 Sequential Impulse 更通用,可以处理摩擦约束、关节约束、传动约束的统一表达。PGS 的实现成本高于 Sequential Impulse,但收敛更快(对刚性约束 10-15 次迭代可稳定)。PhysX 4 / 5 的默认求解器都是 PGS,这也是 3D 物理的工程标准。
TGS(Temporal Gauss-Seidel)。TGS 是 PGS 的时间扩展版本,把约束求解扩展到"过去帧 + 当前帧"的联合优化,从而获得更好的稳定性。TGS 在长穿透、缠绕约束(关节锁链被甩起来)上比 PGS 更稳。PhysX 5 默认用 TGS,这是 2022 年后 3D 物理求解器的最优解。
Guendelman-Bridson。一种基于 LCP(Linear Complementarity Problem)的求解范式,对大规模约束集(关节链、布娃娃、绳索)有独特的稳定性优势。Bullet 在 2026 年仍保留这个求解器作为高级选项,主要用于电影级仿真(毛发、布料)。
数值积分的工程取舍相对简单:主流是 Semi-implicit Euler(半隐式欧拉),比 explicit Euler 稳定,比 Verlet/RK4 便宜。Semi-implicit Euler 的误差累积在 60Hz 步长下可以接受(每秒漂移 <1%),但 determinism 要求更高时(锁步仿真、回放兼容)需要切换到 fixed-point 整数化——见下一节。
求解器的工程取舍是:TGS 是 2026 年 3D 物理的最优解,PhysX 5 和 Chaos 都默认 TGS;Bullet 保留 Sequential Impulse / PGS / Guendelman-Bridson 三选一作为可选;Jolt 用自定义的"Stable Islands + PGS"组合求解,在并发稳定性上有独特的优化(见 §6)。
Determinism 是物理引擎在 2026 年最被低估、但影响最深的工程属性。一个非 deterministic 的物理引擎,让分布式仿真、回放系统、反作弊服务变成不可解的工程问题。
Determinism 的工程含义可以用一句话概括:给定相同的输入序列(初始位置、速度、约束、力),在所有平台(Windows / Linux / macOS / Android / iOS)、所有 CPU 架构(x86 / ARM)、所有 SIMD 指令集(SSE / AVX / NEON)上,产生完全相同的输出序列。听上去简单,实际是个无底洞。
第一个无底洞是 floating point determinism。IEEE 754 浮点数在 x86 与 ARM 上的舍入模式差异是出了名的:x86 的 SIMD 寄存器在某些操作上默认使用"最近偶数舍入",而 ARM NEON 在某些情况下用"向零舍入"。这种差异在单次操作上看不到(差异在 ULP 量级),但在 60Hz 物理仿真下累积 60×30=1800 次/秒,30 秒后可以看到厘米级别的位置偏差。这正是 deterministic lockstep 游戏的杀手——MOBA 的"客户端预测 + 服务器权威"模式依赖物理仿真的 byte-exact determinism,一个 ULP 级别的偏差会让客户端和服务器在 30 秒后完全不同步。
第二个无底洞是 SIMD 路径差异。同一个物理引擎在不同平台用不同 SIMD 实现(AVX2 / AVX512 / NEON / SVE),即使底层算法相同,SIMD 路径的 reduction 顺序差异也会导致 ULP 级别偏差。Jolt 在这一点上做得最严格:Jolt 用 scalar-only 路径作为 determinism 基准,SIMD 路径作为加速选项但不保证 byte-exact,工程上给用户一个 "Deterministic Mode" 开关,关闭所有 SIMD 优化换取跨平台一致性。
第三个无底洞是 solver iteration count 与 early-out 行为。迭代次数不同直接导致结果不同——这就是为什么确定性要求所有平台的 solver 必须严格迭代相同次数,不允许任何 early-out。
工程上应对 determinism 的方案有三种:(1) fixed-point 整数化(Starcraft / Age of Empires 的做法,完全避免 floating point);(2) Deterministic Mode + scalar-only(Jolt 的做法,牺牲性能换一致性);(3) Acceptance Window(最现实的方案,允许 1-2 像素的位置偏差作为同步窗口)。确定 Lockstep / Rollback 网络的代码必须走 (1) 或 (2),不允许走 (3)。
Determinism 的工程代价是巨大的:Deterministic Mode 通常让物理引擎性能下降 30-50%。这就是为什么大多数商业游戏不追求完全 determinism,而是用 Acceptance Window 兜底。只有真正需要 lockstep 网络(MOBA、FPS、格斗)的游戏才愿意付出这个性能代价。
物理引擎的多线程化是 2026 年最活跃的工程领域。早期物理引擎(Bullet 3、PhysX 3)是单线程 + SIMD 的设计,所有碰撞检测、求解都在一个 worker thread 跑完。多线程化的核心挑战是约束求解的顺序依赖——约束求解本质上是一个迭代过程,iteration N 依赖 iteration N-1 的结果,无法直接并行化。
工程上的解决方案有三条路径。
路径一:Phase-based parallelism。把物理仿真拆成"宽相 → 窄相 → 求解"三个 phase,每个 phase 内部并行,phase 之间串行。宽相天然并行(每个 collision pair 独立),窄相天然并行(每个 pair 独立),但求解是串行的(iteration N 依赖 iteration N-1)。这种方案的并行度有限,通常只能做到 2-3 倍加速,适合中规模场景(几千个动态物体)。
路径二:Island-based parallelism。把场景拆成"island"(连通分量),每个 island 内部串行求解,island 之间并行。Island 的拆分通过宽相的 connected components 算法得到,工程上非常优雅。这是 PhysX 5 和 Chaos 的方案——把场景拆成 N 个 island,每个 island 跑在一个 worker thread,worker thread 数受限于核心数(典型 4-8 个),如果 island 数 > 线程数则有线程空闲。
路径三:Warm-starting + SIMD batch。Jolt 走的是这条路径——不是拆 island,而是把约束 solver 的 warm-starting 阶段 SIMD 化(8 个同构约束一起求解),iteration 阶段保持串行但每次处理 8 倍数据。Jolt 的实测性能在 8 核机器上与 Chaos 持平甚至略快,因为 SIMD batch 的 cache 效率高于 island-based 的线程切换。
多线程物理与 Job System 的集成是另一个工程难点。Unity 的 Job System + Burst 编译器与 Unity Physics 的集成是 2024 年后的事实标准:DOTS 模式下物理仿真完全跑在 Burst 编译的 IJobParallelFor 上,主线程零阻塞。Unreal 的 Task Graph + Chaos 集成走的是更传统的"Engine Tick + Worker Thread"模式,主线程跑 Game Thread,Worker Thread 跑 Physics Thread,通过 ENQUEUE_RENDER_COMMAND 类宏同步。
Jolt 在 Job System 集成上做得最对——Jolt 不依赖任何特定引擎的 Job System,而是接受一个 JobSystem 接口,让用户注入自己的 worker pool。Godot 4 用这个接口把 Jolt 集成到 Godot 的 Worker Thread Pool,Flecs 用这个接口把 Jolt 集成到 Flecs 的 Job System,Unreal 用这个接口把 Jolt 集成到 Unreal 的 Task Graph。Jolt 的可嵌入性是它在 2024 年后快速崛起的关键工程优势。
把 PhysX、Chaos、Jolt、Bullet 放在 2026 年的工程桌面上做一次诚实横评,这四个引擎不是"一个替代一个"的简单关系,而是在不同工程象限占优。
PhysX 5(NVIDIA)。PhysX 的工程优势是GPU 集成——作为 NVIDIA 维护的引擎,PhysX 5 的 GPU broad phase、GPU 粒子、GPU 布料与 CUDA 生态深度绑定。如果你的游戏是 PC + 主机 + 高端移动平台(无 GPU broad phase 需求),PhysX 是合理选择。PhysX 的工程劣势是授权复杂(Unity 集成免费,Unreal 集成免费,但第三方集成需要 NVIDIA 许可)+ determinism 不友好(TGS 在不同 SIMD 路径下不 byte-exact)。
Chaos(Unreal Engine)。Chaos 的工程优势是与 Unreal 深度集成——UE5 默认物理引擎就是 Chaos,所有 Unreal 工具链(UPLOG、Chaos Visual Debugger、Chaos Insights)都深度集成。Chaos 在静态场景 BVH、动态 SAP、island-based 多线程上做了极致的工程优化,在大规模开放世界场景(10k+ dynamic)上有最强的工程文档和最佳实践。Chaos 的劣势是与 Unreal 强绑定——不在 UE 之外用,也没有其他引擎深度集成 Chaos。
Jolt(Jorrit Rouwé)。Jolt 是 2024 年后崛起最快的开源物理引擎,工程优势是可嵌入性(Job System 接口、Deterministic Mode、cache-friendly 数据布局)+ 移动端友好(Mali / Adreno 实测性能优于 PhysX)+ 现代代码风格(C++17、SIMD-first、可读性强)。Jolt 在 Godot 4、Unreal(可选)、一些自研引擎都有集成。Jolt 的核心劣势是生态——没有 GPU 物理、没有 cloth(布料)、没有 vehicle(载具),这些都需要第三方或自己实现。
Bullet(Open Source)。Bullet 仍然是 indie / research / film VFX 领域的标准物理引擎,工程优势是算法最完整(PGS / Sequential Impulse / Guendelman-Bridson / Featherstone 关节全有)+ richest documentation(Bullet Physics 手册、GDC talks、CMU 课程教材)。Bullet 的劣势是架构老(C++03 风格、单线程 + SIMD 设计、多线程化困难)+ 移动端性能差(cache miss 比 Jolt 高 2-3 倍)。
横评结论:AAA Unreal 项目用 Chaos;Unity DOTS 项目用 Unity Physics(底层是 PhysX)或 Jolt;自研引擎、Godot、移动端 indie 优先选 Jolt;研究、电影 VFX、indie 跨平台选 Bullet。PhysX 在 2026 年的定位有点尴尬——它在 Unity 集成生态里被 Unity Physics 包装,在 GPU 物理优势上又因为生态门槛高而不被中小项目采用,PhysX 5 在 2026 年的市场份额实际在缓慢下降。
把上面六节内容收敛成可执行的工程结论,如果你是引擎开发者或技术美术,在物理引擎集成上应该做什么?
第一,先用 Profiler 抓宽窄相的实际占比,而不是凭直觉优化求解器。大多数物理性能问题都在宽相(场景太大、AABB 频繁变化)和窄相(凸分解过细、convex pair 过多),求解器的迭代次数反而是次要因素。先 RenderDoc / Tracy 抓帧,定位瓶颈,再对症下药。
第二,集成阶段必须暴露三组可观测性钩子:broad phase pair count、narrow phase convex pair count、solver iteration count。三组数字必须能在 DevTools / Console 里实时查看,这是物理调优的"X 光"。Chaos 的 Chaos Visual Debugger、PhysX 的 PVD(PhysX Visual Debugger)、Jolt 的 Jolt Debug Renderer 都有这个能力,集成时务必开启。
第三,Determinism 必须是显式开关,而不是默认行为。Determinism 的性能代价巨大(30-50% 性能损失),不应该作为默认选项。游戏侧的 Acceptance Window 阈值通常是 1-2 像素,这个阈值在 client-side prediction + server reconciliation 模型里足够。只有真正的 Lockstep 网络(确定性回滚)才需要 Deterministic Mode。
第四,多线程物理的 island 拆分数应该与硬件线程数对齐。Islands > 2× 硬件线程数没有意义,只会增加线程调度开销。Unreal 提供了 p.Chaos.Solver.IslandCount CVAR 调整,Chaos 默认 8 islands(对应 8 核 CPU)。
第五,凸分解是离线资产管线的活,不是运行时活。V-HACD 输出应该作为资产 metadata 持久化(烘焙到 asset 文件),运行时直接加载而不是每次动态生成。V-HACD 参数(凸包数量上限、最大凸包体积)应该美术可调,不同物体类型(地形 mesh、关卡 mesh、动态 debris mesh)用不同参数。
第六,多物理引擎支持不值得追求,除非有明确业务需求。同时支持 PhysX + Jolt + Chaos 的"引擎框架"在工程上是灾难级别的复杂度来源——三个引擎的数据布局、序列化格式、Debug 工具都不一样,没有 2-3 个全职工程师的投入,根本撑不起来。
最后给一份调试清单,对应到前面八节内容。
(1) broad phase pair count > 10× narrow phase pair count?意味着场景物体 AABB 频繁变化或者场景规模太大,考虑切到 BVH(static)+ SAP(dynamic)hybrid。
(2) narrow phase convex pair count > 50% of dynamic objects?凸分解过细,提高 V-HACD 最大凸包体积参数或者接受更多 convex pair 的成本。
(3) solver iteration count > 20 且仍在穿透?提高 iteration budget 到 30-50,或者考虑切到 TGS(PhysX 5)。
(4) 多线程物理 worker 利用率 < 50%?island 拆分数不够,增加 island 数或者用 Jolt 的 SIMD batch 路径。
(5) Deterministic Mode 性能损失 > 50%?考虑换 Jolt(它的 scalar-only 路径比 Chaos 的 Deterministic Mode 快 20%),或者放宽 Acceptance Window 到 3 像素。
(6) 物理帧时间在 60Hz 下 > 5ms?考虑切到 fixed timestep(30Hz 物理 + 60Hz 渲染的解耦),这是 2026 年移动端 indie 的事实标准。
物理引擎的工程化在 2026 年还远未收敛——Jolt 的崛起、Chaos 的成熟、PhysX 的缓慢边缘化,都是工程现实在重塑这个子系统的明证。真正决定物理引擎项目成败的不是算法选择,而是工程集成质量——这是这篇文章反复强调的核心论点。
Conversation
0 条