Reduce the public API of Hittable and Material

This commit is contained in:
Jean-Michel Gorius 2022-11-13 16:42:05 +01:00
parent 391aa6240e
commit 8db2d5aa2c
6 changed files with 219 additions and 246 deletions

View File

@ -1,7 +1,7 @@
#!/bin/bash #!/bin/bash
CC=${CC:-"gcc"} CC=${CC:-"gcc"}
CFLAGS=${CFLAGS:-"-Wall -Wextra -std=gnu18 -fno-strict-aliasing -O3"} CFLAGS=${CFLAGS:-"-Wall -Wextra -Wno-unused-function -std=gnu18 -fno-strict-aliasing -O3"}
LDFLAGS=${LDFLAGS:-"-lm"} LDFLAGS=${LDFLAGS:-"-lm"}
MAIN_FILE=${MAIN_FILE:-"main.c"} MAIN_FILE=${MAIN_FILE:-"main.c"}

View File

@ -101,6 +101,99 @@ Hittable *hittable_create_bvh_node(const Hittable **objects, size_t start,
return result; return result;
} }
static 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;
}
static void get_sphere_uv(Point3 p, double *u, double *v) {
double theta = acos(-p.y);
double phi = atan2(-p.z, p.x) + M_PI;
*u = phi / (2 * M_PI);
*v = theta / M_PI;
}
static 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);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}
static 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);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}
static bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max,
HitRecord *record) {
if (!aabb_hit(&node->box, r, t_min, t_max))
return false;
bool hit_left = hittable_hit(node->left, r, t_min, t_max, record);
bool hit_right =
hittable_hit(node->right, r, t_min, hit_left ? record->t : t_max, record);
return hit_left || hit_right;
}
bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max, bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
HitRecord *record) { HitRecord *record) {
switch (hittable->type) { switch (hittable->type) {
@ -116,6 +209,67 @@ bool hittable_hit(const Hittable *hittable, Ray r, double t_min, double t_max,
return false; return false;
} }
static bool hittable_list_bounding_box(const HittableList *list,
double time_start, double time_end,
AABB *bounding_box) {
if (list->size == 0)
return false;
AABB temp_box;
bool first_box = true;
for (size_t i = 0; i < list->size; ++i) {
if (!hittable_bounding_box(list->objects[i], time_start, time_end,
&temp_box))
return false;
*bounding_box =
first_box ? temp_box : aabb_surrounding_box(bounding_box, &temp_box);
first_box = false;
}
return true;
}
static bool sphere_bounding_box(const Sphere *sphere, double time_start,
double time_end, AABB *bounding_box) {
(void)time_start, (void)time_end;
*bounding_box = (AABB){
.min = point3_add(sphere->center, (Vec3){-sphere->radius, -sphere->radius,
-sphere->radius}),
.max = point3_add(sphere->center,
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
return true;
}
static bool moving_sphere_bounding_box(const MovingSphere *sphere,
double time_start, double time_end,
AABB *bounding_box) {
AABB box_start = {
.min =
point3_add(moving_sphere_center(sphere, time_start),
(Vec3){-sphere->radius, -sphere->radius, -sphere->radius}),
.max = point3_add(moving_sphere_center(sphere, time_start),
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
AABB box_end = {
.min =
point3_add(moving_sphere_center(sphere, time_end),
(Vec3){-sphere->radius, -sphere->radius, -sphere->radius}),
.max = point3_add(moving_sphere_center(sphere, time_end),
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
*bounding_box = aabb_surrounding_box(&box_start, &box_end);
return true;
}
static bool bvh_node_bounding_box(const BVHNode *node, double time_start,
double time_end, AABB *bounding_box) {
(void)time_start, (void)time_end;
*bounding_box = node->box;
return true;
}
bool hittable_bounding_box(const Hittable *hittable, double time_start, bool hittable_bounding_box(const Hittable *hittable, double time_start,
double time_end, AABB *bounding_box) { double time_end, AABB *bounding_box) {
switch (hittable->type) { switch (hittable->type) {
@ -156,89 +310,6 @@ void hittable_list_add(HittableList *list, const Hittable *hittable,
list->objects[list->size++] = hittable; 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;
}
bool hittable_list_bounding_box(const HittableList *list, double time_start,
double time_end, AABB *bounding_box) {
if (list->size == 0)
return false;
AABB temp_box;
bool first_box = true;
for (size_t i = 0; i < list->size; ++i) {
if (!hittable_bounding_box(list->objects[i], time_start, time_end,
&temp_box))
return false;
*bounding_box =
first_box ? temp_box : aabb_surrounding_box(bounding_box, &temp_box);
first_box = false;
}
return true;
}
static void get_sphere_uv(Point3 p, double *u, double *v) {
double theta = acos(-p.y);
double phi = atan2(-p.z, p.x) + M_PI;
*u = phi / (2 * M_PI);
*v = theta / M_PI;
}
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);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}
bool sphere_bounding_box(const Sphere *sphere, double time_start,
double time_end, AABB *bounding_box) {
(void)time_start, (void)time_end;
*bounding_box = (AABB){
.min = point3_add(sphere->center, (Vec3){-sphere->radius, -sphere->radius,
-sphere->radius}),
.max = point3_add(sphere->center,
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
return true;
}
Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end, Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end,
double start, double end, double radius, double start, double end, double radius,
const Material *material, const Material *material,
@ -254,72 +325,3 @@ Hittable *hittable_create_moving_sphere(Point3 center_start, Point3 center_end,
result->moving_sphere.material = material; result->moving_sphere.material = material;
return result; return result;
} }
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);
get_sphere_uv((Point3){outward_normal.x, outward_normal.y, outward_normal.z},
&record->u, &record->v);
record->material = sphere->material;
return true;
}
bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start,
double time_end, AABB *bounding_box) {
AABB box_start = {
.min =
point3_add(moving_sphere_center(sphere, time_start),
(Vec3){-sphere->radius, -sphere->radius, -sphere->radius}),
.max = point3_add(moving_sphere_center(sphere, time_start),
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
AABB box_end = {
.min =
point3_add(moving_sphere_center(sphere, time_end),
(Vec3){-sphere->radius, -sphere->radius, -sphere->radius}),
.max = point3_add(moving_sphere_center(sphere, time_end),
(Vec3){sphere->radius, sphere->radius, sphere->radius}),
};
*bounding_box = aabb_surrounding_box(&box_start, &box_end);
return true;
}
bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max,
HitRecord *record) {
if (!aabb_hit(&node->box, r, t_min, t_max))
return false;
bool hit_left = hittable_hit(node->left, r, t_min, t_max, record);
bool hit_right =
hittable_hit(node->right, r, t_min, hit_left ? record->t : t_max, record);
return hit_left || hit_right;
}
bool bvh_node_bounding_box(const BVHNode *node, double time_start,
double time_end, AABB *bounding_box) {
(void)time_start, (void)time_end;
*bounding_box = node->box;
return true;
}

View File

@ -83,25 +83,5 @@ bool hittable_bounding_box(const Hittable *hittable, double time_start,
void hittable_list_add(HittableList *list, const Hittable *hittable, void hittable_list_add(HittableList *list, const Hittable *hittable,
Arena *arena); Arena *arena);
bool hittable_list_hit(const HittableList *list, Ray r, double t_min,
double t_max, HitRecord *record);
bool hittable_list_bounding_box(const HittableList *list, double time_start,
double time_end, AABB *bounding_box);
bool sphere_hit(const Sphere *sphere, Ray r, double t_min, double t_max,
HitRecord *record);
bool sphere_bounding_box(const Sphere *sphere, double time_start,
double time_end, AABB *bounding_box);
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);
bool moving_sphere_bounding_box(const MovingSphere *sphere, double time_start,
double time_end, AABB *bounding_box);
bool bvh_node_hit(const BVHNode *node, Ray r, double t_min, double t_max,
HitRecord *record);
bool bvh_node_bounding_box(const BVHNode *node, double time_start,
double time_end, AABB *bounding_box);
#endif /* INCLUDED_HITTABLE_H */ #endif /* INCLUDED_HITTABLE_H */

4
main.c
View File

@ -167,9 +167,9 @@ int main(void) {
/* Image parameters */ /* Image parameters */
const double aspect_ratio = 16.0 / 9.0; const double aspect_ratio = 16.0 / 9.0;
const int image_width = 1200; const int image_width = 400;
const int image_height = (int)(image_width / aspect_ratio); const int image_height = (int)(image_width / aspect_ratio);
const int samples_per_pixel = 500; const int samples_per_pixel = 100;
const int max_depth = 50; const int max_depth = 50;
/* World */ /* World */

View File

@ -7,6 +7,68 @@
#include <assert.h> #include <assert.h>
#include <math.h> #include <math.h>
static bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const HitRecord *record, Color *attenuation,
Ray *scattered) {
(void)r;
Vec3 scatter_direction = vec3_add(record->normal, vec3_random_unit_vector());
/* Catch degenerate scatter direction */
if (vec3_is_near_zero(scatter_direction))
scatter_direction = record->normal;
*scattered = (Ray){record->p, scatter_direction, r.time};
*attenuation =
texture_value(lambertian->albedo, record->u, record->v, record->p);
return true;
}
static bool metal_scatter(const Metal *metal, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered) {
Vec3 reflected = vec3_reflect(vec3_normalize(r.direction), record->normal);
assert(metal->fuzziness <= 1);
*scattered = (Ray){
record->p,
vec3_add(reflected,
vec3_mul(metal->fuzziness, vec3_random_in_unit_sphere())),
r.time,
};
*attenuation = texture_value(metal->albedo, record->u, record->v, record->p);
return vec3_dot(scattered->direction, record->normal) > 0;
}
static double schlick_reflectance(double cosine, double eta) {
double r0 = (1 - eta) / (1 + eta);
r0 *= r0;
return r0 + (1 - r0) * pow(1 - cosine, 5);
}
static bool dielectric_scatter(const Dielectric *dielectric, Ray r,
const struct HitRecord *record,
Color *attenuation, Ray *scattered) {
*attenuation = (Color){1.0, 1.0, 1.0};
double refraction_ratio =
record->front_face ? (1.0 / dielectric->eta) : dielectric->eta;
Vec3 unit_direction = vec3_normalize(r.direction);
double cos_theta =
fmin(vec3_dot(vec3_neg(unit_direction), record->normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta * cos_theta);
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
Vec3 direction;
if (cannot_refract ||
schlick_reflectance(cos_theta, refraction_ratio) > random_double())
direction = vec3_reflect(unit_direction, record->normal);
else
direction = vec3_refract(unit_direction, record->normal, refraction_ratio);
*scattered = (Ray){record->p, direction, r.time};
return true;
}
bool material_scatter(const Material *material, Ray r, bool material_scatter(const Material *material, Ray r,
const struct HitRecord *record, Color *attenuation, const struct HitRecord *record, Color *attenuation,
Ray *scattered) { Ray *scattered) {
@ -35,22 +97,6 @@ Material *material_create_lambertian_color(Color albedo, Arena *arena) {
arena); arena);
} }
bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const HitRecord *record, Color *attenuation,
Ray *scattered) {
(void)r;
Vec3 scatter_direction = vec3_add(record->normal, vec3_random_unit_vector());
/* Catch degenerate scatter direction */
if (vec3_is_near_zero(scatter_direction))
scatter_direction = record->normal;
*scattered = (Ray){record->p, scatter_direction, r.time};
*attenuation =
texture_value(lambertian->albedo, record->u, record->v, record->p);
return true;
}
Material *material_create_metal(const Texture *albedo, double fuzziness, Material *material_create_metal(const Texture *albedo, double fuzziness,
Arena *arena) { Arena *arena) {
Material *result = arena_alloc(arena, sizeof(Material)); Material *result = arena_alloc(arena, sizeof(Material));
@ -66,54 +112,9 @@ Material *material_create_metal_color(Color albedo, double fuzziness,
fuzziness, arena); fuzziness, arena);
} }
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
Color *attenuation, Ray *scattered) {
Vec3 reflected = vec3_reflect(vec3_normalize(r.direction), record->normal);
assert(metal->fuzziness <= 1);
*scattered = (Ray){
record->p,
vec3_add(reflected,
vec3_mul(metal->fuzziness, vec3_random_in_unit_sphere())),
r.time,
};
*attenuation = texture_value(metal->albedo, record->u, record->v, record->p);
return vec3_dot(scattered->direction, record->normal) > 0;
}
Material *material_create_dielectric(double eta, Arena *arena) { Material *material_create_dielectric(double eta, Arena *arena) {
Material *result = arena_alloc(arena, sizeof(Material)); Material *result = arena_alloc(arena, sizeof(Material));
result->type = MATERIAL_DIELECTRIC; result->type = MATERIAL_DIELECTRIC;
result->dielectric.eta = eta; result->dielectric.eta = eta;
return result; return result;
} }
static double schlick_reflectance(double cosine, double eta) {
double r0 = (1 - eta) / (1 + eta);
r0 *= r0;
return r0 + (1 - r0) * pow(1 - cosine, 5);
}
bool dielectric_scatter(const Dielectric *dielectric, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered) {
*attenuation = (Color){1.0, 1.0, 1.0};
double refraction_ratio =
record->front_face ? (1.0 / dielectric->eta) : dielectric->eta;
Vec3 unit_direction = vec3_normalize(r.direction);
double cos_theta =
fmin(vec3_dot(vec3_neg(unit_direction), record->normal), 1.0);
double sin_theta = sqrt(1.0 - cos_theta * cos_theta);
bool cannot_refract = refraction_ratio * sin_theta > 1.0;
Vec3 direction;
if (cannot_refract ||
schlick_reflectance(cos_theta, refraction_ratio) > random_double())
direction = vec3_reflect(unit_direction, record->normal);
else
direction = vec3_refract(unit_direction, record->normal, refraction_ratio);
*scattered = (Ray){record->p, direction, r.time};
return true;
}

View File

@ -43,20 +43,10 @@ bool material_scatter(const Material *material, Ray r,
Material *material_create_lambertian(const Texture *albedo, Arena *arena); Material *material_create_lambertian(const Texture *albedo, Arena *arena);
Material *material_create_lambertian_color(Color albedo, Arena *arena); Material *material_create_lambertian_color(Color albedo, Arena *arena);
bool lambertian_scatter(const Lambertian *lambertian, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered);
Material *material_create_metal(const Texture *albedo, double fuzziness, Material *material_create_metal(const Texture *albedo, double fuzziness,
Arena *arena); Arena *arena);
Material *material_create_metal_color(Color albedo, double fuzziness, Material *material_create_metal_color(Color albedo, double fuzziness,
Arena *arena); Arena *arena);
bool metal_scatter(const Metal *metal, Ray r, const struct HitRecord *record,
Color *attenuation, Ray *scattered);
Material *material_create_dielectric(double eta, Arena *arena); Material *material_create_dielectric(double eta, Arena *arena);
bool dielectric_scatter(const Dielectric *dielectric, Ray r,
const struct HitRecord *record, Color *attenuation,
Ray *scattered);
#endif /* INCLUDED_MATERIAL_H */ #endif /* INCLUDED_MATERIAL_H */