From 1034668c66e8c268a5e071480cf1e641ede31e27 Mon Sep 17 00:00:00 2001 From: Jean-Michel Gorius Date: Sat, 12 Nov 2022 16:46:13 +0100 Subject: [PATCH] Add moving spheres --- build.sh | 2 +- camera.c | 11 ++++++++--- camera.h | 7 ++++--- hittable.c | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ hittable.h | 16 ++++++++++++++++ main.c | 15 +++++++++------ material.c | 5 +++-- ray.h | 1 + 8 files changed, 95 insertions(+), 15 deletions(-) diff --git a/build.sh b/build.sh index 9b7ac8d..4770e8a 100755 --- a/build.sh +++ b/build.sh @@ -1,7 +1,7 @@ #!/bin/bash CC=${CC:-"gcc"} -CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -fno-strict-aliasing -g"} +CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -fno-strict-aliasing -O3"} LDFLAGS=${LDFLAGS:-"-lm"} MAIN_FILE=${MAIN_FILE:-"main.c"} diff --git a/camera.c b/camera.c index 9e3ec0b..c4ab9c1 100644 --- a/camera.c +++ b/camera.c @@ -7,7 +7,8 @@ void camera_init(Camera *camera, Point3 look_from, Point3 look_at, Vec3 up, double vertical_fov, double aspect_ratio, double aperture, - double focus_distance) { + double focus_distance, double shutter_open, + double shutter_close) { double theta = degrees_to_radians(vertical_fov); double h = tan(theta / 2.0); double viewport_height = 2.0 * h; @@ -26,6 +27,8 @@ void camera_init(Camera *camera, Point3 look_from, Point3 look_at, Vec3 up, camera->lower_left_corner = point3_add(camera->origin, vec3_neg(offset)); camera->lens_radius = aperture / 2.0; + camera->shutter_open = shutter_open; + camera->shutter_close = shutter_close; } Ray camera_get_ray(const Camera *camera, double s, double t) { @@ -37,7 +40,9 @@ Ray camera_get_ray(const Camera *camera, double s, double t) { vec3_add(vec3_mul(s, camera->horizontal), vec3_mul(t, camera->vertical))); Vec3 direction = point3_sub(screen_point, camera->origin); return (Ray){ - point3_add(camera->origin, offset), - vec3_sub(direction, offset), + .origin = point3_add(camera->origin, offset), + .direction = vec3_sub(direction, offset), + .time = + random_double_in_range(camera->shutter_open, camera->shutter_close), }; } diff --git a/camera.h b/camera.h index 0607f10..040bf86 100644 --- a/camera.h +++ b/camera.h @@ -8,15 +8,16 @@ typedef struct Camera { Point3 origin; Point3 lower_left_corner; - Vec3 horizontal; - Vec3 vertical; + Vec3 horizontal, vertical; Vec3 u, v, w; double lens_radius; + double shutter_open, shutter_close; } Camera; void camera_init(Camera *camera, Point3 look_from, Point3 look_at, Vec3 up, double vertical_fov, double aspect_ratio, double aperture, - double focus_distance); + double focus_distance, double shutter_open, + double shutter_close); Ray camera_get_ray(const Camera *camera, double s, double t); diff --git a/hittable.c b/hittable.c index 4ddc7cd..95cfc72 100644 --- a/hittable.c +++ b/hittable.c @@ -3,6 +3,7 @@ #include "point3.h" #include "vec3.h" +#include #include #include #include @@ -21,6 +22,9 @@ bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, record); case HITTABLE_SPHERE: return sphere_hit((const Sphere *)hittable, r, t_min, t_max, record); + case HITTABLE_MOVING_SPHERE: + return moving_sphere_hit((const MovingSphere *)hittable, r, t_min, t_max, + record); } return false; } @@ -97,3 +101,52 @@ bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, record->material = sphere->material; return true; } + +MovingSphere *moving_sphere_create(Point3 center_start, Point3 center_end, + double start, double end, double radius, + const Material *material, Arena *arena) { + assert(start <= end); + MovingSphere *sphere = arena_alloc(arena, sizeof(MovingSphere)); + sphere->type = HITTABLE_MOVING_SPHERE; + sphere->center_start = center_start; + sphere->center_end = center_end; + sphere->start = start; + sphere->end = end; + sphere->radius = radius; + sphere->material = material; + return sphere; +} + +Point3 moving_sphere_center(const MovingSphere *sphere, double t) { + Vec3 dir = point3_sub(sphere->center_end, sphere->center_start); + double c = (t - sphere->start) / (sphere->end - sphere->start); + return point3_add(sphere->center_start, vec3_mul(c, dir)); +} + +bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, + double t_max, HitRecord *record) { + Vec3 oc = point3_sub(r.origin, moving_sphere_center(sphere, r.time)); + 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, moving_sphere_center(sphere, r.time)), + sphere->radius); + hit_record_set_face_normal(record, r, outward_normal); + record->material = sphere->material; + return true; +} diff --git a/hittable.h b/hittable.h index bd64755..2e3bd9a 100644 --- a/hittable.h +++ b/hittable.h @@ -23,6 +23,7 @@ void hit_record_set_face_normal(HitRecord *record, Ray r, Vec3 outward_normal); typedef enum HittableType { HITTABLE_LIST, HITTABLE_SPHERE, + HITTABLE_MOVING_SPHERE, } HittableType; typedef struct Hittable { @@ -56,4 +57,19 @@ Sphere *sphere_create(Point3 center, double radius, const Material *material, bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max, HitRecord *record); +typedef struct MovingSphere { + HittableType type; + const Material *material; + Point3 center_start, center_end; + double start, end; + double radius; +} MovingSphere; + +MovingSphere *moving_sphere_create(Point3 center_start, Point3 center_end, + double start, double end, double radius, + const Material *material, Arena *arena); +Point3 moving_sphere_center(const MovingSphere *sphere, double t); +bool moving_sphere_hit(const MovingSphere *sphere, Ray r, double t_min, + double t_max, HitRecord *record); + #endif /* INCLUDED_HITTABLE_H */ diff --git a/main.c b/main.c index aa5bc99..61105ef 100644 --- a/main.c +++ b/main.c @@ -56,8 +56,11 @@ static const Hittable *generate_random_scene(Arena *arena) { if (choose_material < 0.8) { const Lambertian *material = lambertian_create( color_mul(color_random(), color_random()), arena); - const Sphere *sphere = - sphere_create(center, 0.2, (const Material *)material, arena); + Point3 center2 = point3_add( + center, (Vec3){0, random_double_in_range(0.0, 0.5), 0}); + const MovingSphere *sphere = + moving_sphere_create(center, center2, 0.0, 1.0, 0.2, + (const Material *)material, arena); hittable_list_add(&world, (const Hittable *)sphere, arena); } else if (choose_material < 0.95) { const Metal *material = @@ -104,10 +107,10 @@ int main(void) { /* Image parameters */ - const double aspect_ratio = 3.0 / 2.0; - const int image_width = 1200; + const double aspect_ratio = 16.0 / 9.0; + const int image_width = 400; const int image_height = (int)(image_width / aspect_ratio); - const int samples_per_pixel = 2; + const int samples_per_pixel = 100; const int max_depth = 50; /* World */ @@ -124,7 +127,7 @@ int main(void) { Camera camera; camera_init(&camera, look_from, look_at, up, 20, aspect_ratio, aperture, - dist_to_focus); + dist_to_focus, 0.0, 1.0); printf("P3\n%u %u\n255\n", image_width, image_height); diff --git a/material.c b/material.c index ec63ce6..4d3fda5 100644 --- a/material.c +++ b/material.c @@ -41,7 +41,7 @@ bool lambertian_scatter(const Lambertian *lambertian, Ray r, if (vec3_is_near_zero(scatter_direction)) scatter_direction = record->normal; - *scattered = (Ray){record->p, scatter_direction}; + *scattered = (Ray){record->p, scatter_direction, r.time}; *attenuation = lambertian->albedo; return true; } @@ -62,6 +62,7 @@ bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record, record->p, vec3_add(reflected, vec3_mul(metal->fuzziness, vec3_random_in_unit_sphere())), + r.time, }; *attenuation = metal->albedo; return vec3_dot(scattered->direction, record->normal) > 0; @@ -101,6 +102,6 @@ bool dielectric_scatter(const Dielectric *dielectric, Ray r, else direction = vec3_refract(unit_direction, record->normal, refraction_ratio); - *scattered = (Ray){record->p, direction}; + *scattered = (Ray){record->p, direction, r.time}; return true; } diff --git a/ray.h b/ray.h index 86d0ff5..8c3c6a7 100644 --- a/ray.h +++ b/ray.h @@ -7,6 +7,7 @@ typedef struct Ray { Point3 origin; Vec3 direction; + double time; } Ray; Point3 ray_at(Ray r, double t);