diff --git a/Cargo.lock b/Cargo.lock index cb035a7..6c0c44e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -33,6 +33,17 @@ version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +[[package]] +name = "getrandom" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + [[package]] name = "glam" version = "0.24.1" @@ -94,6 +105,42 @@ version = "1.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "31114a898e107c51bb1609ffaf55a0e011cf6a4d7f1170d0015a165082c0338b" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + [[package]] name = "raytracing-in-one-weekend" version = "0.1.0" @@ -101,6 +148,7 @@ dependencies = [ "glam", "indicatif", "itertools", + "rand", ] [[package]] @@ -109,6 +157,12 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b" +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + [[package]] name = "windows-sys" version = "0.45.0" diff --git a/Cargo.toml b/Cargo.toml index b2187bc..ee0b397 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,3 +9,4 @@ edition = "2021" glam = "0.24.1" indicatif = "0.17.6" itertools = "0.11.0" +rand = "0.8.5" diff --git a/src/main.rs b/src/main.rs index d1a80ae..efdb2b0 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,7 @@ use glam::DVec3; use indicatif::ProgressIterator; use itertools::Itertools; +use rand::prelude::*; use std::{fs, io, ops::Range}; fn main() -> io::Result<()> { @@ -31,6 +32,7 @@ struct Camera { pixel_delta_v: DVec3, // viewport_upper_left: DVec3, pixel00_loc: DVec3, + samples_per_pixel: u32, } impl Camera { fn new(image_width: u32, aspect_ratio: f64) -> Self { @@ -73,8 +75,35 @@ impl Camera { pixel_delta_v, // viewport_upper_left, pixel00_loc, + samples_per_pixel: 100, } } + fn get_ray(&self, i: i32, j: i32) -> Ray { + // Get a randomly sampled camera ray for the pixel at location i,j. + + let pixel_center = self.pixel00_loc + + (i as f64 * self.pixel_delta_u) + + (j as f64 * self.pixel_delta_v); + let pixel_sample = + pixel_center + self.pixel_sample_square(); + + let ray_origin = self.center; + let ray_direction = pixel_sample - ray_origin; + + Ray { + origin: self.center, + direction: ray_direction, + } + } + + fn pixel_sample_square(&self) -> DVec3 { + let mut rng = rand::thread_rng(); + // Returns a random point in the square surrounding a pixel at the origin. + let px = -0.5 + rng.gen::(); + let py = -0.5 + rng.gen::(); + (px * self.pixel_delta_u) + + (py * self.pixel_delta_v) + } fn render_to_disk(&self, world: T) -> io::Result<()> where T: Hittable, @@ -86,23 +115,25 @@ impl Camera { * self.image_width as u64, ) .map(|(y, x)| { - let pixel_center = self.pixel00_loc - + (x as f64 * self.pixel_delta_u) - + (y as f64 * self.pixel_delta_v); - let ray_direction = - pixel_center - self.center; - let ray = Ray { - origin: self.center, - direction: ray_direction, - }; - - let pixel_color = ray.color(&world) * 255.0; + let scale_factor = + (self.samples_per_pixel as f64).recip(); + + let multisampled_pixel_color = (0..self + .samples_per_pixel) + .into_iter() + .map(|_| { + self.get_ray(x as i32, y as i32) + .color(&world) + * 255.0 + * scale_factor + }) + .sum::(); format!( "{} {} {}", - pixel_color.x, - pixel_color.y, - pixel_color.z + multisampled_pixel_color.x, + multisampled_pixel_color.y, + multisampled_pixel_color.z ) }) .join("\n"); @@ -154,9 +185,6 @@ trait Hittable { &self, ray: &Ray, interval: Range, - // ray_tmin: f64, - // ray_tmax: f64, - // record: HitRecord, ) -> Option; } @@ -236,9 +264,6 @@ impl Hittable for Sphere { &self, ray: &Ray, interval: Range, - // ray_tmin: f64, - // ray_tmax: f64, - // record: HitRecord, ) -> Option { let oc = ray.origin - self.center; let a = ray.direction.length_squared(); @@ -299,8 +324,6 @@ impl Hittable for HittableList { &self, ray: &Ray, interval: Range, - // ray_tmin: f64, - // ray_tmax: f64, ) -> Option { let (_closest, hit_record) = self .objects