0

I have moved to my ray tracing journey. I have managed to render a mesh with ray tracing and calculate shadows. But when I tried to make reflections and to add bounces I got a totally black screen. I have cleaned the code ind added comments and I think I found the problem in the calculations but I cannot seem to find the reason of this happening.

#version 300 es\n
precision highp float;
precision highp int;
precision highp sampler2D; 
in vec2 vuv;
uniform float time;
uniform vec2 Res, mouse;
uniform sampler2D uMeshData;
uniform int vertsCount;
uniform mat4 uRot;
layout(location = 0) out lowp vec4 fragColor;

#define MAX_BOUNCES 4
#define rmat(a, b) mat3(1, 0, 0, 0, cos(b), -sin(b), 0, sin(b), cos(b)) * mat3(cos(a), 0, sin(a), 0, 1, 0, -sin(a), 0, cos(a))

// Materials
#define LAMB 0
#define METAL 1
#define DIEL 2


struct Ray {
  vec3 orig, dir;
}R_;

struct Material
{
    int type;
    vec3 albedo; 
};

struct Sphere{
    vec3 center;
    float radius;
};

mat4 rotate() {
    // original x= mouse.x, y= mouse.y
    float x = mouse.y; //y=mouse.y+sin(time*2.),z=0.;
    float y= mouse.x,z=0.;
    float a = sin(x), b = cos(x), c = sin(y), d = cos(y), e = sin(z), f = cos(z), ac = a * c, bc = b * c;

    return mat4(d * f,           d * e,           -c,     0.0, 
                ac * f - b * e,  ac * e + b * f,  a * d,  0.0,     
                bc * f + a * e,  bc * e - a * f,  b * d,  0.0, 
                0.0,             0.0,             0.0,    1.0);
}

mat4 frotate( float x, float y, float z ){
    float a = sin(x); float b = cos(x); 
    float c = sin(y); float d = cos(y); 
    float e = sin(z); float f = cos(z); 

    float ac = a*c;
    float bc = b*c;

    return mat4( d*f,      d*e,       -c, 0.0,
                 ac*f-b*e, ac*e+b*f, a*d, 0.0,
                 bc*f+a*e, bc*e-a*f, b*d, 0.0,
                 0.0,      0.0,      0.0, 1.0 );
}

mat4 translate( float x, float y, float z ){
    return mat4( 1.0, 0.0, 0.0, 0.0,
                 0.0, 1.0, 0.0, 0.0,
                 0.0, 0.0, 1.0, 0.0,
                 x, y, z, 1.0 );
}


vec3 getHitPoint(Ray ray, float t) {
    return ray.orig + t * ray.dir;   
}

//intersection test with spheres
bool hitSphere(vec3 orig,vec3 dir,vec3 center,float r,out vec3 intersect){
    vec3 oc = orig - center;
    float b = dot(oc,dir);
    float c = dot(oc,oc) - r * r;
    if(c>0.0 && b > 0.0) return false;
    float discriminant = b*b -c;
    if(discriminant < 0.0) return false;
    float t= -b-sqrt(discriminant);
    if(t<0.0) return false;
    intersect = orig + t*dir;
    return true;  
}

//triangle intersection by miffy
bool hitTriangle(vec3 orig,vec3 dir,vec3 a,vec3 b,vec3 c,out vec3 uvt,out vec3 triangleNormal){
   float eps=1e-8;
   vec3 ab=b-a;
   vec3 ac=c-a;

   triangleNormal = normalize(cross(ab,ac));
   vec3 n=cross(dir,ac);

   float det=dot(ab,n);
   // if the determinant is negative the triangle is backfacing
   // if the determinant is close to 0, the ray misses the triangl
   if(det<=eps){ return false;}

   vec3 ao=orig-a;
   float u=dot(ao,n)/det;
   if(u<0.0 || u>1.0){ return false;}

   vec3 e=cross(ao,ab);
   float v=dot(dir,e)/det;
   if(v<0.0||u+v>1.0){ return false;}

   float t= dot(ac,e)/det;
   uvt = vec3(u,v,t);
   return true;
}


void Camera(out Ray ray, vec3 lookAt, vec3 up, float angle, float aspect) {

    vec3 g = normalize(lookAt - ray.orig);
    vec3 u = normalize(cross(g, up));
    vec3 v = normalize(cross(u, g));
    u = u * tan(radians(angle * .5));
    v = v * tan(radians(angle * .5)) / aspect;
    ray.dir = normalize(g + ray.dir.x * u + ray.dir.y * v);

}

