【问题标题】:Simple octree traversal: How to get the intersection points with AABB简单的八叉树遍历:如何获取与 AABB 的交点
【发布时间】:2014-09-03 11:54:16
【问题描述】:

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

  1. 从第一个节点开始
  2. 如果这个节点被命中,记住交点在排序列表中的位置
  3. 如果此节点有子节点,请检查子框是否被命中并将每个交点写入排序列表中
  4. 从最近交点的子框开始
  5. 如果这个盒子也有孩子,请参阅 4)
  6. 如果节点没有任何子节点,请检查此节点中的每个三角形 对着光线的盒子
  7. 如果击中三角形,则获取三角形的颜色(带阴影和 一切)并将其绘制在屏幕上

不幸的是,我当前的实现似乎在交集计算中有一个“错误”(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 值)的交点。此外,我测试了如下所述的平板方法: 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 值?或者我该怎么做才能使第一个代码被剪断? (到目前为止,我对任何可行的解决方案都很满意,即使这个解决方案很慢)

提前致谢。

【问题讨论】:

  • 无论if是真还是假,if(distance &lt; finalHitResult.distance) finalHitResult.distance = distance; finalHitResult.point = ip;中的finalHitResult.point = ip;语句都会被执行。您可能想要使用括号:if(distance &lt; finalHitResult.distance) { finalHitResult.distance = distance; finalHitResult.point = ip; }
  • 你为什么要检查if (Math.abs(ray.direction.x) &lt; 0.0)?那总是错误的。

标签: java intersection raytracing bounding-box octree


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-01
    • 2015-05-09
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多