You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

JavaFX中3D GJK碰撞检测假阳性问题求助

3D GJK碰撞检测假阳性问题排查与修复

我用JavaFX实现3D GJK碰撞检测,碰撞体实际碰撞或距离较远时功能正常,但特定朝向且近距离时会出现假阳性结果。推测问题出在单纯形(simplex)处理逻辑中,但无法定位具体问题,求帮忙。

Collider_3d类仅存储一组凸集点,已确认点集为凸集,需要的话可提供具体点数据。

原始代码

private static final double EPILSON = 0;

static private Point3D findSuppPoint(Collider_3d a, Collider_3d b, Point3D dir) {
    return a.findExtrema(dir).subtract(b.findExtrema(dir.multiply(-1)));
}

public static boolean Gilbert_Johnson_Keerthi_Collision(Collider_3d a, Collider_3d b) {
    Point3D dir = new Point3D(0,0,1);
    Point3D currPoint = findSuppPoint(a,b,dir);
    ArrayList<Point3D> simplex = new ArrayList<Point3D>();
    simplex.add(currPoint);
    
    dir = currPoint.multiply(-1); // 朝向原点的方向
    
    while(true) {
        currPoint = findSuppPoint(a,b,dir);
        if(currPoint.dotProduct(dir) < EPILSON) {
            return false;
        }
        simplex.add(currPoint);
        dir = calcSimplex(simplex, dir);
        if(dir == null){
            return true;
        }
    }
}

static private Point3D calcSimplex(ArrayList<Point3D> s, Point3D dir) {
    if(s.size() == 2) {
        return nextDir(s);
    }else if(s.size() == 3) {
        return triSimplex(s, dir);
    }else if(s.size() == 4){
        return tetraSimplex(s, dir);
    }
    return dir; // 理论上不会走到这里
}

static private Point3D tetraSimplex(ArrayList<Point3D> s, Point3D dir) {
    Point3D AB = s.get(2).subtract(s.get(3));
    Point3D AC = s.get(1).subtract(s.get(3));
    Point3D AD = s.get(0).subtract(s.get(3));
    Point3D AO = s.get(3).multiply(-1);
    
    Point3D ABCDir = AB.crossProduct(AC);
    Point3D ADBDir = AC.crossProduct(AD);
    Point3D ACDDir = AD.crossProduct(AB);
    
    if(ABCDir.dotProduct(AD) > 0) {
        ABCDir = ABCDir.multiply(-1);
    }else if(ADBDir.dotProduct(AC) > 0) {
        ADBDir = ADBDir.multiply(-1);
    }else if(ACDDir.dotProduct(AB) > 0) {
        ACDDir = ACDDir.multiply(-1);
    }
    
    if(ABCDir.dotProduct(AO) > 0) {
        s.remove(0);
        return ABCDir;
    }else if(ADBDir.dotProduct(AO) > 0) {
        s.remove(1);
        return ADBDir;
    }else if(ACDDir.dotProduct(AO) > 0) {
        s.remove(2);
        return ACDDir;
    }
    System.out.println("AHHHH");
    return null;
}

static private Point3D triSimplex(ArrayList<Point3D> s, Point3D dir) {
    Point3D AB = s.get(1).subtract(s.get(2));
    Point3D AC = s.get(0).subtract(s.get(2));
    Point3D AO = s.get(2).multiply(-1);
    Point3D ABDir = (AC.crossProduct(AB)).crossProduct(AB);
    Point3D ACDir = (AB.crossProduct(AC)).crossProduct(AC);
    if(ABDir.dotProduct(AO) > 0) {
        s.remove(0);
        return ABDir;
    }else if(ACDir.dotProduct(AO) > 0) {
        s.remove(1);
        return ACDir;
    }
    return dir;
}

static private Point3D nextDir(ArrayList<Point3D> s) {
    Point3D AB = s.get(0).subtract(s.get(1));
    Point3D AO = s.get(1).multiply(-1);
    return (AB.crossProduct(AO)).crossProduct(AB);
}

问题根源与修复方案

1. 浮点精度阈值设置错误

问题:EPILSON = 0,浮点运算存在精度误差,临界距离下的点积计算会因微小误差误判。
修复:设置极小正数阈值,避免精度问题导致的误判:

private static final double EPILSON = 1e-8;

2. 线段单纯形方向计算错误

问题:nextDir方法中方向向量未单位化,且向量推导逻辑未确保指向包含原点的半空间。
修复:

