From 07d45875843d9f3b426edbeba19d02152ca64885 Mon Sep 17 00:00:00 2001 From: zongor Date: Thu, 24 Jul 2025 13:02:42 -0400 Subject: [PATCH] add camera --- src/camera.h | 73 ++++++++++++++++++++++++++++++++++++++++++ src/simulation.cpp | 79 +++++++++++----------------------------------- 2 files changed, 92 insertions(+), 60 deletions(-) create mode 100644 src/camera.h diff --git a/src/camera.h b/src/camera.h new file mode 100644 index 0000000..e29be8b --- /dev/null +++ b/src/camera.h @@ -0,0 +1,73 @@ +#ifndef CAMERA_H +#define CAMERA_H + +#include "common.h" +#include "hittable.h" + +class Camera { +public: + double aspect_ratio = 1.0; // Ratio of image width over height + int image_width = 100; // Rendered image width in pixel count + + void render(const hittable &world, Canvas &canvas) { + initialize(); + + for (int j = 0; j < canvas.height; j++) { + for (int i = 0; i < canvas.width; i++) { + auto pixel_center = + pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v); + auto ray_direction = pixel_center - center; + Ray r(center, ray_direction); + + canvas.set_pixel(i, j, ray_color(r, world)); + } + } + } + + void move_to(Point3 loc) { center = loc; } + +private: + int image_height; // Rendered image height + Point3 center = Point3(0, 0, 0); + Point3 pixel00_loc; // Location of pixel 0, 0 + Vec3 pixel_delta_u; // Offset to pixel to the right + Vec3 pixel_delta_v; // Offset to pixel below + + void initialize() { + image_height = int(image_width / aspect_ratio); + image_height = (image_height < 1) ? 1 : image_height; + + // Determine viewport dimensions. + auto focal_length = 1.0; + auto viewport_height = 2.0; + auto viewport_width = + viewport_height * (double(image_width) / image_height); + + // Calculate the vectors across the horizontal and down the vertical + // viewport edges. + auto viewport_u = Vec3(viewport_width, 0, 0); + auto viewport_v = Vec3(0, -viewport_height, 0); + + // Calculate the horizontal and vertical delta vectors from pixel to pixel. + pixel_delta_u = viewport_u / image_width; + pixel_delta_v = viewport_v / image_height; + + // Calculate the location of the upper left pixel. + auto viewport_upper_left = + center - Vec3(0, 0, focal_length) - viewport_u / 2 - viewport_v / 2; + pixel00_loc = viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v); + } + + Color ray_color(const Ray &r, const hittable &world) { + hit_record rec; + if (world.hit(r, 0, infinity, rec)) { + return 0.5 * (rec.normal + Color(1, 1, 1)); + } + + Vec3 unit_direction = unit_vector(r.direction()); + auto a = 0.5 * (unit_direction.y() + 1.0); + return (1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color(0.5, 0.7, 1.0); + } +}; + +#endif diff --git a/src/simulation.cpp b/src/simulation.cpp index c879739..7adbbcb 100644 --- a/src/simulation.cpp +++ b/src/simulation.cpp @@ -1,6 +1,6 @@ #include "simulation.h" +#include "camera.h" #include "common.h" -#include "hittable.h" #include "hittable_list.h" #include "sphere.h" @@ -124,73 +124,32 @@ void simulation_thread(RingBuffer &buffer) { } } -Color ray_color(const Ray &r, const hittable &world) { - hit_record rec; - if (world.hit(r, 0, infinity, rec)) { - return 0.5 * (rec.normal + Color(1, 1, 1)); - } - - Vec3 unit_direction = unit_vector(r.direction()); - auto a = 0.5 * (unit_direction.y() + 1.0); - return (1.0 - a) * Color(1.0, 1.0, 1.0) + a * Color(0.5, 0.7, 1.0); -} - -/** - * Render raytracing. - */ -void render(SimulationState state, Canvas &canvas) { - hittable_list world; - - const double SCALE = 1e9; - - for (auto b : state.bodies) { - // we need to swap y and z because the raytracter uses opengl style coordinates - auto bb = b.position / SCALE; - world.add(make_shared(Vec3(bb.x(), bb.z(), bb.y()), b.radius)); - } - - // Camera - auto focal_length = 1.0; - auto viewport_height = 2.0; - auto viewport_width = viewport_height * (float(canvas.width) / canvas.height); - auto camera_center = Point3(0, 0, 100); - - // Calculate the vectors across the horizontal and down the vertical viewport - // edges. - auto viewport_u = Vec3(viewport_width, 0, 0); - auto viewport_v = Vec3(0, -viewport_height, 0); - - // Calculate the horizontal and vertical delta vectors from pixel to pixel. - auto pixel_delta_u = viewport_u / canvas.width; - auto pixel_delta_v = viewport_v / canvas.height; - - // Calculate the location of the upper left pixel. - auto viewport_upper_left = camera_center - Vec3(0, 0, focal_length) - - viewport_u / 2 - viewport_v / 2; - auto pixel00_loc = - viewport_upper_left + 0.5 * (pixel_delta_u + pixel_delta_v); - - for (int j = 0; j < canvas.height; j++) { - for (int i = 0; i < canvas.width; i++) { - auto pixel_center = - pixel00_loc + (i * pixel_delta_u) + (j * pixel_delta_v); - auto ray_direction = pixel_center - camera_center; - Ray r(camera_center, ray_direction); - - canvas.set_pixel(i, j, ray_color(r, world)); - } - } -} - /** * Render thread. */ void render_thread(RingBuffer &buffer, Canvas &canvas) { + Camera cam; + cam.aspect_ratio = 16.0 / 9.0; + cam.image_width = 400; + cam.move_to(Point3(0, 0, 100)); + while (running) { auto maybe_state = buffer.pop(); if (maybe_state.has_value()) { const SimulationState &state = maybe_state.value(); - render(state, canvas); // Pure function: write pixel colors into canvas + + hittable_list world; + + const double SCALE = 1e9; + + for (auto b : state.bodies) { + // we need to swap y and z because the raytracter uses opengl style + // coordinates + auto bb = b.position / SCALE; + world.add(make_shared(Vec3(bb.x(), bb.z(), bb.y()), b.radius)); + } + + cam.render(world, canvas); } else { std::this_thread::sleep_for(std::chrono::milliseconds(1)); }