stocking

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

commit ccbf1e22718c6d1d30caecb647c878480e6377d5
parent d099b5c11afa71975a5b9e51c12d271afcfeb9d2
Author: Bradley Gannon <bradley@bradleygannon.com>
Date:   Tue, 16 Dec 2025 18:10:05 -0500

Recreate the final test scene from the first book

Diffstat:
Msrc/camera.rs | 7+++----
Msrc/color.rs | 14++++++++++++++
Msrc/main.rs | 94++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------
Msrc/random.rs | 2+-
4 files changed, 78 insertions(+), 39 deletions(-)

diff --git a/src/camera.rs b/src/camera.rs @@ -66,6 +66,7 @@ pub struct CameraConfig { pub view_up: Vec3, pub defocus_angle_degrees: f64, pub focus_distance: f64, + pub rng: RandomNumberGenerator, } impl Default for CameraConfig { @@ -81,13 +82,12 @@ impl Default for CameraConfig { view_up: Vec3::new(0., 1., 0.), defocus_angle_degrees: 0., focus_distance: 10., + rng: RandomNumberGenerator::new_with_time_seed(), } } } impl Camera { - const RNG_SEED: u64 = 42; - pub fn new(config: &CameraConfig) -> Self { let CameraConfig { image_width, @@ -100,6 +100,7 @@ impl Camera { view_up, defocus_angle_degrees, focus_distance, + rng, } = *config; let image_height = ((image_width as f64 / aspect_ratio) as usize).max(1); @@ -126,8 +127,6 @@ impl Camera { let defocus_disk_u = u * defocus_radius; let defocus_disk_v = v * defocus_radius; - let rng = RandomNumberGenerator::new(Self::RNG_SEED); - Camera { image_width, image_height, diff --git a/src/color.rs b/src/color.rs @@ -1,5 +1,7 @@ use std::ops::{Add, AddAssign, Div, Mul}; +use crate::random::RandomNumberGenerator; + #[derive(Clone, Copy, Debug, Default, PartialEq, PartialOrd)] pub struct GammaColor { red: f64, @@ -63,6 +65,18 @@ impl LinearColor { pub const fn blue(&self) -> f64 { self.blue } + + pub fn random(rng: &mut RandomNumberGenerator) -> Self { + LinearColor::new(rng.next_f64(), rng.next_f64(), rng.next_f64()) + } + + pub fn random_in_range(min: f64, max: f64, rng: &mut RandomNumberGenerator) -> Self { + LinearColor { + red: rng.next_f64_in_range(min, max), + green: rng.next_f64_in_range(min, max), + blue: rng.next_f64_in_range(min, max), + } + } } impl Add<LinearColor> for LinearColor { diff --git a/src/main.rs b/src/main.rs @@ -5,6 +5,7 @@ use crate::{ color::LinearColor, hittable::{Hittable, Sphere}, material::{Dielectric, Lambertian, Metal}, + random::RandomNumberGenerator, vec3::Vec3, }; @@ -17,52 +18,77 @@ mod random; mod ray; mod vec3; +const RNG_SEED: u64 = 42; + fn main() { - let material_ground = Lambertian::new(LinearColor::new(0.8, 0.8, 0.)); - let material_center = Lambertian::new(LinearColor::new(0.1, 0.2, 0.5)); - let material_left = Dielectric::new(1.5); - let material_bubble = Dielectric::new(1. / 1.5); - let material_right = Metal::new(LinearColor::new(0.8, 0.6, 0.2), 1.0); + let material_ground = Lambertian::new(LinearColor::new(0.5, 0.5, 0.5)); + let material1 = Dielectric::new(1.5); + let material2 = Lambertian::new(LinearColor::new(0.4, 0.2, 0.1)); + let material3 = Metal::new(LinearColor::new(0.7, 0.6, 0.5), 0.); + + let mut rng = RandomNumberGenerator::new(RNG_SEED); - let world: Vec<Box<dyn Hittable>> = vec![ + let mut world: Vec<Box<dyn Hittable>> = vec![ Box::new(Sphere::new( - Vec3::new(0., -100.5, -1.), - 100., + Vec3::new(0., -1000., 0.), + 1000., Rc::new(material_ground), )), - Box::new(Sphere::new( - Vec3::new(0., 0., -1.2), - 0.5, - Rc::new(material_center), - )), - Box::new(Sphere::new( - Vec3::new(-1., 0., -1.), - 0.5, - Rc::new(material_left), - )), - Box::new(Sphere::new( - Vec3::new(-1., 0., -1.), - 0.4, - Rc::new(material_bubble), - )), - Box::new(Sphere::new( - Vec3::new(1., 0., -1.), - 0.5, - Rc::new(material_right), - )), + Box::new(Sphere::new(Vec3::new(0., 1., 0.), 1., Rc::new(material1))), + Box::new(Sphere::new(Vec3::new(-4., 1., 0.), 1., Rc::new(material2))), + Box::new(Sphere::new(Vec3::new(4., 1., 0.), 1., Rc::new(material3))), ]; + for a in -11..11 { + for b in -11..11 { + let center = Vec3::new( + a as f64 + 0.9 * rng.next_f64(), + 0.2, + b as f64 + 0.9 * rng.next_f64(), + ); + if (center - Vec3::new(4., 0.2, 0.)).length() > 0.9 { + match rng.next_f64() { + 0.0..0.8 => { + let albedo = LinearColor::random(&mut rng) * LinearColor::random(&mut rng); + world.push(Box::new(Sphere::new( + center, + 0.2, + Rc::new(Lambertian::new(albedo)), + ))); + } + 0.8..0.9 => { + let albedo = LinearColor::random_in_range(0.5, 1., &mut rng); + let fuzz = rng.next_f64_in_range(0., 0.5); + world.push(Box::new(Sphere::new( + center, + 0.2, + Rc::new(Metal::new(albedo, fuzz)), + ))); + } + _ => { + world.push(Box::new(Sphere::new( + center, + 0.2, + Rc::new(Dielectric::new(1.5)), + ))); + } + }; + } + } + } + let config = CameraConfig { aspect_ratio: 16. / 9., - image_width: 400, - samples_per_pixel: 100, + image_width: 1200, + samples_per_pixel: 500, max_bounces: 50, field_of_view_degrees: 20., - look_from: Vec3::new(-2., 2., 1.), - look_at: Vec3::new(0., 0., -1.), + look_from: Vec3::new(13., 2., 3.), + look_at: Vec3::new(0., 0., 0.), view_up: Vec3::new(0., 1., 0.), - defocus_angle_degrees: 10., - focus_distance: 3.4, + defocus_angle_degrees: 0.6, + focus_distance: 10., + rng, }; let mut camera = Camera::new(&config); camera.render(&world); diff --git a/src/random.rs b/src/random.rs @@ -17,7 +17,7 @@ impl RandomNumberGenerator { rng } - pub fn _new_with_time_seed() -> Self { + pub fn new_with_time_seed() -> Self { let nanos = SystemTime::now() .duration_since(SystemTime::UNIX_EPOCH) .unwrap()