0

这是我在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 值?或者我该怎么做才能使第一个代码被剪断?(到目前为止,我对任何可行的解决方案都很满意,即使这个解决方案很慢)

提前致谢。

4

1 回答 1

-1

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

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

于 2015-03-18T22:01:34.757 回答