//return intersection point with lightSource for the shadow ray
vec3 hitLightSource(Ray R_, Sphere sphere){
    vec3 hit = vec3(0.0,0.0,0.0);
    float mindist = -1000.;
    vec3 intersect;
    bool isHit = hitSphere(R_.orig,R_.dir,sphere.center,sphere.radius,hit);

    if(isHit && hit.z > mindist)
    {
        intersect = hit;

    }

    return intersect; 
}


//return intersection point with the mesh for the shadow ray
vec3 hitMesh(Ray R_){
    float mindist = -1000.;
    vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
    vec3 intersect = vec3(0.0,0.0,0.0);

    for (int i = 0; i < vertsCount; i += 3) {
        a = texelFetch(uMeshData, ivec2(i, 0), 0);
        b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
        c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));

        vec3 triangleNormal;
        vec3 uvt;
        bool isHit = hitTriangle(R_.orig,R_.dir, a.xyz,b.xyz,c.xyz,uvt, triangleNormal);
        if (isHit) {
            intersect = R_.orig + R_.dir*uvt.z;
            float z = intersect.z;
            if (z>mindist) {
             mindist = z;
            }
        }      
    }
    return intersect;
}

//this is where if it is shadow we multiply the color with vec3(.4,.4,.4) else with vec3(1.,1.,1.,) so it does not affect the color if its not a shadow
vec3 calcShadow(Sphere lightSource, vec3 hitPos){
    vec3 color;

    vec3 lightDir =  normalize(lightSource.center-hitPos);
    Ray shadowRay = Ray(hitPos, lightDir);

    vec3 isHitLightDir = hitLightSource(shadowRay,lightSource);
    vec3 isHitMesh = hitMesh(shadowRay);

    if (isHitMesh.z > isHitLightDir.z ) {
        color = vec3(0.4,0.4,0.4);
    }else{
        color = vec3(1.,1.,1.);    
    }
    return color;
}

//function that it affects the mesh if it is hit by light we multiply with color of the Hitpoint with diffuse 
vec3 getLight(vec3 color, Sphere sphere, vec3 intersect, vec3 normal){
    vec3 lightDir =  normalize(sphere.center-intersect);
    float diffuse = clamp(dot(lightDir, normal), 0., 1.);
    return color*diffuse;
}


// we check if a ray intersected the scene, if it does we return the hitPos of the inteersection,
// the normal of that hitpoint the material and this bool isSphere if it intersectesd the floor, which is a giant sphere(in order to make it a floor)
bool hitScene(Ray R_, out vec3 hitPos, out vec3 normal, out Material material, Sphere sphere, out bool isShpere, Sphere lightSource){  // na thimithw na thesw to isShpere false stin trace synartisi

    vec4 a = vec4(0.0), b = vec4(0.0), c = vec4(0.0);
    float mindist = -1000.;
    bool weHitSomething = false;
    vec3 hitPos1 = vec3(0.);
    vec3 triangleNormal = vec3(0.,0.,0.);
    vec3 sphereNormal;


    //here we chck all the mesh if we hit a triangle if the mesh and we keep the closest hitpoint
    for (int i = 0; i < vertsCount; i += 3) {

        a = texelFetch(uMeshData, ivec2(i, 0), 0);
        b = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(1, 0));
        c = texelFetchOffset(uMeshData, ivec2(i, 0), 0, ivec2(2, 0));

        vec3 uvt;
        bool isHit = hitTriangle(R_.orig,R_.dir, a.xyz,b.xyz,c.xyz,uvt, triangleNormal);
        if (isHit) {
            vec3 intersect = R_.orig + R_.dir*uvt.z;
            float z = intersect.z;
            if (z>mindist) {
                hitPos1 = intersect;

                mindist = z;
            }
        }      
    }


    //here we check if it intersected the floor which is a sphere and we keep the position of the hitPos
    vec3 hit = vec3(0.);
    bool isHit = hitSphere(R_.orig,R_.dir,sphere.center,sphere.radius,hit);

    if(isHit && hit.z > mindist)
    {
        mindist = hit.z;
        sphereNormal = (hit - sphere.center) / sphere.radius;
    }



    //here we have probably have 2 intersection points, one of a sphere, and one of a triangle
    // we keep the max of them cause the min dist is actually -1000. so we keep the closest hitpos

    float flag = max(hitPos1.z, hit.z);

    //based of the type of the interection(triangle or sphere) we assign to the mesh or the floor the material type, the normal and all the
    //information Trace function needs 
    //if we intersected something we set weHitSomething to true

    if ((flag == hitPos1.z) && (flag > -1000.)) {
        weHitSomething = true;
        material.type = METAL;
        material.albedo = vec3(0.9, 0.9, 0.9);
        normal = triangleNormal;
        hitPos = hitPos1;
        isShpere = false;
    }
    else if ((flag == hit.z) && (flag > -1000.)) {
        weHitSomething = true;
        material.type = LAMB;
        material.albedo = vec3(0., 0.9, 0.9);
        normal = sphereNormal;
        hitPos = hit;
        isShpere = true;
    }
    return weHitSomething;
}



