From ec322b7c16ee0e15df6a462072ae1df693dcd57a Mon Sep 17 00:00:00 2001 From: Jean-Michel Gorius Date: Thu, 10 Nov 2022 23:09:05 +0100 Subject: [PATCH] Add hittable objects --- build.sh | 2 +- color.c | 6 ++-- color.h | 8 ++--- hittable.c | 98 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ hittable.h | 56 +++++++++++++++++++++++++++++++ main.c | 67 ++++++++++++++++++++----------------- point3.c | 8 ++--- point3.h | 8 ++--- ray.c | 2 +- ray.h | 10 +++--- utils.c | 7 ++++ utils.h | 6 ++++ vec3.c | 26 +++++++-------- vec3.h | 24 ++++++------- 14 files changed, 250 insertions(+), 78 deletions(-) create mode 100644 hittable.c create mode 100644 hittable.h create mode 100644 utils.c create mode 100644 utils.h diff --git a/build.sh b/build.sh index 08854f0..0b2e497 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/bash CC=${CC:-"gcc"} -CFLAGS=${CFLAGS:-"-Wall -Wextra -std=c18 -g"} +CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -g -fsanitize=address"} LDFLAGS=${LDFLAGS:-"-lm"} MAIN_FILE=${MAIN_FILE:-"main.c"} diff --git a/color.c b/color.c index a52b93a..484fcb4 100644 --- a/color.c +++ b/color.c @@ -1,15 +1,15 @@ #include "color.h" #include -color color_lerp(color c1, color c2, double t) { - return (color){ +Color color_lerp(Color c1, Color c2, double t) { + return (Color){ (1.0 - t) * c1.r + t * c2.r, (1.0 - t) * c1.g + t * c2.g, (1.0 - t) * c1.b + t * c2.b, }; } -void color_write(FILE *out, color c) { +void color_write(FILE *out, Color c) { fprintf(out, "%d %d %d\n", (int)(255.999 * c.r), (int)(255.999 * c.g), (int)(255.999 * c.b)); } diff --git a/color.h b/color.h index 55b47b6..dbffda3 100644 --- a/color.h +++ b/color.h @@ -3,12 +3,12 @@ #include -typedef struct color { +typedef struct Color { double r, g, b; -} color; +} Color; -color color_lerp(color c1, color c2, double t); +Color color_lerp(Color c1, Color c2, double t); -void color_write(FILE *out, color c); +void color_write(FILE *out, Color c); #endif /* INCLUDED_COLOR_H */ diff --git a/hittable.c b/hittable.c new file mode 100644 index 0000000..d5d9cdf --- /dev/null +++ b/hittable.c @@ -0,0 +1,98 @@ +#include "hittable.h" +#include "point3.h" +#include "vec3.h" + +#include +#include + +void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal) { + record->front_face = vec3_dot(r.direction, outward_normal) < 0; + record->normal = + record->front_face ? outward_normal : vec3_neg(outward_normal); +} + +bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, + HitRecord *record) { + switch (hittable->type) { + case HITTABLE_LIST: + return hittable_list_hit(hittable->data, r, t_min, t_max, record); + case HITTABLE_SPHERE: + return sphere_hit(hittable->data, r, t_min, t_max, record); + } + return false; +} + +Hittable make_hittable_list(const HittableList *list) { + return (Hittable){.type = HITTABLE_LIST, .data = list}; +} + +static void hittable_list_grow(HittableList *list, size_t n) { + if (list->objects) { + list->objects = realloc(list->objects, n); + if (!list->objects) + abort(); + list->capacity = n; + } else { + list->objects = malloc(n * sizeof(Hittable)); + if (!list->objects) + abort(); + list->capacity = n; + list->size = 0; + } +} + +void hittable_list_add(HittableList *list, Hittable hittable) { + if (list->capacity == list->size) + hittable_list_grow(list, list->capacity == 0 ? 16 : list->capacity); + list->objects[list->size++] = hittable; +} + +bool hittable_list_hit(const HittableList *list, Ray r, double t_min, + double t_max, HitRecord *record) { + bool hit_anything = false; + double closest_so_far = t_max; + + for (size_t i = 0; i < list->size; ++i) { + if (hittable_hit(&list->objects[i], r, t_min, closest_so_far, record)) { + hit_anything = true; + closest_so_far = record->t; + } + } + + return hit_anything; +} + +void hittable_list_free(HittableList *list) { + free(list->objects); + list->objects = 0; +} + +Hittable make_hittable_sphere(const Sphere *sphere) { + return (Hittable){.type = HITTABLE_SPHERE, sphere}; +} + +bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, + HitRecord *record) { + Vec3 oc = point3_sub(r.origin, sphere->center); + double a = vec3_length2(r.direction); + double half_b = vec3_dot(oc, r.direction); + double c = vec3_length2(oc) - sphere->radius * sphere->radius; + double discriminant = half_b * half_b - a * c; + if (discriminant < 0) + return false; + + double square_root = sqrt(discriminant); + double root = (-half_b - square_root) / a; + if (root < t_min || t_max < root) { + root = (-half_b + square_root) / a; + if (root < t_min || t_max < root) + return false; + } + + record->t = root; + record->p = ray_at(r, root); + Vec3 outward_normal = + vec3_div(point3_sub(record->p, sphere->center), sphere->radius); + hit_record_set_face_normal(record, r, outward_normal); + return true; +} diff --git a/hittable.h b/hittable.h new file mode 100644 index 0000000..cbced23 --- /dev/null +++ b/hittable.h @@ -0,0 +1,56 @@ +#ifndef INCLUDED_HITTABLE_H +#define INCLUDED_HITTABLE_H + +#include "point3.h" +#include "ray.h" +#include "vec3.h" + +#include +#include + +typedef struct HitRecord { + Point3 p; + Vec3 normal; + double t; + bool front_face; +} HitRecord; + +void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal); + +typedef enum HittableType { + HITTABLE_LIST, + HITTABLE_SPHERE, +} HittableType; + +typedef struct Hittable { + HittableType type; + const void *data; +} Hittable; + +bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, + HitRecord *record); + +typedef struct HittableList { + Hittable *objects; + size_t size; + size_t capacity; +} HittableList; + +Hittable make_hittable_list(const HittableList *list); + +void hittable_list_add(HittableList *list, Hittable hittable); +bool hittable_list_hit(const HittableList *list, Ray r, double t_min, + double t_max, HitRecord *record); + void hittable_list_free(HittableList *list); + +typedef struct Sphere { + Point3 center; + double radius; +} Sphere; + +Hittable make_hittable_sphere(const Sphere *sphere); + +bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, + HitRecord *record); + +#endif /* INCLUDED_HITTABLE_H */ diff --git a/main.c b/main.c index 9512fe9..9f87af7 100644 --- a/main.c +++ b/main.c @@ -1,34 +1,24 @@ +#include #include #include #include #include "color.h" +#include "hittable.h" #include "point3.h" #include "ray.h" #include "vec3.h" -double hit_sphere(point3 center, double radius, ray r) { - vec3 oc = point3_sub(r.origin, center); - double a = vec3_length2(r.direction); - double half_b = vec3_dot(oc, r.direction); - double c = vec3_length2(oc) - radius * radius; - double discriminant = half_b * half_b - a * c; - if (discriminant < 0) - return -1.0; - else - return (-half_b - sqrt(discriminant)) / a; -} - -color ray_color(ray r) { - double t = hit_sphere((point3){0, 0, -1}, 0.5, r); - if (t > 0.0) { - vec3 n = vec3_normalize(point3_sub(ray_at(r, t), (point3){0, 0, -1})); - return (color){0.5 * (n.x + 1), 0.5 * (n.y + 1), 0.5 * (n.z + 1)}; +Color ray_color(Ray r, Hittable world) { + HitRecord record; + if (hittable_hit(&world, r, 0, DBL_MAX, &record)) { + return (Color){0.5 * (record.normal.x + 1), 0.5 * (record.normal.y + 1), + 0.5 * (record.normal.z + 1)}; } - vec3 unit_direction = vec3_normalize(r.direction); - t = 0.5 * (unit_direction.y + 1.0); - color gradient1 = {1.0, 1.0, 1.0}; - color gradient2 = {0.5, 0.7, 1.0}; + Vec3 unit_direction = vec3_normalize(r.direction); + double t = 0.5 * (unit_direction.y + 1.0); + Color gradient1 = {1.0, 1.0, 1.0}; + Color gradient2 = {0.5, 0.7, 1.0}; return color_lerp(gradient1, gradient2, t); } @@ -38,17 +28,28 @@ int main(void) { const int image_width = 256; const int image_height = (int)(image_width / aspect_ratio); + /* World */ + HittableList world = {0}; + hittable_list_add(&world, make_hittable_sphere(&(Sphere){ + .center = (Point3){0, 0, -1}, + .radius = 0.5, + })); + hittable_list_add(&world, make_hittable_sphere(&(Sphere){ + .center = (Point3){0, -100.5, -1}, + .radius = 100, + })); + /* Camera parameters */ double viewport_height = 2; double viewport_width = aspect_ratio * viewport_height; double focal_length = 1.0; - point3 origin = {0}; - vec3 horizontal = {viewport_width, 0, 0}; - vec3 vertical = {0, viewport_height, 0}; - vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2)); - offset = vec3_add(offset, (vec3){0, 0, focal_length}); - point3 lower_left_corner = point3_add(origin, vec3_neg(offset)); + Point3 origin = {0}; + Vec3 horizontal = {viewport_width, 0, 0}; + Vec3 vertical = {0, viewport_height, 0}; + Vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2)); + offset = vec3_add(offset, (Vec3){0, 0, focal_length}); + Point3 lower_left_corner = point3_add(origin, vec3_neg(offset)); printf("P3\n%u %u\n255\n", image_width, image_height); @@ -57,22 +58,26 @@ int main(void) { for (int i = 0; i < image_width; ++i) { double u = (double)i / (image_width - 1); double v = (double)j / (image_height - 1); - point3 screen_point = + Point3 screen_point = point3_add(lower_left_corner, vec3_add(vec3_mul(u, horizontal), vec3_mul(v, vertical))); - vec3 direction = point3_sub(screen_point, origin); - ray r = {origin, direction}; - color pixel_color = ray_color(r); + Vec3 direction = point3_sub(screen_point, origin); + Ray r = {origin, direction}; + Color pixel_color = ray_color(r, make_hittable_list(&world)); color_write(stdout, pixel_color); } } fprintf(stderr, "\nDone.\n"); + hittable_list_free(&world); + return 0; } #include "color.c" +#include "hittable.c" #include "point3.c" #include "ray.c" +#include "utils.c" #include "vec3.c" diff --git a/point3.c b/point3.c index ec218b6..9999f7c 100644 --- a/point3.c +++ b/point3.c @@ -1,9 +1,9 @@ #include "point3.h" -point3 point3_add(point3 p, vec3 v) { - return (point3){p.x + v.x, p.y + v.y, p.z + v.z}; +Point3 point3_add(Point3 p, Vec3 v) { + return (Point3){p.x + v.x, p.y + v.y, p.z + v.z}; } -vec3 point3_sub(point3 p1, point3 p2) { - return (vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z}; +Vec3 point3_sub(Point3 p1, Point3 p2) { + return (Vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z}; } diff --git a/point3.h b/point3.h index 158a0e4..69bdc63 100644 --- a/point3.h +++ b/point3.h @@ -3,11 +3,11 @@ #include "vec3.h" -typedef struct point3 { +typedef struct Point3 { double x, y, z; -} point3; +} Point3; -point3 point3_add(point3 p, vec3 v); -vec3 point3_sub(point3 p1, point3 p2); +Point3 point3_add(Point3 p, Vec3 v); +Vec3 point3_sub(Point3 p1, Point3 p2); #endif /* INCLUDED_POINT3_H */ diff --git a/ray.c b/ray.c index cb67376..f789ac0 100644 --- a/ray.c +++ b/ray.c @@ -1,6 +1,6 @@ #include "ray.h" #include "point3.h" -point3 ray_at(ray r, double t) { +Point3 ray_at(Ray r, double t) { return point3_add(r.origin, vec3_mul(t, r.direction)); } diff --git a/ray.h b/ray.h index a905f46..86d0ff5 100644 --- a/ray.h +++ b/ray.h @@ -4,11 +4,11 @@ #include "point3.h" #include "vec3.h" -typedef struct ray { - point3 origin; - vec3 direction; -} ray; +typedef struct Ray { + Point3 origin; + Vec3 direction; +} Ray; -point3 ray_at(ray r, double t); +Point3 ray_at(Ray r, double t); #endif /* INCLUDED_RAY_H */ diff --git a/utils.c b/utils.c new file mode 100644 index 0000000..a964d72 --- /dev/null +++ b/utils.c @@ -0,0 +1,7 @@ +#include "utils.h" + +#include + +double degrees_to_radians(double degrees) { + return degrees * M_PI / 180.0; +} diff --git a/utils.h b/utils.h new file mode 100644 index 0000000..82e3478 --- /dev/null +++ b/utils.h @@ -0,0 +1,6 @@ +#ifndef INCLUDED_UTILS_H +#define INCLUDED_UTILS_H + +double degrees_to_radians(double degrees); + +#endif /* INCLUDED_UTILS_H */ diff --git a/vec3.c b/vec3.c index 3586c22..fb6079b 100644 --- a/vec3.c +++ b/vec3.c @@ -2,32 +2,32 @@ #include -vec3 vec3_neg(vec3 v) { return (vec3){-v.x, -v.y, -v.z}; } +Vec3 vec3_neg(Vec3 v) { return (Vec3){-v.x, -v.y, -v.z}; } -vec3 vec3_add(vec3 v1, vec3 v2) { - return (vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z}; +Vec3 vec3_add(Vec3 v1, Vec3 v2) { + return (Vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z}; } -vec3 vec3_sub(vec3 v1, vec3 v2) { - return (vec3){v1.x - v2.x, v1.y - v2.y, v1.z - v2.z}; +Vec3 vec3_sub(Vec3 v1, Vec3 v2) { + return (Vec3){v1.x - v2.x, v1.y - v2.y, v1.z - v2.z}; } -vec3 vec3_mul(double c, vec3 v) { return (vec3){c * v.x, c * v.y, c * v.z}; } +Vec3 vec3_mul(double c, Vec3 v) { return (Vec3){c * v.x, c * v.y, c * v.z}; } -vec3 vec3_div(vec3 v, double c) { return (vec3){v.x / c, v.y / c, v.z / c}; } +Vec3 vec3_div(Vec3 v, double c) { return (Vec3){v.x / c, v.y / c, v.z / c}; } -double vec3_length(vec3 v) { return sqrt(vec3_length2(v)); } +double vec3_length(Vec3 v) { return sqrt(vec3_length2(v)); } -double vec3_length2(vec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } +double vec3_length2(Vec3 v) { return v.x * v.x + v.y * v.y + v.z * v.z; } -vec3 vec3_normalize(vec3 v) { return vec3_div(v, vec3_length(v)); } +Vec3 vec3_normalize(Vec3 v) { return vec3_div(v, vec3_length(v)); } -double vec3_dot(vec3 v1, vec3 v2) { +double vec3_dot(Vec3 v1, Vec3 v2) { return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z; } -vec3 vec3_cross(vec3 v1, vec3 v2) { - return (vec3){ +Vec3 vec3_cross(Vec3 v1, Vec3 v2) { + return (Vec3){ v1.y * v2.z - v1.z * v2.y, v1.z * v2.x - v1.x * v2.z, v1.x * v2.y - v1.y * v2.x, diff --git a/vec3.h b/vec3.h index 88393fc..4ec62e2 100644 --- a/vec3.h +++ b/vec3.h @@ -1,22 +1,22 @@ #ifndef INCLUDED_VEC3_H #define INCLUDED_VEC3_H -typedef struct vec3 { +typedef struct Vec3 { double x, y, z; -} vec3; +} Vec3; -vec3 vec3_neg(vec3 v); -vec3 vec3_add(vec3 v1, vec3 v2); -vec3 vec3_sub(vec3 v1, vec3 v2); -vec3 vec3_mul(double c, vec3 v); -vec3 vec3_div(vec3 v, double c); +Vec3 vec3_neg(Vec3 v); +Vec3 vec3_add(Vec3 v1, Vec3 v2); +Vec3 vec3_sub(Vec3 v1, Vec3 v2); +Vec3 vec3_mul(double c, Vec3 v); +Vec3 vec3_div(Vec3 v, double c); -double vec3_length(vec3 v); -double vec3_length2(vec3 v); +double vec3_length(Vec3 v); +double vec3_length2(Vec3 v); -vec3 vec3_normalize(vec3 v); +Vec3 vec3_normalize(Vec3 v); -double vec3_dot(vec3 v1, vec3 v2); -vec3 vec3_cross(vec3 v1, vec3 v2); +double vec3_dot(Vec3 v1, Vec3 v2); +Vec3 vec3_cross(Vec3 v1, Vec3 v2); #endif /* INCLUDED_VEC3_H */