Add hittable objects

This commit is contained in:
Jean-Michel Gorius 2022-11-10 23:09:05 +01:00
parent 6352cbf0fd
commit ec322b7c16
14 changed files with 250 additions and 78 deletions

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
CC=${CC:-"gcc"} CC=${CC:-"gcc"}
CFLAGS=${CFLAGS:-"-Wall -Wextra -std=c18 -g"} CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -g -fsanitize=address"}
LDFLAGS=${LDFLAGS:-"-lm"} LDFLAGS=${LDFLAGS:-"-lm"}
MAIN_FILE=${MAIN_FILE:-"main.c"} MAIN_FILE=${MAIN_FILE:-"main.c"}

View File

@ -1,15 +1,15 @@
#include "color.h" #include "color.h"
#include <stdio.h> #include <stdio.h>
color color_lerp(color c1, color c2, double t) { Color color_lerp(Color c1, Color c2, double t) {
return (color){ return (Color){
(1.0 - t) * c1.r + t * c2.r, (1.0 - t) * c1.r + t * c2.r,
(1.0 - t) * c1.g + t * c2.g, (1.0 - t) * c1.g + t * c2.g,
(1.0 - t) * c1.b + t * c2.b, (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), fprintf(out, "%d %d %d\n", (int)(255.999 * c.r), (int)(255.999 * c.g),
(int)(255.999 * c.b)); (int)(255.999 * c.b));
} }

View File

@ -3,12 +3,12 @@
#include <stdio.h> #include <stdio.h>
typedef struct color { typedef struct Color {
double r, g, b; 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 */ #endif /* INCLUDED_COLOR_H */

98
hittable.c Normal file
View File

@ -0,0 +1,98 @@
#include "hittable.h"
#include "point3.h"
#include "vec3.h"
#include <math.h>
#include <stdlib.h>
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;
}

56
hittable.h Normal file
View File

@ -0,0 +1,56 @@
#ifndef INCLUDED_HITTABLE_H
#define INCLUDED_HITTABLE_H
#include "point3.h"
#include "ray.h"
#include "vec3.h"
#include <stdbool.h>
#include <stddef.h>
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 */

67
main.c
View File

@ -1,34 +1,24 @@
#include <float.h>
#include <math.h> #include <math.h>
#include <stdbool.h> #include <stdbool.h>
#include <stdio.h> #include <stdio.h>
#include "color.h" #include "color.h"
#include "hittable.h"
#include "point3.h" #include "point3.h"
#include "ray.h" #include "ray.h"
#include "vec3.h" #include "vec3.h"
double hit_sphere(point3 center, double radius, ray r) { Color ray_color(Ray r, Hittable world) {
vec3 oc = point3_sub(r.origin, center); HitRecord record;
double a = vec3_length2(r.direction); if (hittable_hit(&world, r, 0, DBL_MAX, &record)) {
double half_b = vec3_dot(oc, r.direction); return (Color){0.5 * (record.normal.x + 1), 0.5 * (record.normal.y + 1),
double c = vec3_length2(oc) - radius * radius; 0.5 * (record.normal.z + 1)};
double discriminant = half_b * half_b - a * c;
if (discriminant < 0)
return -1.0;
else
return (-half_b - sqrt(discriminant)) / a;
} }
Vec3 unit_direction = vec3_normalize(r.direction);
color ray_color(ray r) { double t = 0.5 * (unit_direction.y + 1.0);
double t = hit_sphere((point3){0, 0, -1}, 0.5, r); Color gradient1 = {1.0, 1.0, 1.0};
if (t > 0.0) { Color gradient2 = {0.5, 0.7, 1.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)};
}
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};
return color_lerp(gradient1, gradient2, t); return color_lerp(gradient1, gradient2, t);
} }
@ -38,17 +28,28 @@ int main(void) {
const int image_width = 256; const int image_width = 256;
const int image_height = (int)(image_width / aspect_ratio); 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 */ /* Camera parameters */
double viewport_height = 2; double viewport_height = 2;
double viewport_width = aspect_ratio * viewport_height; double viewport_width = aspect_ratio * viewport_height;
double focal_length = 1.0; double focal_length = 1.0;
point3 origin = {0}; Point3 origin = {0};
vec3 horizontal = {viewport_width, 0, 0}; Vec3 horizontal = {viewport_width, 0, 0};
vec3 vertical = {0, viewport_height, 0}; Vec3 vertical = {0, viewport_height, 0};
vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2)); Vec3 offset = vec3_add(vec3_div(horizontal, 2), vec3_div(vertical, 2));
offset = vec3_add(offset, (vec3){0, 0, focal_length}); offset = vec3_add(offset, (Vec3){0, 0, focal_length});
point3 lower_left_corner = point3_add(origin, vec3_neg(offset)); Point3 lower_left_corner = point3_add(origin, vec3_neg(offset));
printf("P3\n%u %u\n255\n", image_width, image_height); 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) { for (int i = 0; i < image_width; ++i) {
double u = (double)i / (image_width - 1); double u = (double)i / (image_width - 1);
double v = (double)j / (image_height - 1); double v = (double)j / (image_height - 1);
point3 screen_point = Point3 screen_point =
point3_add(lower_left_corner, point3_add(lower_left_corner,
vec3_add(vec3_mul(u, horizontal), vec3_mul(v, vertical))); vec3_add(vec3_mul(u, horizontal), vec3_mul(v, vertical)));
vec3 direction = point3_sub(screen_point, origin); Vec3 direction = point3_sub(screen_point, origin);
ray r = {origin, direction}; Ray r = {origin, direction};
color pixel_color = ray_color(r); Color pixel_color = ray_color(r, make_hittable_list(&world));
color_write(stdout, pixel_color); color_write(stdout, pixel_color);
} }
} }
fprintf(stderr, "\nDone.\n"); fprintf(stderr, "\nDone.\n");
hittable_list_free(&world);
return 0; return 0;
} }
#include "color.c" #include "color.c"
#include "hittable.c"
#include "point3.c" #include "point3.c"
#include "ray.c" #include "ray.c"
#include "utils.c"
#include "vec3.c" #include "vec3.c"

