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:
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()