//Trace is the main function of the max bounces
vec3 Trace(out Ray ray, Sphere floor, Sphere lightSource){

    vec3 hitPos, normal;
    bool isShpere;
    Material material;
    vec3 color = vec3(0.);
    vec3 attenuation = vec3(1.);
    vec3 light, shadow;

    //this if for every ray to bounce 4 times.(hopefully)
    for(int i=0; i< MAX_BOUNCES; i++){

        // we check if we hit something
        if(hitScene(ray, hitPos, normal, material, floor, isShpere, lightSource)){

            //we check the material type(if it is metal it is refering to the mesh)
            if (material.type == METAL) {

               //we calculate the new direction

                vec3 direction = reflect(ray.dir, normal);

                //here we check if the new direction and the hitPos normal is >0 then i do all the calculations 
                //and we start the new ray from the hitPos to the reflected direction

                // HERE is the problem for some reason it passes the dot product !!!!!!!
                //and it enters in the else where it assigns the color to hitPos  !!!!!!!

                if (dot(direction,normal) > 0.) {
                    ray = Ray(hitPos, direction); 
                    light = getLight(color, lightSource,hitPos, normal);
                    shadow = calcShadow(lightSource, hitPos);
                    color *= material.albedo * attenuation * light *shadow;
                    attenuation *= material.albedo;
                }

                else{

                    color = hitPos;
                }
            }

             if (material.type == LAMB) {
                vec3 direction = reflect(ray.dir, normal);
                if (dot(direction,normal) > 0.) {
                    ray = Ray(hitPos, direction);
                    light = getLight(color, lightSource,hitPos, normal);
                    shadow = calcShadow(lightSource, hitPos);
                    color *= material.albedo * attenuation*light*shadow;
                    attenuation *= material.albedo;
                }
                else{
                //   color = hitPos;
                }
            }
        }

        else{
            color = attenuation;
        }
    }

    return color;
}


void main() {

    //initialize lightSource, floor, Ray, camera
    Sphere lightSource = Sphere(vec3(1.,3.,1.), 0.18);
    Sphere floor  = Sphere(vec3(0., -1e3, 0.), 1e3);
    R_ = Ray(vec3(0.0, 1.0, 6.0), vec3(vuv, -1.));
    Camera(R_, vec3(0., 0., 1.), vec3(0., 1., 0.), 90.0, (Res.x / Res.y));

    // rotation
    R_.dir = mat3(uRot) * R_.dir;
    R_.orig = mat3(uRot) * R_.orig;

    //color coming from the trace function
    vec3 color = Trace(R_, floor, lightSource);

    fragColor.rgb = color;
    fragColor.a = 1.0;
}

I want to know if I did something fundamentally wrong with the hitScene and Trace function. Triangle intersections and light worked fine without the recursion (when I had them all in main function).

In my hitScene function I check for 2 intersections. One with the floor which is a giant sphere, and one with the rest of the mesh, and I return the normal of the hit position, the hit Position, and the material of the hit. Then I return it to the trace function.

There based on the material I compute the new reflected ray and apply the materials. Clearly something went completerly wrong and I would hope I would get a hint of where to start looking.

this is what i get, where i iam supposed to get a scene with a floor 1 cube and 2 ico spheres

I have made some minor changes and I'm getting this result. And as you see it does not render correctly behind the spheres enter image description here

4b0
  • 21,981
  • 30
  • 95
  • 142
Thodoris Koutsis
  • 109
  • 1
  • 12
  • I am afraid no one will debug your code for you here ... anyway You are mentioning recursion IIRC that is forbidden in GLSL so your problem might be there see [Reflection and refraction impossible without recursive ray tracing?](https://stackoverflow.com/a/45140313/2521214) on how to workaround it and also on some hints on debugging your code ... – Spektre Mar 12 '19 at 10:18
  • @Spektre thank you for the answer, althought i found the problem it was on the ray-triangle intersection test function, which i was like 100% sure that it was working... – Thodoris Koutsis Mar 12 '19 at 19:37
  • heh glad to hear you debugged it ... ray tracers are very hard to debug ... and yeah been there too with 100% certainity that something works as should be ... many times ... but the two fundamental Murphy's programming laws are always right ... – Spektre Mar 12 '19 at 19:40

0 Answers0