From fb09a5553ee4f4669a706a633738f9fcb5cf321c Mon Sep 17 00:00:00 2001 From: Camden Dixie O'Brien Date: Mon, 22 Sep 2025 20:16:41 +0100 Subject: [PATCH] Allow camera position and target position to vary --- demo.c | 24 +++++++++++++----------- include/camera.h | 4 ++-- src/camera.c | 29 +++++++++++++++++------------ 3 files changed, 32 insertions(+), 25 deletions(-) diff --git a/demo.c b/demo.c index 0dfd61e..f98666f 100644 --- a/demo.c +++ b/demo.c @@ -7,22 +7,23 @@ #define W 800 #define H 600 -#define FOV 120 +#define FOV 90 #define SAMPLES_PER_PIXEL 100 #define NELEMS(arr) (sizeof(arr) / sizeof(arr[0])) -static const vec3_t camera_pos = { 0.0, 0.0, 0.0 }; +static const vec3_t camera_pos = { 0.0, 1.5, -2.0 }; +static const vec3_t camera_target = { 0.0, 1.0, 1.0 }; static const obj_t scene[] = { - 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.2, 0.9, 0.3)), - SPHERE(0.3, -0.7, -1.8, 0.3, LAMBERTIAN(0.9, 0.6, 0.2)), - SPHERE(-1.6, -0.8, -2.0, 0.2, REFLECTIVE(1.0, 0.9, 0.4, 0.0)), - SPHERE(-6.0, 5.0, -5.0, 6.0, REFLECTIVE(0.9, 0.9, 0.9, 0.05)), - SPHERE(-0.7, -0.75, -1.5, 0.25, DIELECTRIC(1.5)), - SPHERE(0.0, -1001.0, 0.0, 1000.0, LAMBERTIAN(0.3, 0.3, 0.3)), + SPHERE(1.0, 1.0, 3.0, 1.0, LAMBERTIAN(0.6, 0.2, 0.8)), + SPHERE(-0.8, 1.0, 3.8, 1.0, LAMBERTIAN(1.0, 1.0, 1.0)), + SPHERE(-1.0, 0.2, 2.6, 0.2, LAMBERTIAN(0.2, 0.9, 0.3)), + SPHERE(0.3, 0.3, 1.8, 0.3, LAMBERTIAN(0.9, 0.6, 0.2)), + SPHERE(-1.6, 0.2, 2.0, 0.2, REFLECTIVE(1.0, 0.9, 0.4, 0.0)), + SPHERE(-6.0, 6.0, 5.0, 6.0, REFLECTIVE(0.9, 0.9, 0.9, 0.05)), + SPHERE(-0.7, 0.25, 1.5, 0.25, DIELECTRIC(1.5)), + SPHERE(0.0, -1000.0, 0.0, 1000.0, LAMBERTIAN(0.3, 0.3, 0.3)), }; static pix_t pixbuf[W * H]; @@ -30,7 +31,8 @@ static pix_t pixbuf[W * H]; int main() { img_t img = { .pix = pixbuf }; - camera_t camera = camera_init(camera_pos, FOV, W, H, SAMPLES_PER_PIXEL); + camera_t camera = camera_init( + camera_pos, camera_target, FOV, W, H, SAMPLES_PER_PIXEL); camera_render(&camera, scene, NELEMS(scene), &img); ff_write(STDOUT_FILENO, img); diff --git a/include/camera.h b/include/camera.h index 0bd5e8a..774c7d9 100644 --- a/include/camera.h +++ b/include/camera.h @@ -15,8 +15,8 @@ typedef struct { } camera_t; camera_t camera_init( - vec3_t pos, double fov, uint32_t img_width, uint32_t img_height, - unsigned samples_per_pixel); + vec3_t pos, vec3_t target, double fov, uint32_t img_width, + uint32_t img_height, unsigned samples_per_pixel); void camera_render( const camera_t *camera, const obj_t *scene, unsigned scene_count, diff --git a/src/camera.c b/src/camera.c index c099799..9156812 100644 --- a/src/camera.c +++ b/src/camera.c @@ -16,7 +16,6 @@ #define MAX_ITER 10 #define MIN_T 1e-6 #define SAMPLE_STDDEV 0.333 -#define FOCAL_LEN 1.0 #define GAMMA 2.2 @@ -31,6 +30,8 @@ typedef struct { atomic_uint *progress; } work_slice_t; +static const vec3_t up = { 0.0, 1.0, 0.0 }; + 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 }; @@ -97,10 +98,11 @@ static int render_thread(void *arg) const vec3_t offset = vec3_add( vec3_scale(camera->x_step, jitter.x), vec3_scale(camera->y_step, jitter.y)); + const vec3_t jittered_pix = vec3_add(pix, offset); const ray_t ray = { .orig = camera->pos, - .dir = vec3_unit(vec3_add(pix, offset)), + .dir = vec3_unit(vec3_sub(jittered_pix, camera->pos)), }; const vec3_t sample = trace( ray, slice->scene, slice->scene_count, &slice->rng); @@ -118,20 +120,23 @@ static int render_thread(void *arg) } camera_t camera_init( - vec3_t pos, double fov, uint32_t img_width, uint32_t img_height, - unsigned samples_per_pixel) + vec3_t pos, vec3_t target, double fov, uint32_t img_width, + uint32_t img_height, unsigned samples_per_pixel) { - const double aspect = (double)img_width / (double)img_height; - const double fov_rad = M_PI * fov / 180.0; - const double viewport_height = FOCAL_LEN * tan(fov_rad / 2); + const double aspect = (double)img_width / (double)img_height; + const double viewport_height = tan(fov_rad / 2); const double viewport_width = viewport_height * aspect; - const vec3_t viewport_disp = { 0, 0, FOCAL_LEN }; - const vec3_t u = { viewport_width, 0, 0 }; - const vec3_t v = { 0, -viewport_height, 0 }; - const vec3_t topleft = vec3_sub( - vec3_sub(pos, viewport_disp), vec3_scale(vec3_add(u, v), 0.5)); + const vec3_t w_hat = vec3_unit(vec3_sub(target, pos)); + const vec3_t u_hat = vec3_unit(vec3_cross(up, w_hat)); + const vec3_t v_hat = vec3_unit(vec3_cross(w_hat, u_hat)); + + const vec3_t u = vec3_scale(u_hat, viewport_width); + const vec3_t v = vec3_scale(v_hat, -viewport_height); + + const vec3_t topleft + = vec3_sub(vec3_add(pos, w_hat), vec3_scale(vec3_add(u, v), 0.5)); const vec3_t x_step = vec3_scale(u, 1.0 / (double)img_width); const vec3_t y_step = vec3_scale(v, 1.0 / (double)img_height);