☰
PhysX 碰撞检测:为什么要拆成 Broad Phase 和 Narrow Phase?
2026/10/8 1:19:24 网站建设 项目流程

核心原因是:

精确碰撞检测比较昂贵,不能对场景中所有形状两两执行。PhysX 先用低成本方法筛选候选对,再对少量候选对进行精确计算。

两阶段分别回答不同的问题:

Broad Phase(宽相) 哪些形状可能发生碰撞? ↓ Narrow Phase(窄相) 这些形状是否形成接触? 接触在哪里、法线是什么、分离距离是多少?

严格来说,小规模场景可以直接两两检测,并不是数学上“必须”分成两阶段。但对大型实时物理场景,这种分工非常重要。


一、为什么不能直接做精确检测?

假设场景中有 (N) 个碰撞形状,全部两两配对的数量为:

[
P=\frac{N(N-1)}{2}
]

当 (N=10,000) 时:

[
P=49,995,000
]

即接近5000 万个形状对。

如果每个物理步都对这些配对进行凸体、三角网格等精确检测,成本通常不可接受。

而且绝大多数配对根本没有意义:

城市东侧的汽车 vs 城市西侧的垃圾桶

它们相距几公里,不应该进入精确几何计算。

因此需要:

大量形状 ↓ 低成本空间筛选 少量候选对 ↓ 精确几何检测 有效接触

二、Broad Phase:用便宜的边界替代复杂形状

1. 核心职责

Broad Phase 不直接研究形状表面的细节,而是使用包围体进行空间筛选。

PhysX 的模拟宽相主要围绕AABB,轴对齐包围盒工作:

实际碰撞形状 ↓ 包住它的世界空间 AABB ↓ 检查哪些包围盒可能重叠

AABB 的三个方向始终与坐标轴平行,可以用最小点和最大点表示:

structAABB{Vec3 min;Vec3 max;};

两个 AABB 是否重叠,只需要检查三个坐标轴上的区间:

booloverlap(constAABB&a,constAABB&b){returna.min.x<=b.max.x&&a.max.x>=b.min.x&&a.min.y<=b.max.y&&a.max.y>=b.min.y&&a.min.z<=b.max.z&&a.max.z>=b.min.z;}

这比复杂凸体或网格之间的精确接触生成便宜得多。

2. 只是换成 AABB,还不够

一个容易忽视的点是:

如果仍然把所有 AABB 两两比较,复杂度还是 (O(N^2))。

因此 Broad Phase 不仅使用简单边界,还使用空间组织算法,避免检查大量明显不相关的配对。

PhysX 不同版本和配置提供的宽相算法包括:

  • SAP:Sweep and Prune;
  • MBP:Multi Box Pruning;
  • ABP:Automatic Box Pruning;
  • GPU Broad Phase。

例如,SAP 利用坐标轴上的区间排序,空间分区类方法则利用区域划分减少候选配对。

Broad Phase 的价值是“便宜的表示 + 高效的候选搜索”,不是只有包围盒。


三、为什么 Broad Phase 不能直接判定碰撞?

因为包围盒只是实际形状的保守近似。

以两个球为例:

球 A:圆心 (0, 0, 0),半径 1 球 B:圆心 (1.5, 1.5, 0),半径 1

它们的 AABB 在三个轴上都有重叠。

但球心距离为:

[
d=\sqrt{1.52+1.52}\approx2.121
]

而半径之和为:

[
r_A+r_B=2
]

所以两个球实际没有相交。

这就是:

AABB 重叠 ≠ 实际几何相交

对于旋转后的细长物体,包围盒中的空白区域可能更大,误选也更加明显。

宽相的设计原则

对于需要检测的交互,Broad Phase 应尽量做到:

  • 允许误选:包围盒重叠,但几何不接触;
  • 避免漏选:应该进一步检测的对象对没有被提交。

误选可以由窄相排除;漏选则可能导致碰撞丢失。

这里的“避免漏选”以正确的边界、更新和配置为前提。只使用离散位置的边界,并不能自动避免高速物体穿墙,还需要相应的 CCD 机制。


四、Narrow Phase:把候选对变成接触数据

1. 核心职责

窄相接收通过宽相和过滤的候选对,执行具体几何算法:

球体 vs 球体 球体 vs 盒体 胶囊 vs 凸体 凸体 vs 凸体 凸体 vs 三角网格

对于普通接触,其任务不只是返回一个true或false,还需要生成求解器所需的接触信息。

典型输出包括:

