stocking

git clone https://git.bradleygannon.com/stocking.git
Log | Files | Refs

commit 4f5afe74f9d1a18f02b93604bbb546a8f6e65a59
parent d60d1c0ee69d379f622f276e9c2bbfd3e0c3d622
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Tue, 16 Dec 2025 13:27:30 -0500

Make Camera more configurable

Complete Chapter 12

Diffstat:
Msrc/camera.rs | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++----------------
Msrc/main.rs | 14++++++++++++--
Msrc/vec3.rs | 12++++++++++++
3 files changed, 79 insertions(+), 18 deletions(-)

diff --git a/src/camera.rs b/src/camera.rs @@ -38,6 +38,7 @@ impl Image { } } +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] pub struct Camera { image_width: usize, image_height: usize, @@ -47,29 +48,66 @@ pub struct Camera { samples_per_pixel: usize, rng: RandomNumberGenerator, max_bounces: usize, + center: Vec3, +} + +#[derive(Clone, Copy, Debug, PartialEq, PartialOrd)] +pub struct CameraConfig { + pub image_width: usize, + pub aspect_ratio: f64, + pub samples_per_pixel: usize, + pub max_bounces: usize, + pub field_of_view_degrees: f64, + pub look_from: Vec3, + pub look_at: Vec3, + pub view_up: Vec3, +} + +impl Default for CameraConfig { + fn default() -> Self { + CameraConfig { + image_width: 100, + aspect_ratio: 1., + samples_per_pixel: 10, + max_bounces: 10, + field_of_view_degrees: 90., + look_from: Vec3::new(0., 0., 0.), + look_at: Vec3::new(0., 0., -1.), + view_up: Vec3::new(0., 1., 0.), + } + } } impl Camera { - const FOCAL_LENGTH: f64 = 1.; - const CAMERA_CENTER: Vec3 = Vec3::new(0., 0., 0.); const RNG_SEED: u64 = 42; - pub fn new( - image_width: usize, - aspect_ratio: f64, - samples_per_pixel: usize, - max_bounces: usize, - ) -> Self { + pub fn new(config: &CameraConfig) -> Self { + let CameraConfig { + image_width, + aspect_ratio, + samples_per_pixel, + max_bounces, + field_of_view_degrees, + look_from, + look_at, + view_up, + } = *config; + let image_height = ((image_width as f64 / aspect_ratio) as usize).max(1); - let viewport_height = 2.; + let theta = field_of_view_degrees.to_radians(); + let h = (theta / 2.).tan(); + let focal_length = (look_from - look_at).length(); + let viewport_height = 2. * h * focal_length; let viewport_width = viewport_height * aspect_ratio; - let viewport_u = Vec3::new(viewport_width, 0., 0.); - let viewport_v = Vec3::new(0., -viewport_height, 0.); - let viewport_upper_left = Self::CAMERA_CENTER - - Vec3::new(0., 0., Self::FOCAL_LENGTH) - - viewport_u / 2. - - viewport_v / 2.; + + let w = (look_from - look_at).unit(); + let u = view_up.cross(&w).unit(); + let v = w.cross(&u); + + let viewport_u = viewport_width * u; + let viewport_v = viewport_height * -v; + let viewport_upper_left = look_from - focal_length * w - viewport_u / 2. - viewport_v / 2.; let pixel_delta_u = viewport_u / image_width as f64; let pixel_delta_v = viewport_v / image_height as f64; @@ -86,6 +124,7 @@ impl Camera { samples_per_pixel, rng, max_bounces, + center: look_from, } } @@ -127,6 +166,6 @@ impl Camera { let pixel_sample = self.pixel_origin + (i as f64 + offset.x()) * self.pixel_delta_u + (j as f64 + offset.y()) * self.pixel_delta_v; - Ray::new(Self::CAMERA_CENTER, pixel_sample - Self::CAMERA_CENTER) + Ray::new(self.center, pixel_sample - self.center) } } diff --git a/src/main.rs b/src/main.rs @@ -1,7 +1,7 @@ use std::rc::Rc; use crate::{ - camera::Camera, + camera::{Camera, CameraConfig}, color::LinearColor, hittable::{Hittable, Sphere}, material::{Dielectric, Lambertian, Metal}, @@ -52,6 +52,16 @@ fn main() { )), ]; - let mut camera = Camera::new(400, 16. / 9., 100, 50); + let config = CameraConfig { + aspect_ratio: 16. / 9., + image_width: 400, + samples_per_pixel: 100, + max_bounces: 50, + field_of_view_degrees: 20., + look_from: Vec3::new(-2., 2., 1.), + look_at: Vec3::new(0., 0., -1.), + view_up: Vec3::new(0., 1., 0.), + }; + let mut camera = Camera::new(&config); camera.render(&world); } diff --git a/src/vec3.rs b/src/vec3.rs @@ -171,6 +171,18 @@ impl Mul<Vec3> for f64 { } } +impl Mul<Vec3> for Vec3 { + type Output = Vec3; + + fn mul(self, rhs: Vec3) -> Self::Output { + Vec3 { + x: self.x * rhs.x, + y: self.y * rhs.y, + z: self.z * rhs.z, + } + } +} + impl Neg for Vec3 { type Output = Self;