Reputation: 129
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:
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
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
:
true
if the ray intersects the triangle primitive in the positive direction of ray (dir
) after the origin of the ray (orig
). N
is the normal vector of the plane which is defined by the triangle.uvt
are the Barycentric coordinates of the triangle.x
is the intersection point on the triangle.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