简单的八叉树遍历:如何获得与AABB的交叉点

时间:2014-07-13 10:32:28

标签: java intersection raytracing bounding-box octree

这是我在stackoverflow上的第一个问题,所以我希望我把所有事情都做对了。对于java项目,我需要使用八叉树进行光线跟踪。我已经创建了一个简单的八叉树(没有邻居信息或其他东西),并将对象网格的三角形分类到八叉树的AABB中。现在我想通过树轻松遍历每条光线。 (它应该非常简单,因为完成这个项目的时间非常短)。基本算法如下:

  1. 从第一个节点开始
  2. 如果此节点被点击,请记住排序列表中交叉点的位置
  3. 如果此节点有子节点,则检查子框是否被命中并写入已排序列表中的每个交叉点
  4. 从具有最近交叉点的子框开始
  5. 如果这个盒子有孩子,也可以看到4)
  6. 如果某个节点没有任何子节点检查此中的每个三角形 反对射线的盒子
  7. 如果击中一个三角形,则获得三角形的颜色(带阴影和 一切)并在屏幕上绘制
  8. 不幸的是,我目前的实施似乎有一个"错误"在交叉点计算中(ray vs ABBB)。我检查AABB的任何一侧是否被击中并记住最近的ip(距射线原点的最小距离)。

    以下是我的BoundingBox类中此函数的代码:

    public HitResult intersects6(Ray ray) {
        double t;
        Vec3d ip = new Vec3d();
        HitResult finalHitResult = null;
    
        // front xy
        if (Math.abs(ray.direction.z) > Helper.EPSYLON) {
            t = (vmax.z - ray.origin.z) / ray.direction.z;
    
            ip.x = ray.origin.x + t * ray.direction.x;
            ip.y = ray.origin.y + t * ray.direction.y;
            ip.z = vmax.z;
    
    
            if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.y >= vmin.y) && (ip.y <= vmax.y)) {
                // here is an intersection
                double distance = Vec3d.distance(ray.origin, ip);
                finalHitResult = new HitResult(ip, distance);
            }
    
        }
        // back xy
        if (Math.abs(ray.direction.z) > Helper.EPSYLON) {
            t = (vmin.z + ray.origin.z) / -ray.direction.z;
    
            ip.x = ray.origin.x + t * ray.direction.x;
            ip.y = ray.origin.y + t * ray.direction.y;
            ip.z = vmin.z;
    
            if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.y >= vmin.y) && (ip.y <= vmax.y)) {
                double distance = Vec3d.distance(ray.origin, ip);
                if (finalHitResult!= null) {
                    if(distance < finalHitResult.distance)
                        finalHitResult.distance = distance;
                        finalHitResult.point = ip; 
                }
                else 
                    finalHitResult = new HitResult(ip, distance);
    
            }
    
        }
        // Side Right
        if (Math.abs(ray.direction.x) > Helper.EPSYLON) {
            t = (vmax.x - ray.origin.x) / ray.direction.x;
    
            ip.y = ray.origin.y + t * ray.direction.y;
            ip.z = ray.origin.z + t * ray.direction.z;
            ip.x = vmax.x;
    
            if ((ip.y >= vmin.y) && (ip.y <= vmax.y) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
                double distance = Vec3d.distance(ray.origin, ip);
                if (finalHitResult!= null) {
                    if(distance < finalHitResult.distance)
                        finalHitResult.distance = distance;
                        finalHitResult.point = ip; 
                }
                else 
                    finalHitResult = new HitResult(ip, distance);
            }
        }
        // Side Left
        if (Math.abs(ray.direction.x) > Helper.EPSYLON) {
            t = (vmin.x + ray.origin.x) / -ray.direction.x;
    
            ip.y = ray.origin.y + t * ray.direction.y;
            ip.z = ray.origin.z + t * ray.direction.z;
            ip.x = vmin.x;
    
            if ((ip.y >= vmin.y) && (ip.y <= vmax.y) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
                double distance = Vec3d.distance(ray.origin, ip);
                if (finalHitResult!= null) {
                    if(distance < finalHitResult.distance)
                        finalHitResult.distance = distance;
                        finalHitResult.point = ip; 
                }
                else 
                    finalHitResult = new HitResult(ip, distance);
            }
        }
        // Top
        if (Math.abs(ray.direction.y) > Helper.EPSYLON) {
            t = (vmax.y - ray.origin.y) / ray.direction.y;
    
            ip.x = ray.origin.x + t * ray.direction.x;
            ip.z = ray.origin.z + t * ray.direction.z;
            ip.y = vmax.y;
    
            if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
                double distance = Vec3d.distance(ray.origin, ip);
                if (finalHitResult!= null) {
                    if(distance < finalHitResult.distance)
                        finalHitResult.distance = distance;
                        finalHitResult.point = ip; 
                }
                else 
                    finalHitResult = new HitResult(ip, distance);
            }
        }
        // Bottom
        if (Math.abs(ray.direction.y) > Helper.EPSYLON) {
            t = (vmin.y + ray.origin.y) / -ray.direction.y;
    
            ip.x = ray.origin.x + t * ray.direction.x;
            ip.z = ray.origin.z + t * ray.direction.z;
            ip.y = vmin.y;
    
            if ((ip.x >= vmin.x) && (ip.x <= vmax.x) && (ip.z >= vmin.z) && (ip.z <= vmax.z)) {
                double distance = Vec3d.distance(ray.origin, ip);
                if (finalHitResult!= null) {
                    if(distance < finalHitResult.distance)
                        finalHitResult.distance = distance;
                        finalHitResult.point = ip; 
                }
                else 
                    finalHitResult = new HitResult(ip, distance);
            }
        }
    
        return finalHitResult;
    

    我想这不是最好的方法。在我的第一个实现中,我只使用了t值并对它们进行了比较(找到我想要访问的框)。但问题是一样的。找不到一些交叉点。

    我还在这里查看了交集方法: https://code.google.com/p/3d-workspace/source/browse/trunk/MathLibrary/Bounding/BoundingBox.cpp?r=17 但我无法看到如何获得与此代码(甚至任何t值)的交叉点。此外,我测试了slab方法,如下所示: http://tavianator.com/2011/05/fast-branchless-raybounding-box-intersections/ 但这似乎也错过了一些交叉路口,我不知道为什么:

    public double[] intersects3(Ray ray) {
        double Tnear = -1e30;
        double Tfar = 1e30;
    
        // First, check slab in X.
        if (Math.abs(ray.direction.x) < 0.0) {
            // Ray is parallel to X, but starts outside. Fail.
            if (ray.origin.x < vmin.x || ray.origin.x > vmax.x) {
                return null;
            }
        } else {
            double Ta = ((vmin.x - ray.origin.x) / ray.direction.x), Tb = (vmax.x - ray.origin.x) / ray.direction.x;
            double T1 = Math.min(Ta, Tb);
            double T2 = Math.max(Ta, Tb);
            if (T1 > Tnear)
                Tnear = T1;
            if (T2 < Tfar)
                Tfar = T2;
            if (Tnear > Tfar)
                return null;
            if (Tfar < 0)
                return null;
        }
    
        // Then check slab in Y.
        if (Math.abs(ray.direction.y) < 0.0) {
            // Ray is parallel to X, but starts outside. Fail.
            if (ray.origin.y < vmin.y || ray.origin.y > vmax.y) {
                return null;
            }
        } else {
            double Ta = (vmin.y - ray.origin.y) / ray.direction.y, Tb = (vmax.y - ray.origin.y) / ray.direction.y;
            double T1 = Math.min(Ta, Tb);
            double T2 = Math.max(Ta, Tb);
            if (T1 > Tnear)
                Tnear = T1;
            if (T2 < Tfar)
                Tfar = T2;
            if (Tnear > Tfar)
                return null;
            if (Tfar < 0)
                return null;
        }
    
        // Then check slab in Z.
        if (Math.abs(ray.direction.z) < 0.0) {
            // Ray is parallel to X, but starts outside. Fail.
            if (ray.origin.z < vmin.z || ray.origin.z > vmax.z) {
                return null;
            }
        } else {
            double Ta = (vmin.z - ray.origin.z) / ray.direction.z, Tb = (vmax.z - ray.origin.z) / ray.direction.z;
            double T1 = Math.min(Ta, Tb);
            double T2 = Math.max(Ta, Tb);
            if (T1 > Tnear)
                Tnear = T1;
            if (T2 < Tfar)
                Tfar = T2;
            if (Tnear > Tfar)
                return null;
            if (Tfar < 0)
                return null;
        }
    
        // If we have survived this far, the test passed.
        return new double[] { Tnear, Tfar };
    }
    

    也许我对光线追踪太愚蠢了。

    但我的实际问题是:

    是否可以使用t值来比较哪个框具有最近的交叉点?如果是的话,我怎样才能得到这个t值?或者我该怎么做才能使第一个代码剪断工作? (到目前为止,我对任何工作解决方案都很满意,即使这个解决方案非常慢)

    提前致谢。

1 个答案:

答案 0 :(得分:-1)

也许这可能会有所帮助: http://chiranjivi.tripod.com/octrav.html

我试图实现四叉树的想法: https://github.com/alexroat/quadtree-traversal