diff --git a/rtchallenge/examples/eoc8.rs b/rtchallenge/examples/eoc8.rs index db6665a..c7a44cd 100644 --- a/rtchallenge/examples/eoc8.rs +++ b/rtchallenge/examples/eoc8.rs @@ -22,6 +22,10 @@ use rtchallenge::{ struct Opt { #[structopt(long, default_value = "rayon")] render_strategy: RenderStrategy, + /// Number of samples per pixel. 0 renders from the center of the pixel, 1 or more samples N + /// times randomly across the pixel. + #[structopt(short, long, default_value = "0")] + samples: usize, } fn main() -> Result<()> { @@ -38,6 +42,7 @@ fn main() -> Result<()> { let up = Tuple::point(0., 1., 0.); camera.set_transform(view_transform(from, to, up)); camera.render_strategy = opt.render_strategy; + camera.samples_per_pixel = opt.samples; let mut floor = Sphere::default(); floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.)); diff --git a/rtchallenge/src/camera.rs b/rtchallenge/src/camera.rs index ddb137f..f494256 100644 --- a/rtchallenge/src/camera.rs +++ b/rtchallenge/src/camera.rs @@ -13,7 +13,12 @@ use serde::Deserialize; use structopt::StructOpt; use crate::{ - canvas::Canvas, matrices::Matrix4x4, rays::Ray, tuples::Tuple, world::World, Float, BLACK, + canvas::Canvas, + matrices::Matrix4x4, + rays::Ray, + tuples::{Color, Tuple}, + world::World, + Float, BLACK, }; #[derive(Copy, Clone, StructOpt, Debug, Deserialize)] @@ -41,6 +46,8 @@ pub struct Camera { half_width: Float, half_height: Float, pub render_strategy: RenderStrategy, + /// 0 renders from the center of the pixel, 1 or higher is random sampling of the pixel. + pub samples_per_pixel: usize, } enum Request { @@ -94,6 +101,7 @@ impl Camera { half_height, half_width, render_strategy: RenderStrategy::Rayon, + samples_per_pixel: 0, } } pub fn hsize(&self) -> usize { @@ -275,7 +283,7 @@ impl Camera { let pixel_resp_tx = pixel_resp_tx.clone(); thread::spawn(move || { core_affinity::set_for_current(id); - render_worker(&c, &w, pixel_req_rx, &pixel_resp_tx); + render_worker_task(&c, &w, pixel_req_rx, &pixel_resp_tx); }) }) .collect::>(); @@ -322,19 +330,8 @@ impl Camera { (0..self.vsize).into_par_iter().for_each(|y| { let mut row_image = Canvas::new(self.hsize, 1, BLACK); for x in 0..self.hsize { - const SAMPLES: usize = 0; - if SAMPLES > 0 { - let color = self - .supersample_rays_for_pixel(x, y, SAMPLES) - .iter() - .map(|ray| w.color_at(&ray)) - .fold(BLACK, |acc, c| acc + c); - row_image.set(x, 0, color / SAMPLES as Float); - } else { - let ray = self.ray_for_pixel(x, y); - let color = w.color_at(&ray); - row_image.set(x, 0, color); - } + let color = self.sample(w, x, y); + row_image.set(x, 0, color); } // TODO(wathiede): create a row based setter for memcpying the row as a whole. let mut image = image_mu.lock().expect("failed to lock image mutex"); @@ -352,16 +349,28 @@ impl Camera { let mut image = Canvas::new(self.hsize, self.vsize, BLACK); for y in 0..self.vsize { for x in 0..self.hsize { - let ray = self.ray_for_pixel(x, y); - let color = w.color_at(&ray); + let color = self.sample(w, x, y); image.set(x, y, color); } } image } -} -fn render_worker( + fn sample(&self, w: &World, x: usize, y: usize) -> Color { + if self.samples_per_pixel > 0 { + let color = self + .supersample_rays_for_pixel(x, y, self.samples_per_pixel) + .iter() + .map(|ray| w.color_at(&ray)) + .fold(BLACK, |acc, c| acc + c); + color / self.samples_per_pixel as Float + } else { + let ray = self.ray_for_pixel(x, y); + w.color_at(&ray) + } + } +} +fn render_worker_task( c: &Camera, w: &World, input_chan: Arc>>, @@ -381,8 +390,7 @@ fn render_worker( Request::Line { width, y } => { let mut pixels = Canvas::new(width, 1, BLACK); for x in 0..width { - let ray = c.ray_for_pixel(x, y); - let color = w.color_at(&ray); + let color = c.sample(w, x, y); pixels.set(x, 0, color); } output_chan