Reduce the public API of Hittable and Material
This commit is contained in:
parent
391aa6240e
commit
8db2d5aa2c
2
build.sh
2
build.sh
@ -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"}
|
||||||
|
|
||||||
|
|||||||
306
hittable.c
306
hittable.c
@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
20
hittable.h
20
hittable.h
@ -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
4
main.c
@ -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 */
|
||||||
|
|||||||
123
material.c
123
material.c
@ -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;
|
|
||||||
}
|
|
||||||
|
|||||||
10
material.h
10
material.h
@ -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 */
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user