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.
... and how it should look like (it is a non bvh-tree version)
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.
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:
For simplicity I put the content of
firstIntersection
method totraverseBvhNode
method.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.I also modified the for cycle to a while cycle, as with
while
I can modify the value ofi
in the block, whereas for does not permit this kind of action.Because there are some
continue
instructions as well in thetraverseBvhNode
method, I put thei++
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;
}