数据用途
接触位置确定接触作用位置及力臂
接触法线确定主要约束方向
分离距离描述距离或穿透情况
接触流形组织一组相关接触
几何附加信息支持材质、面索引等后续处理

触发器交互则通常只需要重叠状态,不进入普通接触冲量求解。

2. 不同形状使用不同算法

例如:

  • 球体与球体:可以使用简单解析计算;
  • 凸体之间:可能使用 GJK、EPA、SAT 及其他专用路径;
  • 凸体与网格:通常先筛选相关三角形,再进行局部检测;
  • 持续接触:可以利用持久接触流形减少重复计算。

所以,不能把 PhysX 的整个窄相概括为“统一执行一次 GJK”。

3. 接触不一定要求已经穿透

PhysX 可以根据接触生成参数,在几何尚未实际穿透时生成接触。

因此更准确的表述是:

Narrow Phase 判断是否需要生成接触,并计算接触数据,而不只是判断几何体是否相交。

例如,两个形状可能已经进入接触生成距离,但还没有达到静止时的目标间距。


五、两者如何配合?

以一个箱子落到地面为例:

第一步:更新边界

箱子的位置与旋转 ↓ 更新箱子的世界空间 AABB

第二步:宽相筛选

箱子的边界与地面的边界形成候选重叠 ↓ 产生或维持候选对

此时只能确认“值得继续检查”。

第三步:碰撞过滤

检查:

  • 是否允许这两类对象交互;
  • 是否为触发器;
  • 是否需要接触响应;
  • 是否请求通知等。

第四步:窄相生成接触

箱子几何 + 地面几何 ↓ 计算接触位置、法线和分离距离

第五步:交给约束求解器

接触数据 + 质量 + 惯性 + 速度 + 材质 ↓ 约束准备与求解 ↓ 计算支撑、摩擦或反弹响应

Broad Phase 和 Narrow Phase 都不负责最终决定“把箱子弹多远”。运动响应属于后续约束求解。


六、这种拆分究竟节省了什么?

可以用一个简化成本模型表示:

[
T
\approx
T_{\text{边界更新}}
+
T_{\text{宽相搜索}}
+
K \cdot C_{\text{窄相}}
]

其中:

  • (K):进入窄相的有效候选对数量;
  • (C_{\text{窄相}}):平均单对精确检测成本。

如果直接两两精确检测:

[
T_{\text{直接}}
\approx
\frac{N(N-1)}{2}\cdot C_{\text{窄相}}
]

两阶段设计的关键,是在典型场景里让:

[
K \ll \frac{N(N-1)}{2}
]

不过,宽相没有消灭最坏情况。

如果大量物体挤在同一个区域,所有边界都互相重叠,候选对数量仍可能接近 (O(N^2))。不能笼统地说 Broad Phase 把任何场景都变成了 (O(N\log N))。


七、PhysX 还会利用“上一物理步”的结果

实际引擎不会每步完全从零开始。

Broad Phase 的时间相干性

维护候选对变化:

新增重叠对 保持重叠的关系 已经分离的对

对于变化较小的场景,增量更新比全量重建更有效。

Narrow Phase 的时间相干性

复用接触缓存和持久接触流形:

上一物理步的接触 + 本步的相对运动 ↓ 验证、更新或补充接触

例如静止在桌面上的箱子,不需要每步把全部几何关系当作第一次见到来处理。


八、对应源码应该看哪里?

以常见 PhysX 4.x/5.x 源码布局为例:

职责主要阅读位置
AABB 管理、Broad Phasesource/lowlevelaabb/
几何测试、接触生成、流形source/geomutils/
底层碰撞任务与接触处理组织source/lowlevel/
交互生命周期与场景控制source/simulationcontroller/
接触约束准备与响应求解source/lowleveldynamics/

这些职责会跨模块协作,不是一个目录严格对应一个串行阶段。

另外,Scene Query 不等于模拟碰撞的 Broad Phase。射线、Sweep、Overlap 使用自己的查询组织与加速机制,虽然也会复用几何算法。


总结

对比项Broad PhaseNarrow Phase
核心问题谁可能碰到谁?是否需要接触,接触情况如何?
主要依据包围盒与空间组织具体几何与接触缓存
处理范围场景中的大量边界筛选后的有效候选对
输出候选对及其变化接触或重叠数据
成本特点强调低成本、大规模筛选单对通常更昂贵
是否计算最终碰撞响应否否

一句话概括:Broad Phase 负责不让昂贵的计算浪费在无关对象上,Narrow Phase 负责把“可能碰撞”变成可供求解器使用的精确接触信息。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询