static private Point3D nextDir(ArrayList<Point3D> s) {
    Point3D A = s.get(0);
    Point3D B = s.get(1);
    Point3D AB = B.subtract(A);
    Point3D AO = A.multiply(-1);
    // 计算垂直于AB且指向原点的方向向量,并单位化
    Point3D perp = AB.crossProduct(AO).crossProduct(AB);
    return perp.normalize();
}

3. 三角形单纯形裁剪逻辑错误

问题:未正确判断原点是否在三角形平面的包含区域,且方向向量计算未确保指向原点。
修复:

static private Point3D triSimplex(ArrayList<Point3D> s, Point3D dir) {
    Point3D A = s.get(0);
    Point3D B = s.get(1);
    Point3D C = s.get(2);
    Point3D AB = B.subtract(A);
    Point3D AC = C.subtract(A);
    Point3D AO = A.multiply(-1);
    
    Point3D ABCNormal = AB.crossProduct(AC);
    // 确保法线朝向原点
    if (ABCNormal.dotProduct(AO) < 0) {
        ABCNormal = ABCNormal.multiply(-1);
    }
    
    // 检查原点是否在三角形平面外侧
    if (ABCNormal.dotProduct(AO) > EPILSON) {
        // 检查AB边区域
        Point3D ABPerp = AB.crossProduct(ABCNormal).crossProduct(AB);
        if (ABPerp.dotProduct(AO) > EPILSON) {
            s.remove(2);
            return ABPerp.normalize();
        }
        // 检查AC边区域
        Point3D ACPerp = AC.crossProduct(ABCNormal).crossProduct(AC);
        if (ACPerp.dotProduct(AO) > EPILSON) {
            s.remove(1);
            return ACPerp.normalize();
        }
        // 原点在三角形内部,判定碰撞
        return null;
    } else {
        // 原点在平面另一侧,裁剪为线段BC
        s.clear();
        s.add(B);
        s.add(C);
        return nextDir(s);
    }
}

4. 四面体单纯形面法线方向错误

问题:面法线方向修正逻辑错误,未确保法线指向原点所在的半空间,导致裁剪方向错误。
修复:

static private Point3D tetraSimplex(ArrayList<Point3D> s, Point3D dir) {
    Point3D A = s.get(0);
    Point3D B = s.get(1);
    Point3D C = s.get(2);
    Point3D D = s.get(3);
    
    Point3D AO = A.multiply(-1);
    Point3D BO = B.multiply(-1);
    
    // 计算四个面的法线,确保朝向原点
    Point3D ABCNormal = (B.subtract(A)).crossProduct(C.subtract(A));
    if (ABCNormal.dotProduct(AO) < 0) ABCNormal = ABCNormal.multiply(-1);
    
    Point3D ABDNormal = (B.subtract(A)).crossProduct(D.subtract(A));
    if (ABDNormal.dotProduct(AO) < 0) ABDNormal = ABDNormal.multiply(-1);
    
    Point3D ACDNormal = (C.subtract(A)).crossProduct(D.subtract(A));
    if (ACDNormal.dotProduct(AO) < 0) ACDNormal = ACDNormal.multiply(-1);
    
    Point3D BCDNormal = (C.subtract(B)).crossProduct(D.subtract(B));
    if (BCDNormal.dotProduct(BO) < 0) BCDNormal = BCDNormal.multiply(-1);
    
    // 检查原点是否在各面外侧,裁剪单纯形
    if (ABCNormal.dotProduct(AO) > EPILSON) {
        s.remove(3);
        return ABCNormal.normalize();
    }
    if (ABDNormal.dotProduct(AO) > EPILSON) {
        s.remove(2);
        return ABDNormal.normalize();
    }
    if (ACDNormal.dotProduct(AO) > EPILSON) {
        s.remove(1);
        return ACDNormal.normalize();
    }
    if (BCDNormal.dotProduct(BO) > EPILSON) {
        s.remove(0);
        return BCDNormal.normalize();
    }
    // 原点在四面体内部,判定碰撞
    return null;
}

核心修复总结

  • 必须设置合理的浮点精度阈值,避免临界情况误判
  • 所有方向向量计算后需单位化,确保后续点积计算的准确性
  • 单纯形裁剪时,必须确保面法线/垂直方向指向包含原点的半空间,否则会导致错误的搜索方向
  • 严格统一单纯形中点的顺序和索引引用,避免向量计算混乱

内容的提问来源于stack exchange,提问作者Glitchh

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 09:57:01