为什么边界框会以一种奇怪的方式出现在对象周围?

Why does the bounding box appear around the object in a strange way?

我正在为多边形模型创建一个基于 OpenGL 的光线追踪器。基本结构是将结果从片段着色器渲染到四边形。为了加速应用程序,使用了 BVH 树。因为 GLSL 中没有递归,所以我决定寻找其他方法来遍历边界框。 bvh 节点(包括框)和原始坐标被发送到片段着色器到着色器存储缓冲区中。

我正在使用中描述的基本思想:

上述解决方案使用links“用于跳过不需要评估的节点”。 有命中link:命中时跳转到哪个节点 有未命中link:未命中时跳转到哪个节点。

实际上,我没有使用 links 在框之间导航,因为我有一个完整的二叉树,这使得在不同深度之间导航更容易。但基本概念类似于上面的link。我按广度优先顺序存储节点。

不幸的是,当程序是 运行 时会出现奇怪的结果。我可以看到部分光线追踪的对象和边界框。边界框是灰色的,但是这个颜色应该是背景的颜色。

下图为当前状态。您应该会在灰色背景中看到一个圆锥体,但您会在其对象周围看到一个灰色边界框,而不是这个。

...以及它应该是什么样子(它是非 bvh-tree 版本)

这是我的片段着色器:

#version 460 core

layout(std140, binding=0) buffer primitives{
    vec3 primitiveCoordinates[];
};

struct FlatBvhNode   //I checked the data and it works fine.
{
                      // base aligment   aligned offset
    vec4 min;         // 16 byte                 0
    vec4 max;         // 16 byte                16
    int  order;       // 4 byte                 32
    int  isLeaf;      // 4 byte                 36
    int  createdEmpty;// 4 byte                 40  //it is because of the complete binary tree
    int  leftOrRight; // 4 byte                 44
    vec4 indices[10];// 32 byte                 48
};

layout(std430, binding=2) buffer TNodes
{
    FlatBvhNode nodes[];   // the nodes of the tree in breadth-first order
};

out vec4 FragColor;
in vec3 p;
uniform vec3 wEye;

struct Light{
    vec3 Le, La;
    vec3 direction;
    vec3 position;
};

uniform Light lights[];

struct Ray{
    vec3 orig, dir;
};

struct Hit{
    vec3 orig, dir, normal;
    float t;
};


Hit rayTriangleIntersect(Ray ray, vec3 v0, vec3 v1, vec3 v2){
    // This works well, so I don't include.
}

vec3 getCoordinatefromIndices(float index){
    return primitiveCoordinates[int(index)];
}


Hit firstIntersect(Ray ray, int i){
    Hit besthit;
    besthit.t=-1;
    for (int j=0;j<nodes[i].indices.length();j++){
        vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x);
        vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y);
        vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z);
        Hit hit=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);

        if (hit.t==-1){ continue; }

        if (hit.t>0 && (besthit.t>hit.t || besthit.t<0)){
            besthit=hit;
        }
    }
    return besthit;
}


bool rayIntersectWithBox(const vec4 boxMin, const vec4 boxMax, const Ray r) {
    vec3 invdir = 1.0 / r.dir.xyz;
    const vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
    const vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;

    const vec3 tmax = max(f, n);
    const vec3 tmin = min(f, n);

    const float t1 = min(tmax.x, min(tmax.y, tmax.z));
    const float t0 = max(max(tmin.x, max(tmin.y, tmin.z)), 0.0f);

    return t1 >= t0;
}



//This is where should be the problem.
//This is the method responsible for evaluating the bboxes.
//Instead of the links I can reach the childs with 2*i+1 or 2*i+2 and I can also get the parent 
//with an inverse (int(ceil(i-2)/2))

    Hit traverseBvhNode(Ray ray, FlatBvhNode node){
        Hit result;

    int next = 0;

    for (int i = 0; i < nodes.length(); i++) {
        if (i != next) {   continue;  }

        bool hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);

        if (nodes[i].createdEmpty==1){ hit=false;} 

        if (hit) {
            if (nodes[i].isLeaf==1 && nodes[i].createdEmpty!=1){ return firstIntersect(ray, i);}
            next = 2*i+1;
        }

        else if (!hit) {

            if (nodes[i].leftOrRight==0){ next = i+1; }
            else if (nodes[i].leftOrRight==1){ next = int(ceil(i-2)/2);
                if (next==5){
                    result.t=-1;
                    return result;
                }
            }
        }
    }

    return result;
}