View File

@ -1,9 +1,9 @@
#include "point3.h" #include "point3.h"
point3 point3_add(point3 p, vec3 v) { Point3 point3_add(Point3 p, Vec3 v) {
return (point3){p.x + v.x, p.y + v.y, p.z + v.z}; return (Point3){p.x + v.x, p.y + v.y, p.z + v.z};
} }
vec3 point3_sub(point3 p1, point3 p2) { Vec3 point3_sub(Point3 p1, Point3 p2) {
return (vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z}; return (Vec3){p1.x - p2.x, p1.y - p2.y, p1.z - p2.z};
} }

View File

@ -3,11 +3,11 @@
#include "vec3.h" #include "vec3.h"
typedef struct point3 { typedef struct Point3 {
double x, y, z; double x, y, z;
} point3; } Point3;
point3 point3_add(point3 p, vec3 v); Point3 point3_add(Point3 p, Vec3 v);
vec3 point3_sub(point3 p1, point3 p2); Vec3 point3_sub(Point3 p1, Point3 p2);
#endif /* INCLUDED_POINT3_H */ #endif /* INCLUDED_POINT3_H */

2
ray.c
View File

@ -1,6 +1,6 @@
#include "ray.h" #include "ray.h"
#include "point3.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)); return point3_add(r.origin, vec3_mul(t, r.direction));
} }

10
ray.h
View File

@ -4,11 +4,11 @@
#include "point3.h" #include "point3.h"
#include "vec3.h" #include "vec3.h"
typedef struct ray { typedef struct Ray {
point3 origin; Point3 origin;
vec3 direction; Vec3 direction;
} ray; } Ray;
point3 ray_at(ray r, double t); Point3 ray_at(Ray r, double t);
#endif /* INCLUDED_RAY_H */ #endif /* INCLUDED_RAY_H */

7
utils.c Normal file
View File

@ -0,0 +1,7 @@
#include "utils.h"
#include <math.h>
double degrees_to_radians(double degrees) {
return degrees * M_PI / 180.0;
}

6
utils.h Normal file
View File

@ -0,0 +1,6 @@
#ifndef INCLUDED_UTILS_H
#define INCLUDED_UTILS_H
double degrees_to_radians(double degrees);
#endif /* INCLUDED_UTILS_H */

26
vec3.c
View File

@ -2,32 +2,32 @@
#include <math.h> #include <math.h>
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) { Vec3 vec3_add(Vec3 v1, Vec3 v2) {
return (vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z}; return (Vec3){v1.x + v2.x, v1.y + v2.y, v1.z + v2.z};
} }
vec3 vec3_sub(vec3 v1, vec3 v2) { Vec3 vec3_sub(Vec3 v1, Vec3 v2) {
return (vec3){v1.x - v2.x, v1.y - v2.y, v1.z - v2.z}; 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; return v1.x * v2.x + v1.y * v2.y + v1.z * v2.z;
} }
vec3 vec3_cross(vec3 v1, vec3 v2) { Vec3 vec3_cross(Vec3 v1, Vec3 v2) {
return (vec3){ return (Vec3){
v1.y * v2.z - v1.z * v2.y, v1.y * v2.z - v1.z * v2.y,
v1.z * v2.x - v1.x * v2.z, v1.z * v2.x - v1.x * v2.z,
v1.x * v2.y - v1.y * v2.x, v1.x * v2.y - v1.y * v2.x,

24
vec3.h
View File

@ -1,22 +1,22 @@
#ifndef INCLUDED_VEC3_H #ifndef INCLUDED_VEC3_H
#define INCLUDED_VEC3_H #define INCLUDED_VEC3_H
typedef struct vec3 { typedef struct Vec3 {
double x, y, z; double x, y, z;
} vec3; } Vec3;
vec3 vec3_neg(vec3 v); Vec3 vec3_neg(Vec3 v);
vec3 vec3_add(vec3 v1, vec3 v2); Vec3 vec3_add(Vec3 v1, Vec3 v2);
vec3 vec3_sub(vec3 v1, vec3 v2); Vec3 vec3_sub(Vec3 v1, Vec3 v2);
vec3 vec3_mul(double c, vec3 v); Vec3 vec3_mul(double c, Vec3 v);
vec3 vec3_div(vec3 v, double c); Vec3 vec3_div(Vec3 v, double c);
double vec3_length(vec3 v); double vec3_length(Vec3 v);
double vec3_length2(vec3 v); double vec3_length2(Vec3 v);
vec3 vec3_normalize(vec3 v); Vec3 vec3_normalize(Vec3 v);
double vec3_dot(vec3 v1, vec3 v2); double vec3_dot(Vec3 v1, Vec3 v2);
vec3 vec3_cross(vec3 v1, vec3 v2); Vec3 vec3_cross(Vec3 v1, Vec3 v2);
#endif /* INCLUDED_VEC3_H */ #endif /* INCLUDED_VEC3_H */