首页 > 解决方案 > Why does the bounding box appear around the object in a strange way?

问题描述

I am creating an OpenGL based ray tracer for polygon models. The basic structure is about to render the results to a quad from the fragment shader. To accelerate the application, BVH-trees are used. Because there is no recursion in GLSL, I decided to find an other way to traverse the bounding boxes. The bvh nodes (including the boxes) and the primitive coordinates are sent to the fragment shader into a shader storage buffer.

I am using the basic idea described in: Threaded BVH-tree traversal in shaders

The above solution uses links "which are used to skip nodes which don't need to be evaluated". There is a hit link: which node to jump to in case of a hit and there is a miss link: which node to jump to in case of a miss.

Actually, I am not using links to navigate between the boxes, as I have a complete binary tree, which makes easier to navigate between the different depths. But the basic concept is similar to link above. I store the nodes in breadth-first order.

Unfortunately, when the program is running there is a weird result. I can see the object partly ray-traced and the bounding box as well. The bounding box has grey color, but this color should be the color of the background.

The below picture shows the current state. You should see a cone in a grey background, but instead of this you can see a grey bounding box around its object. enter image description here

... and how it should look like (it is a non bvh-tree version)

enter image description here

Here is my fragment shader:

#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);

}

Any help is well appreciated.


Update 1:

I used Rabbid76's advice and modified the rayIntersectWithBox method's return statement from return t1 >= t0; to return tmin.x < tmax.x && tmin.y < tmax.y && tmin.z < tmax.z;

As you can see in the snapshot, there is no visible bounding box, but the object still has some troubles.

enter image description here


Update 2:

I updated the traverseBvhNode of fragment shader's code. I am sure the algorithm traverses all the nodes now, as I checked if i==nodes.length() and it gives true.

Unfortunately, the result in the window is still the same as in my last update.

Here are the modifcations of traverseBvhNode method:

  1. For simplicity I put the content of firstIntersection method to traverseBvhNode method.

  2. Moreover, I move the return besthit line to the end of traverseBvhNode method, because I have to traverse all nodes before returning with the best intersection.

  3. I also modified the for cycle to a while cycle, as with while I can modify the value of i in the block, whereas for does not permit this kind of action.

  4. Because there are some continue instructions as well in the traverseBvhNode method, I put the i++ instruction at the beginning of the method.

Here is the updated code:

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;
}

标签: openglglslraytracingbounding-boxbounding-volume

解决方案


中的算法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;
}

推荐阅读