GJK算法要求输入必须为凸多边形,否则会发散或误判;需用叉积符号一致性验证凸性,凹形须预处理为凸片;support函数须按方向投影找极值点而非最远点,且需归一化方向、浮点容差及正确初始化。

必须先确认两个多边形都是凸的
GJK算法对输入形状有硬性要求:support函数只在凸集上保证返回边界极值点;传入凹多边形会导致迭代发散、原点误判或无限循环。常见错误现象包括:明明相交却返回 false,simplex 退化成线段后不再更新,或迭代 100 次仍卡在同一个方向。
实操建议:
- 用叉积符号一致性检查凸性:
cross(v[i+1] - v[i], v[i+2] - v[i+1])在所有顶点上同号(全 ≥ 0 或全 ≤ 0),且顶点数 ≥ 3 - 别信美术资源标注的“convex”标签——运行时校验比文档更可靠
- 若检测到凹形,必须预处理:用耳切法拆成凸片,再对每对凸片调用 GJK,不能跳过这步直接喂给
gjk_intersect() - 顶点顺序(顺/逆时针)不影响 GJK 主逻辑,但会影响
support函数中点积比较的稳定性,建议统一为逆时针
support 函数不能按距离找最远点
support 函数不是找离原点欧氏距离最远的顶点,而是找在给定方向 d 上投影最大的点——即 dot(v, normalize(d)) 最大者。写错这里,整个 GJK 就会往错误方向“生长”,单纯形永远包不住原点。
常见错误和修正:
立即学习“C++免费学习笔记(深入)”;
- 漏归一化
d:直接用未归一化的d点积,导致投影值随方向长度缩放,结果不可靠 → 必须先调用normalize(d)或手动除以sqrt(d.x*d.x + d.y*d.y) - 浮点比较没容差:用
if (dp > max_dp)容易因精度抖动跳过正确顶点 → 改为if (dp > max_dp + 1e-9) - 初始化错误:用
0.0初始化max_dp,遇到全负坐标时失效 → 改用std::numeric_limits<double>::lowest()</double> - 错误地用
norm(v)替代点积 → 这是在找离原点最远点,完全偏离 GJK 所需的支持方向
GJK 主循环里 simplex 更新容易逻辑错乱
二维 GJK 中 simplex 只可能是 1 点、2 点(线段)、3 点(三角形)。很多人试图手算重心坐标或枚举边朝向来删点,反而引入分支错误。实际只需根据原点相对于当前 simplex 的位置,决定保留哪些顶点。
关键判断逻辑:
- 单点
{a}:方向直接设为-a,下一次取support(A, d) - support(B, -d) - 线段
{a, b}:计算cross(b - a, -a)符号,若为负,说明原点在a对面 → 删a,保留b;否则删b - 三角形
{a, b, c}:对每条边(如b→c)构造外法向n = perp(c - b),若dot(n, a) ,说明 <code>a在该边外侧 → 删a,新 simplex 为{b, c};同理试b、c - 必须设迭代上限(如 20 次)并配合相对容差退出:
if (dot(new_point, new_point) ,避免零向量或数值坍缩
重叠 ≠ 相交,GJK 只回答“是否接触或穿透”
GJK 判定的是“两个凸多边形的闵可夫斯基差是否包含原点”,等价于“是否存在非空交集”——包括相交、嵌套、边贴边、点碰点。它不区分“部分重叠”和“完全包含”,也不返回交集区域或深度。如果你需要知道“是否严格相交(非包含)”,得额外做点在多边形内的检测。
容易被忽略的细节:
- 当一个小凸多边形完全在另一个内部时,GJK 仍返回
true—— 这是正确行为,不是 bug - 若需排除包含关系,必须在 GJK 返回
true后,用 winding number 或射线法单独验证:是否存在 A 的顶点在 B 外部,且 B 的顶点在 A 外部 - GJK 不处理退化情况:共线顶点、重复点、面积为零的多边形会导致
support返回异常或cross为零 → 前置过滤掉面积的多边形 - 没有 AABB 粗筛就直接跑 GJK,小物体高频检测时性能会断崖下跌 → 总是先调
aabb_intersect(a, b),失败则直接跳过


















