diff --git a/src/raytracer/main.c b/src/raytracer/main.c index 0c4847b..366d03a 100644 --- a/src/raytracer/main.c +++ b/src/raytracer/main.c @@ -4,6 +4,7 @@ #include "window/window.h" #include #include +#include #include #include @@ -13,6 +14,7 @@ typedef struct { f32 radius; vec3f_t centre; colour_t colour; + f32 specular; } sphere_t; typedef enum { @@ -48,9 +50,16 @@ solutions_t ray_intersects_sphere(vec3f_t origin, vec3f_t direction, sphere_t sphere); colour_t trace_ray(vec3f_t origin, vec3f_t direction, f32 t_min, f32 t_max, const scene_t *scene, colour_t default_colour); -f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene); +f32 compute_lighting(vec3f_t position, vec3f_t surface_normal, + vec3f_t view_vector, f32 specular_exponent, + const scene_t *scene); f32 light_diffuse(f32 light_intensity, vec3f_t light_direction, vec3f_t surface_normal); +f32 light_specular(f32 light_intensity, vec3f_t light_direction, + vec3f_t surface_normal, vec3f_t view_vector, + f32 specular_exponent); +f32 cos_angle_between_vectors(vec3f_t v1, vec3f_t v2); +f32 clamp(f32 value, f32 min, f32 max); i32 main(i32 argc, char *argv[]) { colour_t bg = @@ -74,6 +83,7 @@ i32 main(i32 argc, char *argv[]) { .colour = (colour_t){ .rgba.r = 245, .rgba.g = 238, .rgba.b = 158, .rgba.a = 255}, + .specular = 500.0f, }, (sphere_t){ .radius = 1.0f, @@ -81,6 +91,7 @@ i32 main(i32 argc, char *argv[]) { .colour = (colour_t){ .rgba.r = 59, .rgba.g = 142, .rgba.b = 165, .rgba.a = 255}, + .specular = 10.0f, }, (sphere_t){ .radius = 1.0f, @@ -88,6 +99,7 @@ i32 main(i32 argc, char *argv[]) { .colour = (colour_t){ .rgba.r = 171, .rgba.g = 52, .rgba.b = 40, .rgba.a = 255}, + .specular = 500.0f, }, (sphere_t){ .radius = 5000.0f, @@ -95,6 +107,7 @@ i32 main(i32 argc, char *argv[]) { .colour = (colour_t){ .rgba.r = 255, .rgba.g = 255, .rgba.b = 0, .rgba.a = 255}, + .specular = 1000.0f, }, }; @@ -200,20 +213,34 @@ colour_t trace_ray(vec3f_t origin, vec3f_t direction, f32 t_min, f32 t_max, return default_colour; } - vec3f_t P = + vec3f_t position = vec_add(vec3f_t, origin, vec_mul_num(vec3f_t, direction, closest_t)); - vec3f_t N = vec_sub(vec3f_t, P, closest_sphere->centre); - N = vec_div_num(vec3f_t, N, vec_magnitude(vec3f_t, N)); - f32 light = compute_lighting(P, N, scene); + vec3f_t surface_normal = vec_sub(vec3f_t, position, closest_sphere->centre); + surface_normal = vec_div_num(vec3f_t, surface_normal, + vec_magnitude(vec3f_t, surface_normal)); - return (colour_t){.rgba.r = closest_sphere->colour.rgba.r * light, - .rgba.g = closest_sphere->colour.rgba.g * light, - .rgba.b = closest_sphere->colour.rgba.b * light, + vec3f_t view_vector = vec_mul_num(vec3f_t, direction, -1.0f); + + f32 light = compute_lighting(position, surface_normal, view_vector, + closest_sphere->specular, scene); + + f32 r = (f32)(closest_sphere->colour.rgba.r) * light; + r = clamp(r, 0.0f, (f32)UINT8_MAX); + f32 g = (f32)(closest_sphere->colour.rgba.g) * light; + g = clamp(g, 0.0f, (f32)UINT8_MAX); + f32 b = (f32)(closest_sphere->colour.rgba.b) * light; + b = clamp(b, 0.0f, (f32)UINT8_MAX); + + return (colour_t){.rgba.r = (u8)r, + .rgba.g = (u8)g, + .rgba.b = (u8)b, .rgba.a = closest_sphere->colour.rgba.a}; } -f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene) { +f32 compute_lighting(vec3f_t position, vec3f_t surface_normal, + vec3f_t view_vector, f32 specular_exponent, + const scene_t *scene) { f32 I = 0.0f; light_t light = {0}; @@ -223,20 +250,25 @@ f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene) { if (light.type == LIGHT_TYPE_AMBIENT) { I += light.intensity; } else { - vec3f_t L = {0}; + vec3f_t light_direction = {0}; switch (light.type) { case LIGHT_TYPE_POINT: - L = vec_sub(vec3f_t, light.position, P); + light_direction = vec_sub(vec3f_t, light.position, position); break; case LIGHT_TYPE_DIRECTIONAL: - L = light.direction; + light_direction = light.direction; break; default: break; } - I += light_diffuse(light.intensity, L, N); + I += light_diffuse(light.intensity, light_direction, surface_normal); + + if (specular_exponent != -1.0f) { + I += light_specular(light.intensity, light_direction, surface_normal, + view_vector, specular_exponent); + } } } @@ -245,18 +277,48 @@ f32 compute_lighting(vec3f_t P, vec3f_t N, const scene_t *scene) { f32 light_diffuse(f32 light_intensity, vec3f_t light_direction, vec3f_t surface_normal) { + return light_intensity * + cos_angle_between_vectors(light_direction, surface_normal); +} + +f32 light_specular(f32 light_intensity, vec3f_t light_direction, + vec3f_t surface_normal, vec3f_t view_vector, + f32 specular_exponent) { + vec3f_t _2N = vec_mul_num(vec3f_t, surface_normal, 2.0f); f32 dot_product = vec_dot(vec3f_t, light_direction, surface_normal); + vec3f_t _2N_mul_dot = vec_mul_num(vec3f_t, _2N, dot_product); + + vec3f_t R = vec_sub(vec3f_t, _2N_mul_dot, light_direction); + + return light_intensity * + powf(cos_angle_between_vectors(R, view_vector), specular_exponent); +} + +f32 cos_angle_between_vectors(vec3f_t v1, vec3f_t v2) { + f32 dot_product = vec_dot(vec3f_t, v1, v2); + if (dot_product < 0.0f) { return 0.0f; } - f32 divisor = vec_magnitude(vec3f_t, light_direction) * - vec_magnitude(vec3f_t, surface_normal); + f32 divisor = vec_magnitude(vec3f_t, v1) * vec_magnitude(vec3f_t, v2); if (divisor == 0.0f) { return 0.0f; } - return light_intensity * dot_product / divisor; + return dot_product / divisor; +} + +f32 clamp(f32 value, f32 min, f32 max) { + if (value < min) { + return min; + } + + if (value > max) { + return max; + } + + return value; }