Search code examples
openglglslshadowraytracing

Why there is no shadows behind the mesh object?


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.

The problem is, that the method, which tests, if the shadow ray intersects something returns true where it should not return true, meaning that, there is no shadows at all. Only two side of the cube are black, they are not facing the light source.

Here is a screenshot, where you can see a cube on a plane. And there is a light source as well. enter image description here

Here is the useful parts from the fragment shader: the trace and the shadowIntersect method:

bool shadowIntersect(Ray ray1){ // for testing if the shadowray intersects something
    for (int i;i<nodes.length();i++){
        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;
                Hit hit=rayTriangleIntersect(ray1, TrianglePointA, TrianglePointB, TrianglePointC);
                if (hit.t>0){
                    return true;
                }
            }
        }
    }
    return false;
}
vec3 trace(Ray ray){
    const float epsilon = 0.0001f;
    vec3 outRadiance = vec3(0, 0, 0);
    vec3 gold_ka = vec3(0.24725f, 0.1995f, 0.0745f);
    vec3 gold_kd = vec3 (40.75164f, 0.60648f, 0.22648f);
    vec3 gold_ks = vec3 (0.628281f, 0.555802f, 0.366065f);
    float goldshininess= 0.4f;

    // Traversing the bounding volume hierachies tree
    Hit hit=traverseBvhTree(ray);

    if (hit.t<0){ return lights[0].La;}

    outRadiance+= gold_ka * lights[0].La;

    Ray shadowRay;
    shadowRay.orig = normalize(hit.orig) + normalize(hit.normal) * epsilon;
    shadowRay.dir  = normalize(lights[0].direction);

    float cosTheta = dot(normalize(hit.normal), normalize(lights[0].direction));

    // if the shadow ray intersects nothing, we add the emission coefficient of the light.
    if (cosTheta>0 && shadowIntersect(shadowRay)==false) {
        outRadiance +=lights[0].Le * gold_kd * cosTheta;

    }
    return outRadiance;
}

Any help is appreciated!


Update 1:

I updated the code according to the advice of codetiger, avoiding normalizing the shadowray's origin and with an other way to calculate the direction of shadowray.

Ray shadowRay;
shadowRay.dir = normalize(lights[0].position - hit.orig);
shadowRay.orig = hit.orig + shadowRay.dir * epsilon

The above changes giving me the cube with only ambient lighting.

enter image description here

I also tested the below rows as well:

 if(shadowIntersect(shadowRay)==false){
        return vec3(1,1,1);
    }
    return vec3(0,0,0);

and this time the whole object becomes black so, the problem should be with the shadowIntersect method as well.

Here is the full fragment shader anyway:

#version 460 core

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


struct FlatBvhNode
{
// 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
    int  leftOrRight;
    vec4 indices[100];// 32 byte     48
};

layout(std430, binding=1) buffer TNodes
{
    FlatBvhNode nodes[];
};

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){

    Hit hit;
    hit.t=-1;
    float t; float u; float v;
    vec3 v0v1 = v1 - v0;
    vec3 v0v2 = v2 - v0;
    vec3 pvec = cross(ray.dir, v0v2);
    float det = dot(v0v1, pvec);


    if (abs(det) <  0.0001){
        hit.t=-1;
        return hit;// Culling is off
    }
    float invDet = 1 / det;

    vec3 tvec = ray.orig - v0;
    u = dot(tvec, pvec) * invDet;
    if (u < 0 || u > 1){
        hit.t=-1;
        return hit;
    }

    vec3 qvec = cross(tvec, v0v1);
    v = dot(ray.dir, qvec) * invDet;
    if (v < 0 || u + v > 1) {
        hit.t=-1;
        return hit;
    }

    hit.t = dot(v0v2, qvec) * invDet;
    hit.normal=cross(v0v1, v0v2);
    return hit;
}

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


FlatBvhNode leftChild(FlatBvhNode node){
    return nodes[2*node.order+1];
}

FlatBvhNode rightChild(FlatBvhNode node){
    return nodes[2*node.order+2];
}

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(dot(ray.dir, besthit.normal)>0) besthit.normal = besthit.normal * (-1);
                }
            }

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

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


Hit traverseBvhTree(Ray ray){
    return traverseBvhNode(ray, nodes[0]);
}

bool shadowIntersect(Ray ray1){ // for testing if the shadowray intersects something
    for (int i;i<nodes.length();i++){
        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;
                if (rayTriangleIntersect(ray1, TrianglePointA, TrianglePointB, TrianglePointC).t>0){
                    return true;

                }
            }
        }
    }
    return false;
}

vec3 trace(Ray ray){
    const float epsilon = 0.0001f;
    vec3 outRadiance = vec3(0, 0, 0);
    vec3 gold_ka = vec3(0.44725f, 0.3995f, 0.2745f);
    vec3 gold_kd = vec3 (40.75164f, 0.60648f, 0.22648f);
    vec3 gold_ks = vec3 (0.628281f, 0.555802f, 0.366065f);
    float goldshininess= 0.4f;

    // Traversing the bounding volume hierachies tree
    Hit hit=traverseBvhTree(ray);

    if (hit.t<0){ return lights[0].La; }

    outRadiance+= gold_ka * lights[0].La;

    Ray shadowRay;

    /*shadowRay.orig = hit.orig + normalize(hit.normal) * epsilon;
    shadowRay.dir  = normalize(lights[0].direction);*/

    shadowRay.dir = normalize(lights[0].direction);
    shadowRay.orig = hit.orig + shadowRay.dir * epsilon;

    float cosTheta = dot(normalize(hit.normal), normalize(lights[0].direction));

    // if the shadow ray intersects nothing, we add the emission coefficient of the light.
    if (cosTheta>0 && shadowIntersect(shadowRay)==false) {
        outRadiance +=lights[0].Le * gold_kd * cosTheta;
        vec3 halfway = normalize(-normalize(ray.dir) + normalize(lights[0].direction));
        float cosDelta = dot(normalize(hit.normal), halfway);
        if (cosDelta > 0) outRadiance += lights[0].Le * gold_ks * pow(cosDelta, goldshininess);

    }
    return outRadiance;
}


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

    // FragColor = vec4(nodes[4].isLeaf, 1, 1, 1);
}

Solution

  • The problems was, I have not calculated the origin of the intersection in the ray-intersection algorithm.

    Adding this line solved the problem:

    hit.orig=ray.orig+normalize(ray.dir)*hit.t;