分离轴定理(sat)判断两三角形相交需检测最多15条候选轴(3+3+9),投影到各轴取顶点点积的min/max区间,若某轴上区间不重叠则分离;实操宜用三维向量库并归一化去零向量。

用分离轴定理(SAT)判断两个三角形是否相交
直接对三角形做边-边、点-面、面-面穷举检测既慢又易漏,工业级做法是用分离轴定理:如果存在一个轴,使得两三角形在该轴上的投影不重叠,则它们不相交;否则相交。对两个凸多边形(三角形是特例),只需检查有限条候选分离轴。
对两个三角形 A 和 B,候选轴共 11 条:
- A 的 3 条面法向量(即三角形所在平面的法向量,实际只需 1 条,因共面)
- B 的 3 条面法向量(同理只需 1 条)
- A 每条边 × B 每条边 → 共 9 个叉积方向(去重后最多 9 条)
但三角形是二维嵌入三维的,更稳妥的做法是统一在 3D 下处理,取全部 3 + 3 + 9 = 15 条轴,再归一化并跳过零向量。
实操建议:
- 用 glm::vec3 或自定义三维向量类型,避免手写叉积/点积错误
- 投影时对每个三角形的 3 个顶点做点积,取 min 和 max 得区间
- 判断两区间重叠:若 projA_max ,则该轴分离,立即返回 <code>false
- 所有轴都未分离 → 返回 true
注意退化三角形和浮点误差
真实数据中常出现面积极小、三点几乎共线的三角形,导致法向量接近零、叉积精度崩坏。此时 SAT 的轴可能失效或产生 NaN。
必须做前置防护:
- 计算三角形面积(如用叉积模长的一半),若 area ,按退化处理(可返回 <code>false 或转为线段/点相交检测)
- 所有候选轴计算后,先调用 glm::normalize 或手动检查长度,若 length ,跳过该轴<br>
- 投影比较时用带 epsilon 的区间重叠判断:<code>projA_max + 1e-6f 才算分离<br>
- 不要依赖 <code>== 0 判断共面,改用 abs(dot(normal, edge))
二维三角形相交?直接降维用 Minkowski 差集 + 射线法
若两个三角形严格位于同一平面(如 Z=0 的 XY 平面),强行套 3D SAT 是浪费。更高效的是转为 2D 问题:
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
步骤:
- 提取所有顶点的 x、y 坐标,丢弃 z
- 用 boost::geometry 的 intersection 或手写射线法判断多边形是否相交(三角形是简单多边形)
- 或更轻量:检查是否存在一个三角形的顶点在另一个内部(用重心坐标或叉积符号法),再检查边是否相交(线段相交用跨立实验)
- 线段相交核心逻辑:direction(a,b,c) * direction(a,b,d) ,其中 <code>direction(p,q,r) 返回 (q-p) × (r-p) 的 z 分量
注意:direction 返回值为 0 表示三点共线,此时需额外处理端点重合或内含情况,不能直接跳过。
性能敏感场景下的预剪枝策略
在碰撞检测循环中每帧判断上百对三角形时,不做预剪枝会严重拖慢。必须加两层快速拒绝:
- 第一层:用包围球(sphere)或 AABB 快速粗筛。
对每个三角形,预计算其 min/max 坐标得 AABB,用 AABB_intersect(A, B) 判断;若不相交,直接返回 false
- 第二层:若共面且投影到主轴(X/Y/Z)后包围盒不重叠,也可早退
- 不要每次临时计算 AABB —— 应在三角形构建时缓存 aabb_min、aabb_max 成员变量
- 若使用 SIMD,可批量处理 4 对三角形的 AABB 检测,但 SAT 本身难以向量化,不建议强求
真正耗时的是 SAT 的 15 轴投影,而退化检查和浮点容错又不可避免。所以最现实的优化不是加速 SAT,而是让绝大多数对在第一层就被拦住。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