Hit traverseBvhTree(Ray ray){
    Hit hit;
    if (rayIntersectWithBox(nodes[0].min, nodes[0].max, ray)){
        return traverseBvhNode(ray, nodes[0]);
    }
    return hit;
}

vec3 trace(Ray ray){
    vec3 color= vec3(0, 0, 0);
    vec3 ka= vec3(0.135, 0.2225, 0.1575);
    vec3 kd= vec3(0.54, 0.89, 0.63);

    Hit hit=traverseBvhTree(ray);

    if (hit.t==-1){ return lights[0].La; }

    color=lights[0].La*ka;

// The below part is under contruction, but functions well.
        Ray shadowRay;
        shadowRay.orig=hit.orig+hit.normal*0.001f;
        shadowRay.dir=lights[0].direction;
        float cosTheta = dot(hit.normal, lights[0].direction)/(length(hit.normal)*length(lights[0].direction));
        if (cosTheta > 0){
            color+=lights[0].Le*cosTheta*kd;
            float cosDelta=dot(hit.normal, normalize(-ray.dir + lights[0].direction));
            if (cosDelta>0){
                color=color+lights[0].Le*vec3(0.316228, 0.316228, 0.316228)*pow(0.1, cosDelta);
            }
        }
        return color;
    }

void main()
{
    Ray ray;
    ray.orig = wEye;
    ray.dir = normalize(p - wEye);
    FragColor = vec4(trace(ray), 1);

}

非常感谢任何帮助。


更新 1:

我使用了 Rabbid76 的建议并将 rayIntersectWithBox 方法的 return 语句从 return t1 >= t0; 修改为 return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;

正如您在快照中所见,没有可见的边界框,但对象仍然存在一些问题。


更新 2:

我更新了片段着色器的代码traverseBvhNode。我确信该算法现在遍历了所有节点,因为我检查了 i==nodes.length() 是否为真。

不幸的是,window 中的结果仍然与我上次更新时的结果相同。

这里是traverseBvhNode方法的修改:

  1. 为简单起见我把firstIntersection方法的内容放到了traverseBvhNode方法中。

  2. 此外,我将 return besthit 行移动到 traverseBvhNode方法,因为我之前要遍历所有节点 return与最佳路口相交。

  3. 我也修改了for循环为while循环,和while我可以修改block中i的值,而for不允许这种行动。

  4. 因为traverseBvhNode方法中也有一些continue指令,所以我把i++指令放在了方法的开头。

这是更新后的代码:

Hit traverseBvhNode(Ray ray, FlatBvhNode node){
    Hit besthit;
    besthit.t=-1;

    int db=0;
    Hit hitreal;

    int next = 0;
    int i=-1;
    while(i<=nodes.length()) {
        i++;

        if(i >nodes.length()){ break;}
        if (i != next) { continue; }

        bool hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);

        if (nodes[i].createdEmpty==1){ hit=false; }

        if (hit) {

            if (nodes[i].isLeaf==1 && nodes[i].createdEmpty!=1){
                db++;

                for (int j=0;j<nodes[i].indices.length();j++){
                    if (mod(nodes[i].indices[j].x, 1)==0 && mod(nodes[i].indices[j].y, 1)==0 && mod(nodes[i].indices[j].z, 1)==0){

                        vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x).xyz;
                        vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y).xyz;
                        vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z).xyz;

                        hitreal=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);

                        if (hitreal.t==-1){ continue; }

                        if (hitreal.t>0 && (besthit.t>hitreal.t || besthit.t<0)){
                            besthit=hitreal;
                        }
                    }
                    else{ continue;}
                }
            }
            else{ next = 2*i+1;}
        }

        else  {
            if (nodes[i].leftOrRight==0){  next = i+1; }

            else if (nodes[i].leftOrRight==1){

                int id=int(ceil(i-2)/2);
                FlatBvhNode parent=nodes[id];

                while(parent.leftOrRight==1){
                    parent=nodes[int(ceil(parent.order-2)/2)];
                }
                next = parent.order+1;
                i=next-1;
            }
        }
    }
    return besthit;
}

