From ce45c57662237f8dcfe0352fa10be1bb57edb26b Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Tue, 23 Sep 2025 15:36:08 +0100 Subject: [PATCH] Create material abstraction --- CMakeLists.txt | 1 + demo.c | 7 ++++--- include/material.h | 40 ++++++++++++++++++++++++++++++++++++++++ include/obj.h | 11 ++++------- include/vec3.h | 1 + src/camera.c | 29 +++++++++++++---------------- src/material.c | 12 ++++++++++++ src/vec3.c | 9 +++++++++ 8 files changed, 84 insertions(+), 26 deletions(-) create mode 100644 include/material.h create mode 100644 src/material.c diff --git a/CMakeLists.txt b/CMakeLists.txt index c1488c0..756b34b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ endmacro() add_library(batomorph src/camera.c src/ff.c + src/material.c src/obj.c src/rng.c src/vec3.c diff --git a/demo.c b/demo.c index 5ccf95c..fbb9dff 100644 --- a/demo.c +++ b/demo.c @@ -15,9 +15,10 @@ static const vec3_t camera_pos = { 0.0, 0.0, 0.0 }; static const obj_t scene[] = { - SPHERE(1.0, 0.0, -3.0, 1.0), - SPHERE(-2.0, -0.5, -5.0, 1.0), - SPHERE(0.0, -1001.0, 0.0, 1000.0), + SPHERE(1.0, 0.0, -3.0, 1.0, LAMBERTIAN(0.6, 0.2, 0.8)), + SPHERE(-0.8, 0.0, -3.8, 1.0, LAMBERTIAN(1.0, 1.0, 1.0)), + SPHERE(-1.0, -0.8, -2.6, 0.2, LAMBERTIAN(0.9, 0.6, 0.2)), + SPHERE(0.0, -1001.0, 0.0, 1000.0, LAMBERTIAN(0.3, 0.3, 0.3)), }; static pix_t pixbuf[W * H]; diff --git a/include/material.h b/include/material.h new file mode 100644 index 0000000..c14475e --- /dev/null +++ b/include/material.h @@ -0,0 +1,40 @@ +#ifndef MATERIAL_H +#define MATERIAL_H + +#include "ray.h" +#include "rng.h" + +#define LAMBERTIAN(r, g, b) \ + { \ + .scatter = scatter_lambertian, \ + .params = { .lambertian = { .albedo = { r, g, b } } }, \ + } + +typedef struct { + vec3_t point, normal; + double t; + bool front; +} hit_t; + +typedef struct { + vec3_t albedo; +} lambertian_params_t; + +typedef union { + lambertian_params_t lambertian; +} material_params_t; + +typedef bool scatter_fn_t( + material_params_t params, hit_t hit, rng_t *rng, ray_t *ray, + vec3_t *atten_out); + +typedef struct { + scatter_fn_t *scatter; + material_params_t params; +} material_t; + +bool scatter_lambertian( + material_params_t params, hit_t hit, rng_t *rng, ray_t *ray, + vec3_t *atten_out); + +#endif diff --git a/include/obj.h b/include/obj.h index f766634..71753cb 100644 --- a/include/obj.h +++ b/include/obj.h @@ -1,22 +1,18 @@ #ifndef OBJ_H #define OBJ_H +#include "material.h" #include "ray.h" -#define SPHERE(x, y, z, r) \ +#define SPHERE(x, y, z, r, m) \ { \ .intersect = intersect_sphere, \ + .material = m, \ .params = { \ .sphere = { .centre = { x, y, z }, .radius = r }, \ }, \ } -typedef struct { - vec3_t point, normal; - double t; - bool front; -} hit_t; - typedef struct { vec3_t centre; double radius; @@ -32,6 +28,7 @@ typedef bool intersect_fn_t( typedef struct { intersect_fn_t *intersect; + material_t material; obj_params_t params; } obj_t; diff --git a/include/vec3.h b/include/vec3.h index 5ef216b..d86d925 100644 --- a/include/vec3.h +++ b/include/vec3.h @@ -10,6 +10,7 @@ vec3_t vec3_add(vec3_t v, vec3_t u); vec3_t vec3_sub(vec3_t v, vec3_t u); double vec3_dot(vec3_t v, vec3_t u); vec3_t vec3_cross(vec3_t v, vec3_t u); +vec3_t vec3_hadamard(vec3_t v, vec3_t u); double vec3_len(vec3_t v); vec3_t vec3_unit(vec3_t v); diff --git a/src/camera.c b/src/camera.c index 1f10b1a..999df26 100644 --- a/src/camera.c +++ b/src/camera.c @@ -30,32 +30,29 @@ static const vec3_t lightblue = { 0.4, 0.6, 1.0 }; static const vec3_t white = { 1.0, 1.0, 1.0 }; static const vec3_t black = { 0.0, 0.0, 0.0 }; -static ray_t scatter(hit_t hit, rng_t *rng) -{ - return (ray_t) { - .orig = hit.point, - .dir = vec3_add(hit.normal, rng_vec3(rng)), - }; -} - static vec3_t trace(ray_t ray, const obj_t *scene, unsigned scene_count, rng_t *rng) { - double coeff = 1.0; + vec3_t colour = white; for (unsigned i = 0; i < MAX_ITER; ++i) { hit_t hit = { .t = DBL_MAX }; - for (unsigned j = 0; j < scene_count; ++j) - scene[j].intersect(scene[j].params, ray, &hit, MIN_T, hit.t); + material_t material = {}; + for (unsigned j = 0; j < scene_count; ++j) { + if (scene[j].intersect(scene[j].params, ray, &hit, MIN_T, hit.t)) + material = scene[j].material; + } if (hit.t == DBL_MAX) { const double a = (ray.dir.y + 1.0) / 2.0; - return vec3_scale( - vec3_add(vec3_scale(lightblue, a), vec3_scale(white, 1 - a)), - coeff); + const vec3_t bg = vec3_add( + vec3_scale(lightblue, a), vec3_scale(white, 1 - a)); + return vec3_hadamard(colour, bg); } - ray = scatter(hit, rng); - coeff *= 0.3; + vec3_t atten; + if (!material.scatter(material.params, hit, rng, &ray, &atten)) + break; + colour = vec3_hadamard(colour, atten); } return black; diff --git a/src/material.c b/src/material.c new file mode 100644 index 0000000..d66a441 --- /dev/null +++ b/src/material.c @@ -0,0 +1,12 @@ +#include "material.h" + +bool scatter_lambertian( + material_params_t params, hit_t hit, rng_t *rng, ray_t *ray, + vec3_t *atten_out) +{ + const vec3_t dir = vec3_unit(vec3_add(hit.normal, rng_vec3(rng))); + ray->orig = hit.point; + ray->dir = vec3_len(dir) == 0 ? hit.normal : dir; + *atten_out = params.lambertian.albedo; + return true; +} diff --git a/src/vec3.c b/src/vec3.c index 5dd2e80..84abf94 100644 --- a/src/vec3.c +++ b/src/vec3.c @@ -40,6 +40,15 @@ vec3_t vec3_cross(vec3_t v, vec3_t u) }; } +vec3_t vec3_hadamard(vec3_t v, vec3_t u) +{ + return (vec3_t) { + .x = v.x * u.x, + .y = v.y * u.y, + .z = v.z * u.z, + }; +} + double vec3_len(vec3_t v) { return sqrt(v.x * v.x + v.y * v.y + v.z * v.z);