An educational project to build a ray tracer's core components, including multiple shading models, reflections, and a BVH acceleration structure.
C | 3D Graphics | Ray Tracing
This project, part of a 'Graphics and Game Technology' course, involved implementing the fundamental algorithms of a ray tracer from a provided C framework. I implemented the initial camera ray generation and various shading models, including Matte, Blinn-Phong, and recursive reflections, complete with shadow casting. A major component was performance optimization, where I implemented an efficient traversal algorithm for a Bounding Volume Hierarchy (BVH) to accelerate ray-triangle intersections for complex 3D models. Finally, I added anti-aliasing to improve final image quality.
Wrote the core logic for camera ray generation, shooting rays from the camera’s position through the center of each pixel.
Implemented a BVH traversal algorithm to drastically speed up intersection tests on complex models.
Optimized BVH traversal by implementing branch pruning, skipping nodes that are farther than the closest intersection already found.
Implemented a Matte shader to compute surface color based on light contribution.
Added shadow rays to the shaders, checking for occlusions between intersection points and light sources.
Built a Blinn-Phong shader to render metallic-like surfaces by calculating ambient, diffuse, and specular lighting components.
Implemented a recursive reflection shader that combined 75% matte shading with 25% reflected color, obtained by tracing new rays recursively.
Added anti-aliasing (AA) by shooting and averaging four rays per pixel through sub-pixel centers to smooth jagged edges.
shade_matte(intersection_point ip)
{
float c = 0;
float shadow_ray_origin_offset = 1.0e-3;
vec3 normal = ip.n;
for (int i = 0; i < scene_num_lights; i++) {
light scene_light = scene_lights[i];
float intensity = scene_light.intensity;
vec3 direction = v3_normalize(v3_subtract(scene_light.position, ip.p));
float point_light_contribution = 0;
vec3 shadow_ray_origin = v3_add(ip.p, v3_multiply(direction, shadow_ray_origin_offset));
if (!shadow_check(shadow_ray_origin, direction)) {
point_light_contribution = intensity * fmax(0, v3_dotprod(normal, direction));
}
c += point_light_contribution;
if (c >= 1) {
c = 1;
break;
}
}
c = fmin(1, c + scene_ambient_light);
return v3_create(c, c, c);
}
light scene_light = scene_lights[i];
float intensity = scene_light.intensity;
vec3 direction = v3_normalize(v3_subtract(scene_light.position, ip.p));
vec3 halfway_vec = v3_normalize(v3_add(ip.i, direction));
float diffuse_contribution = 0;
float specular_contribution = 0;
vec3 shadow_ray_origin = v3_add(ip.p, v3_multiply(direction, shadow_ray_origin_offset));
if (!shadow_check(shadow_ray_origin, direction)) {
diffuse_contribution = intensity * fmax(0, v3_dotprod(normal, direction));
specular_contribution = intensity * powf(v3_dotprod(normal, halfway_vec), alpha);
}
c_diffuse += diffuse_contribution;
c_specular += specular_contribution;