Thodoris Koutsis
Thodoris Koutsis

Reputation: 129

Problem in rendering triangle mesh with reflections in webgl and opengles

iam currently writing a ray tracer with triangle intersection. i have exported the mesh as a .raw format just to have coordinates of each vertex. And i have noticed a thing that when i rotate the camera it kind of showing occluded mesh.

i post my triangle intersection function below in my fragment shader

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

below it the hitScene function where i go throuth all the mesh and check for intersections

bool hitScene(Ray R_, out vec3 hitPos, out vec3 normal, out Material material, 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.),triangleNormal = vec3(0.,0.,0.), 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;
                weHitSomething = true;
                material.type = METAL;
                material.albedo = vec3(.0, .8, .8);
                normal = triangleNormal;
                hitPos = hitPos1;
            }
        }      
    }      
    
    return weHitSomething;
}

and below is the Trace function that loops through the traces

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

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

    //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, lightSource)){
  
            //we calculate the new direction
            vec3 direction = normalize(reflect(ray.dir, normal));
                
            ray = Ray(hitPos, direction); 
             
            color *= material.albedo * attenuation*hitPos;
            attenuation *= material.albedo;    
        }

        else{
            color = attenuation;
        }
    }

    return color;
}

and after that iam getting something like that: enter image description here As you see I am getting reflections but i am seeing the cube as well inside the sphere. Does that mean i fail the ray-triangle intersection tests? or the min distance is not correct??

What might be the source of the problem?

Upvotes: 1

Views: 294

Answers (1)

Rabbid76
Rabbid76

Reputation: 211166

This is won't solve all your issues, but it is one part of the answer.

In your code there is a basic misunderstanding of Barycentric coordinates. The barycentric coordinates are not a coordinate, but they are 3 values in range [0, 1]. Imagine a barycentric coordinates as 3 weights, which weight the 3 corner points of a triangle.

The sum of the 3 scales of a barycentric coordinate is 1:

b.x + b.x + b.z == 1

If a triangle is defined by three points A, B and C, then the point X on the triangle, which is defined by the barycentric coordinate can be calculated by the formula:

X =  A * b.x + B * b.y + C * b.z

This means the calculation of the intersection distance in hitScene has to be adapted as follows:

vec3 intersect = a.xyz * uvt.x + b.xyz * uvt.y + c.xyz * uvt.z;

Thi distance to the origin of the ray is the is the length of the vector form the origin to the intersection point i the direction of the ray.
In general I would calculate this somehow like that:

float z = dot(intersect - R_.orig, normalize(R_.dir));

Further the algorithm to calculate the barycentric coordinates looks odd. I'm very sure it has to be uvt = vec3(t, u, v) rather than uvt = vec3(u, v, t);. But this has to be investigated further.

The intersection of a triangle and a ray can be calculated as follows:

The ray is defined by a point R0 and a direction D.
The plane is defined by a triangle with the three points PA, PB, and PC.

The normal vector of the plane can be calculated by the cross product of 2 legs of the triangle:

N  =  normalize( cross(PC-PA, PB-PA)

The normal distance n of the point R0 to the plane is:

n  =  | R0 - PA | * cos(alpha)  =  dot(PA - R0, N)

It follows that the distance d of the intersection point X to the origin of the ray R0 is:

d  =  n / cos(beta)  =  n / dot(D, N)

The intersection point X is:

X  =  R0 + D * d  =  R0 + D * dot(PA - R0, N) / dot(D, N)

Applying the to your code, I recommend to use the following function hitTriangle:

  • The return value is true if the ray intersects the triangle primitive in the positive direction of ray (dir) after the origin of the ray (orig).
  • The output N is the normal vector of the plane which is defined by the triangle.
  • The output uvt are the Barycentric coordinates of the triangle.
  • The output x is the intersection point on the triangle.
  • And the output dist is the distance from the origin of the ray (orig) to the intersection point x.

Note, the ray direction (dir) has to be normalized. The output values have only a meaning if the function returns true.

bool hitTriangle(
    vec3 orig, vec3 dir, vec3 a, vec3 b, vec3 c,
    out vec3 uvt, out vec3 N, out vec3 x, out float dist) {

    float eps=1e-8;

    vec3 ab = b - a;
    vec3 ac = c - a;

    N = normalize(cross(ab, ac));

    dist = dot(a - orig, N) / dot(dir, N);
    x    = orig + dir * dist;

    vec3 ax = x - a;

    float d00 = dot(ab, ab);
    float d01 = dot(ab, ac);
    float d11 = dot(ac, ac);
    float d20 = dot(ax, ab);
    float d21 = dot(ax, ac);

    float denom = d00 * d11 - d01 * d01; // determinant

    // if the determinant is negative the triangle is backfacing
    // if the determinant is close to 0, the ray misses the triangl
    if ( denom <= eps )
        return false;

    uvt.y = (d11 * d20 - d01 * d21) / denom;
    if ( uvt.y < 0.0 || uvt.y > 1.0 )
        return false;

    uvt.z = (d00 * d21 - d01 * d20) / denom;
    if ( uvt.z < 0.0 || uvt.z > 1.0 )
        return false;

    uvt.x = 1.0 - uvt.y - uvt.z;
    if ( uvt.x < 0.0 || uvt.x > 1.0 )
        return false;

    return true;
}

Use the hit test (hitTriangle) in the function hitScene as follows:

bool hitScene(Ray R_, out vec3 hitPos, out vec3 normal, out Material material, 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.),triangleNormal = vec3(0.,0.,0.), sphereNormal;

    vec3 ray_dir = normalize(R_.dir);

    //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;
        vec3 intersect;
        float z;
        bool isHit = hitTriangle(R_.orig, ray_dir, a.xyz, b.xyz, c.xyz, uvt, triangleNormal, intersect, z);
        if (isHit)
        {
            if (z < mindist && z > 0.001)
            {
                hitPos1 = intersect;

                mindist = z;
                weHitSomething = true;
                material.type = METAL;
                material.albedo = vec3(.0, .8, .8);
                normal = triangleNormal;
                hitPos = hitPos1;
            }
        }
    }

    return weHitSomething;
}

Upvotes: 1

Related Questions