GJK算法要求两多边形必须均为凸的,否则support函数失效、迭代发散或结果不可靠;需先校验凸性,凹形须预处理为凸片;support函数应基于点积最大而非距离最远;simplex更新依赖原点相对位置判断,需设迭代上限并采用相对容差检测原点包含性。

只用GJK判断相交,必须确保两个多边形都是凸的
不是“能用就行”,而是硬性前提:GJK算法本身不处理凹多边形。如果传入一个凹多边形,support函数返回的点可能不在边界上,单纯形迭代会发散或提前终止,结果不可靠。
常见错误现象包括:明明相交却返回 false、反复迭代后卡在某个方向不动、simplex 退化成线段甚至点却没及时退出。
- 实际开发中,先用
is_convex()检查顶点顺序和叉积符号一致性,别依赖美术资源标注 - 若需支持凹形,必须预处理:用耳切法或单调链分解为多个凸多边形,再两两调用 GJK
- 注意顶点顺序(顺时针/逆时针)会影响
support函数实现,但不影响 GJK 主逻辑;只要统一即可
关键在于 support 函数怎么写,不是“找最远点”那么简单
support 函数是 GJK 的入口,它决定每次迭代往哪个方向“探”。写错这里,整个算法就偏了。
对凸多边形,support(A, d) 应返回 A 中在方向 d 上投影最大的顶点。不是欧氏距离最远,是点积 dot(v, d) 最大。
立即学习“C++免费学习笔记(深入)”;
- 错误写法:
max_element(vertices.begin(), vertices.end(), [&](auto& a, auto& b) { return norm(a) —— 这是在找离原点最远的点,完全偏离方向 - 正确写法:
auto max_proj = dot(vertices[0], d); size_t best_i = 0; for (size_t i = 1; i max_proj) { max_proj = proj; best_i = i; } } - 如果多边形带旋转/缩放变换,
support必须作用于世界坐标下的顶点,不能直接用局部顶点加 transform —— 容易漏掉反向变换
迭代中 simplex 管理比想象中容易出错
GJK 的核心是维护一个 simplex(二维下最多 3 个点),每次用新 support 点替换旧点集的一部分。很多人卡在“怎么删点”这一步。
典型错误是手动枚举三角形三个边的朝向、投影、重心坐标——太重且易错。实际只需关注:当前 simplex 是否包含原点;如果不包含,原点落在哪一侧,就去掉对面的点。
- 二维中,
simplex可能是 1 点(初始)、2 点(线段)、3 点(三角形)。用 vector存,长度即状态 - 判断原点是否在线段
a-b的“正侧”,用叉积符号:cross(b-a, -a);若为负,说明原点在另一侧,应保留b去掉a - 三角形情况,用三个子空间(对应三条边的法向)做半平面测试,任一失败就删掉对面顶点;不要试图解重心坐标,开销大且不稳定
- 务必设迭代上限(比如 20 次),避免无限循环——凸多边形理论上收敛很快,但浮点误差可能导致抖动
别跳过原点是否在 simplex 内部的数值鲁棒性检查
判断原点是否在三角形内部,不能只靠符号判断。浮点计算下,cross 结果可能极小但非零,导致误判“不包含”,进而错误退出。
真实项目里,这个检查要带容差,而且容差不能固定写死。
- 用相对容差:
abs(cross(...)) ,避免大坐标下失效 - 当 simplex 退化(三点共线或两点重合),立刻返回 false 或降维处理;否则
cross为 0,后续方向更新会除零或 NaN - 方向更新公式
d = closest_point_to_origin(simplex)必须返回非零向量;如果simplex是单点且该点就是原点,说明已相交,直接返回 true
最易被忽略的是:GJK 判断“相交”等价于“原点在 Minkowski 差集中”,而这个集合的几何中心未必在原点附近。所以初始方向选 A.centroid() - B.centroid() 比随便选 (1,0) 更稳。


