rayIntersectWithBox中的算法好像有误

光线与盒子相交,如果最小值小于最大值,分别针对所有 3 个维度 此外,您还必须考虑光线的方向。这意味着您必须根据方向向量 (sign(invdir)) 分量的 符号 来计算最小值和最大值。

我建议:

bool rayIntersectWithBox(const vec4 boxMin, const vec4 boxMax, const Ray r) {
    vec3 invdir = 1.0 / r.dir.xyz;
    const vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
    const vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;

    const vec3 tmax = f * sign(invdir);
    const vec3 tmin = n * sign(invdir);

    return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;
}

所以边界体积树的遍历由于几个故障而流血此外,我还更新了rayIntersectionWithBox方法(感谢飞向Rabbid76)

所以这里是bvh遍历方法和rayIntersectWithBox。我还将 firstintersection 方法移到了遍历方法中。 我的解决方案类似于:,只是我没有在节点中使用预定义链接。我使用广度优先算法到达父节点或子节点。

bool rayIntersectWithBox(vec4 boxMin, vec4 boxMax, Ray r) {
    vec3 invdir = 1.0 / r.dir.xyz;
    vec3 f = (boxMax.xyz - r.orig.xyz) * invdir;
    vec3 n = (boxMin.xyz - r.orig.xyz) * invdir;

    vec3 tmax = f * sign(invdir);
    vec3 tmin = n * sign(invdir);

    return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;
}

Hit traverseBvhNode(Ray ray, FlatBvhNode node){
    Hit besthit;
    besthit.t=-1;
    bool hit;
    Hit hitreal;
    int i=0;

    while (i<=nodes.length()) {

        if (nodes[i].isLeaf==1){
            for (int j=0;j<nodes[i].indices.length();j++){
                if (mod(nodes[i].indices[j].x, 1)==0 && mod(nodes[i].indices[j].y, 1)==0 && mod(nodes[i].indices[j].z, 1)==0){
                    vec3 TrianglePointA=getCoordinatefromIndices(nodes[i].indices[j].x).xyz;
                    vec3 TrianglePointB=getCoordinatefromIndices(nodes[i].indices[j].y).xyz;
                    vec3 TrianglePointC=getCoordinatefromIndices(nodes[i].indices[j].z).xyz;

                    hitreal=rayTriangleIntersect(ray, TrianglePointA, TrianglePointB, TrianglePointC);

                    if (hitreal.t==-1){ continue; }

                    if (hitreal.t>0 && (besthit.t>hitreal.t || besthit.t<0)){
                        besthit=hitreal;
                    }
                }
            }
            if (nodes[i].leftOrRight==0){
                i=i+1;
                continue;
            }

            else if (nodes[i].leftOrRight==1){

                int id=int(ceil(i-2)/2);
                FlatBvhNode parent=nodes[id];

                while (parent.leftOrRight==1){
                    parent=nodes[int(ceil(parent.order-2)/2)];
                    if (parent.order==0){
                        return besthit;
                    }
                }
                i = parent.order+1;
                continue;
            }
        }


        hit = rayIntersectWithBox(nodes[i].min, nodes[i].max, ray);

        if (hit) {

            if (nodes[i].isLeaf==0){
                i=2*i+1;
                continue;
            }
        }

        else {

            if (nodes[i].order==0){
                break;
            }


            if (nodes[i].leftOrRight==0) {
                i=i+1;
                continue;
            }

           node-nál vagyunk.
            else if (nodes[i].leftOrRight==1){
                FlatBvhNode parent=nodes[int(ceil(i-2)/2)];

                while (parent.leftOrRight==1){
                    parent=nodes[int(ceil(parent.order-2)/2)];
                    if (parent.order==0){
                        if (parent.order==0){
                            return besthit;
                        }
                    }
                }
                i = parent.order+1;
                continue;
            }
        }
    }
    return besthit;
}