diff --git a/src/raytracer/main.c b/src/raytracer/main.c index 1e6816c..e2d8382 100644 --- a/src/raytracer/main.c +++ b/src/raytracer/main.c @@ -1,22 +1,77 @@ #include "SDL_events.h" #include "c_cpp_aliases/aliases.h" +#include "vector/vec.h" #include "window/window.h" +#include #include #include #include +#define ARR_LEN(ARR) sizeof(ARR) / sizeof(ARR[0]) + +typedef struct { + f32 radius; + vec3f_t centre; + colour_t colour; +} sphere_t; + +typedef struct { + sphere_t *spheres; + u32 count; +} scene_t; + +typedef struct { + f32 t1; + f32 t2; +} solutions_t; + +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); + int main(int argc, char *argv[]) { colour_t bg = (colour_t){.r = 128, .g = 128, .b = 128, .a = 255}; + vec3f_t camera = {.x = 0.0f, .y = 0.0f, .z = 0.0f}; + vec3f_t viewport = {.x = 1.0f, .y = 1.0f, .z = 1.0f}; window_t window = {0}; - if (!init_window(&window, 1000, 800, "CG From Scratch")) { + if (!init_window(&window, 800, 800, "CG From Scratch")) { return EXIT_FAILURE; } bool running = true; SDL_Event event = {0}; + sphere_t spheres[] = { + (sphere_t){ + .radius = 1.0f, + .centre = (vec3f_t){.x = 0.0f, .y = -1.0f, .z = 3.0f}, + .colour = (colour_t){.r = 255, .g = 0, .b = 0, .a = 255}, + }, + (sphere_t){ + .radius = 1.0f, + .centre = (vec3f_t){.x = -2.0f, .y = 0.0f, .z = 4.0f}, + .colour = (colour_t){.r = 0, .g = 255, .b = 0, .a = 255}, + }, + (sphere_t){ + .radius = 1.0f, + .centre = (vec3f_t){.x = 2.0f, .y = 0.0f, .z = 4.0f}, + .colour = (colour_t){.r = 0, .g = 0, .b = 255, .a = 255}, + }, + }; + + scene_t scene = { + .spheres = spheres, + .count = ARR_LEN(spheres), + }; + + i32 w_min = ((i32)window.half_width) * -1; + i32 w_max = (i32)window.half_width; + i32 h_min = ((i32)window.half_height) * -1; + i32 h_max = (i32)window.half_height; + while (running) { while (SDL_PollEvent(&event)) { switch (event.type) { @@ -28,6 +83,14 @@ int main(int argc, char *argv[]) { clear_window(&window, bg); + for (i32 y = h_min; y < h_max; ++y) { + for (i32 x = w_min; x < w_max; ++x) { + vec3f_t direction = window_to_viewport(&window, x, y, viewport); + colour_t colour = trace_ray(camera, direction, 1, INFINITY, &scene, bg); + set_pixel(&window, x, y, colour); + } + } + swap_buffers(&window); } @@ -35,3 +98,52 @@ int main(int argc, char *argv[]) { return EXIT_SUCCESS; } + +solutions_t ray_intersects_sphere(vec3f_t origin, vec3f_t direction, + sphere_t sphere) { + f32 r = sphere.radius; + vec3f_t CO = vec_sub(vec3f_t, origin, sphere.centre); + + f32 a = vec_dot(vec3f_t, direction, direction); + f32 b = 2.0f * vec_dot(vec3f_t, CO, direction); + f32 c = vec_dot(vec3f_t, CO, CO) - r * r; + + f32 discriminant = b * b - 4 * a * c; + if (discriminant < 0) { + return (solutions_t){INFINITY, INFINITY}; + } + + f32 t1 = (-b + sqrtf(discriminant)) / (2 * a); + f32 t2 = (-b - sqrtf(discriminant)) / (2 * a); + + return (solutions_t){t1, t2}; +} + +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 closest_t = INFINITY; + sphere_t *closest_sphere = NULL; + + for (u32 i = 0; i < scene->count; ++i) { + solutions_t solutions = + ray_intersects_sphere(origin, direction, scene->spheres[i]); + + if (solutions.t1 >= t_min && solutions.t1 <= t_max && + solutions.t1 < closest_t) { + closest_t = solutions.t1; + closest_sphere = &(scene->spheres[i]); + } + + if (solutions.t2 >= t_min && solutions.t2 <= t_max && + solutions.t2 < closest_t) { + closest_t = solutions.t2; + closest_sphere = &(scene->spheres[i]); + } + } + + if (!closest_sphere) { + return default_colour; + } + + return closest_sphere->colour; +}