From 09d072a658d9043e407c0585fe77619672614036 Mon Sep 17 00:00:00 2001 From: milner Date: Sat, 2 Jul 2022 02:41:00 +0000 Subject: [PATCH] feat(examples): add the scene representation, camera and intersection Sphere and plane records, camera ray generation from field of view and aspect ratio, and the nearest-hit search across the scene. --- examples/raytracer/include/rt_scene.h | 67 +++++++++++++++++++++++++++ examples/raytracer/src/scene/camera.c | 27 +++++++++++ examples/raytracer/src/scene/hit.c | 43 +++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100644 examples/raytracer/include/rt_scene.h create mode 100644 examples/raytracer/src/scene/camera.c create mode 100644 examples/raytracer/src/scene/hit.c diff --git a/examples/raytracer/include/rt_scene.h b/examples/raytracer/include/rt_scene.h new file mode 100644 index 0000000..a269b9d --- /dev/null +++ b/examples/raytracer/include/rt_scene.h @@ -0,0 +1,67 @@ +#ifndef RT_SCENE_H +#define RT_SCENE_H + +#include "rt.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef struct { + vec3 origin, dir; +} ray; + +typedef enum { MAT_LAMBERT = 0, MAT_METAL = 1, MAT_LIGHT = 2 } mat_kind; + +typedef struct { + mat_kind kind; + vec3 albedo; + double fuzz; +} material; + +typedef struct { + vec3 center; + double radius; + material mat; +} sphere; + +typedef struct { + const sphere *spheres; + int count; + vec3 background; +} scene; + +typedef struct { + vec3 origin, lower_left, horizontal, vertical; +} camera; + +typedef struct { + double t; + vec3 point, normal; + material mat; + int front; +} hit; + +camera camera_make(vec3 look_from, vec3 look_at, vec3 up, double fov_deg, double aspect); +ray camera_ray(const camera *c, double u, double v); + +int scene_hit(const scene *s, ray r, double tmin, double tmax, hit *out); +vec3 ray_colour(const scene *s, ray r, int depth, rng *rng_state); + +typedef struct { + const scene *sc; + const camera *cam; + unsigned char *rgb; + int width, height, samples, max_depth; + int row_next; + int threads; +} render_job; + +/* Returns the number of worker threads actually used. */ +int render_run(render_job *job); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/examples/raytracer/src/scene/camera.c b/examples/raytracer/src/scene/camera.c new file mode 100644 index 0000000..c66c0f7 --- /dev/null +++ b/examples/raytracer/src/scene/camera.c @@ -0,0 +1,27 @@ +#include "rt_scene.h" +#include + +camera camera_make(vec3 look_from, vec3 look_at, vec3 up, double fov_deg, double aspect) { + double theta = fov_deg * 3.14159265358979323846 / 180.0; + double half_height = tan(theta / 2.0); + double half_width = aspect * half_height; + + vec3 w = v3norm(v3sub(look_from, look_at)); + vec3 u = v3norm(v3cross(up, w)); + vec3 v = v3cross(w, u); + + camera c; + c.origin = look_from; + c.horizontal = v3scale(u, 2.0 * half_width); + c.vertical = v3scale(v, 2.0 * half_height); + c.lower_left = v3sub(v3sub(v3sub(look_from, v3scale(u, half_width)), v3scale(v, half_height)), w); + return c; +} + +ray camera_ray(const camera *c, double u, double v) { + vec3 target = v3add(v3add(c->lower_left, v3scale(c->horizontal, u)), v3scale(c->vertical, v)); + ray r; + r.origin = c->origin; + r.dir = v3norm(v3sub(target, c->origin)); + return r; +} diff --git a/examples/raytracer/src/scene/hit.c b/examples/raytracer/src/scene/hit.c new file mode 100644 index 0000000..0e1d7e2 --- /dev/null +++ b/examples/raytracer/src/scene/hit.c @@ -0,0 +1,43 @@ +#include "rt_scene.h" +#include + +static int sphere_hit(const sphere *s, ray r, double tmin, double tmax, hit *out) { + vec3 oc = v3sub(r.origin, s->center); + double a = v3len2(r.dir); + double half_b = v3dot(oc, r.dir); + double c = v3len2(oc) - s->radius * s->radius; + double disc = half_b * half_b - a * c; + if (disc < 0.0) return 0; + + double root = sqrt(disc); + double t = (-half_b - root) / a; + if (t < tmin || t > tmax) { + t = (-half_b + root) / a; + if (t < tmin || t > tmax) return 0; + } + + out->t = t; + out->point = v3add(r.origin, v3scale(r.dir, t)); + vec3 outward = v3scale(v3sub(out->point, s->center), 1.0 / s->radius); + out->front = v3dot(r.dir, outward) < 0.0; + out->normal = out->front ? outward : v3neg(outward); + out->mat = s->mat; + return 1; +} + +int scene_hit(const scene *s, ray r, double tmin, double tmax, hit *out) { + hit best; + int found = 0; + double closest = tmax; + + for (int i = 0; i < s->count; i++) { + hit h; + if (sphere_hit(&s->spheres[i], r, tmin, closest, &h)) { + found = 1; + closest = h.t; + best = h; + } + } + if (found) *out = best; + return found; +}