Add moving spheres

This commit is contained in:
Jean-Michel Gorius 2022-11-12 16:46:13 +01:00
parent 2abc2ba697
commit 1034668c66
8 changed files with 95 additions and 15 deletions

View File

@ -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"}

View File

@ -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),
};
}

View File

@ -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);

View File

@ -3,6 +3,7 @@
#include "point3.h"
#include "vec3.h"
#include <assert.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
@ -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;
}

View File

@ -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 */

15
main.c
View File

@ -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);

View File

@ -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;
}

1
ray.h
View File

@ -7,6 +7,7 @@
typedef struct Ray {
Point3 origin;
Vec3 direction;
double time;
} Ray;
Point3 ray_at(Ray r, double t);