GJK算法生成相反单形点致叉乘为零的物理引擎碰撞检测问题
GJK碰撞检测零向量方向问题修复方案
问题根因
- 初始方向选取逻辑存在边界缺陷:当前初始方向基于质心差生成垂直向量,当两个立方体仅沿x轴偏移时,初始方向落到y轴方向,刚好取到闵可夫斯基差的两个反向顶点。
- 线段单形的方向计算未做零向量校验:当
ab与ao共线时,ab.cross(ao).cross(ab)会返回零向量,后续点积校验逻辑直接失效。
修复方案
1. 新增线段单形零向量兜底逻辑
修改lineSimplex函数,检测到生成的新方向为零向量时,自动替换为垂直于ab的非零向量:
def lineSimplex(args): a, b = args[0] ab = b - a ao = -a if ab.dot(ao) > 0: new_dir = ab.cross(ao).cross(ab) if new_dir == Vector3.zero(): # 共线时取任意垂直于ab的非零向量 if abs(ab.x) < abs(ab.y) and abs(ab.x) < abs(ab.z): new_dir = Vector3(1, 0, 0).cross(ab) elif abs(ab.y) < abs(ab.z): new_dir = Vector3(0, 1, 0).cross(ab) else: new_dir = Vector3(0, 0, 1).cross(ab) args[1] = new_dir else: args[0] = [a] args[1] = ao
2. 优化初始方向选取逻辑
优先使用两个形状的质心差作为初始方向,大幅降低边界场景出现概率:
def gjk(a, b): ab = a.pos - b.pos # 优先用质心差作为初始方向,仅质心重合时再生成垂直方向 if ab == Vector3.zero(): c = Vector3(ab.z, ab.z, -ab.x - ab.y) if c == Vector3.zero(): c = Vector3(-ab.y - ab.z, ab.x, ab.x) initial_dir = ab.cross(c) else: initial_dir = ab support = supportPoint(a, b, initial_dir) points = [support] direction = -support # 后续循环逻辑保持不变 while True: support = supportPoint(a, b, direction) if support.dot(direction) <= 0: return None points.insert(0, support) args = [points, direction] if nextSimplex(args): return args[0] points, direction = args
3. 可选全局校验兜底
可以在每次更新direction之后、查询新支持点之前,统一判断方向是否为零向量,若为零则替换为随机非零单位向量,彻底覆盖所有边界场景。
修改完成后测试x轴偏移为-1、0、1的场景,不会再出现零向量导致的校验失效问题,碰撞检测可以正常返回结果。
内容的提问来源于stack exchange,提问作者Ray
相关产品推荐
相关产品推荐

