diff --git a/rtchallenge/Cargo.toml b/rtchallenge/Cargo.toml index 32b461b..6962e19 100644 --- a/rtchallenge/Cargo.toml +++ b/rtchallenge/Cargo.toml @@ -6,6 +6,9 @@ edition = "2018" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[features] +disable_inverse_cache = [] + [dependencies] anyhow = "1.0.41" criterion = "0.3.4" diff --git a/rtchallenge/src/camera.rs b/rtchallenge/src/camera.rs index a7f3181..bbe9af2 100644 --- a/rtchallenge/src/camera.rs +++ b/rtchallenge/src/camera.rs @@ -112,6 +112,7 @@ impl Camera { /// Tuple::vector(2_f32.sqrt() / 2., 0., -2_f32.sqrt() / 2.) /// ); /// ``` + #[cfg(not(feature = "disable_inverse_cache"))] pub fn ray_for_pixel(&self, px: usize, py: usize) -> Ray { // The offset from the edge of the canvas to the pixel's corner. let xoffset = (px as f32 + 0.5) * self.pixel_size; @@ -124,16 +125,35 @@ impl Camera { // Using the camera matrix, transofmrm the canvas point and the origin, // and then compute the ray's direction vector. - // (Remember that the canves is at z>=-1). + // (Remember that the canvas is at z>=-1). let pixel = self.inverse_transform * Tuple::point(world_x, world_y, -1.); let origin = self.inverse_transform * Tuple::point(0., 0., 0.); let direction = (pixel - origin).normalize(); Ray::new(origin, direction) } + #[cfg(feature = "disable_inverse_cache")] + pub fn ray_for_pixel(&self, px: usize, py: usize) -> Ray { + // The offset from the edge of the canvas to the pixel's corner. + let xoffset = (px as f32 + 0.5) * self.pixel_size; + let yoffset = (py as f32 + 0.5) * self.pixel_size; + + // The untransformed coordinates of the pixle in world space. + // (Remember that the camera looks toward -z, so +x is to the left.) + let world_x = self.half_width - xoffset; + let world_y = self.half_height - yoffset; + + // Using the camera matrix, transofmrm the canvas point and the origin, + // and then compute the ray's direction vector. + // (Remember that the canvas is at z>=-1). + let pixel = self.transform.inverse() * Tuple::point(world_x, world_y, -1.); + let origin = self.transform.inverse() * Tuple::point(0., 0., 0.); + let direction = (pixel - origin).normalize(); + + Ray::new(origin, direction) + } /// Use camera to render an image of the given world. - /// /// # Examples /// ``` /// use std::f32::consts::PI; @@ -160,7 +180,28 @@ impl Camera { } #[allow(dead_code)] - fn render_parallel(&self, w: &World) -> Canvas { + fn render_parallel_one_tread_per_core(&self, w: &World) -> Canvas { + let image_mu = Mutex::new(Canvas::new(self.hsize, self.vsize, BLACK)); + + (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 { + let ray = self.ray_for_pixel(x, y); + let color = w.color_at(&ray); + 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"); + for x in 0..self.hsize { + image.set(x, y, row_image.get(x, 0)); + } + }); + image_mu + .into_inner() + .expect("failed to get image out of mutex") + } + #[allow(dead_code)] + fn render_parallel_rayon(&self, w: &World) -> Canvas { let image_mu = Mutex::new(Canvas::new(self.hsize, self.vsize, BLACK)); (0..self.vsize).into_par_iter().for_each(|y| { diff --git a/rtchallenge/src/spheres.rs b/rtchallenge/src/spheres.rs index 9a27f33..64bc3e3 100644 --- a/rtchallenge/src/spheres.rs +++ b/rtchallenge/src/spheres.rs @@ -105,6 +105,7 @@ impl Sphere { /// let n = s.normal_at(Tuple::point(0., 2_f32.sqrt()/2., -2_f32.sqrt()/2.)); /// assert_eq!(n, Tuple::vector(0., 0.97014, -0.24254)); /// ``` + #[cfg(not(feature = "disable_inverse_cache"))] pub fn normal_at(&self, world_point: Tuple) -> Tuple { let object_point = self.inverse_transform * world_point; let object_normal = object_point - Tuple::point(0., 0., 0.); @@ -112,6 +113,14 @@ impl Sphere { world_normal.w = 0.; world_normal.normalize() } + #[cfg(feature = "disable_inverse_cache")] + pub fn normal_at(&self, world_point: Tuple) -> Tuple { + let object_point = self.transform.inverse() * world_point; + let object_normal = object_point - Tuple::point(0., 0., 0.); + let mut world_normal = self.transform.inverse().transpose() * object_normal; + world_normal.w = 0.; + world_normal.normalize() + } pub fn transform(&self) -> Matrix4x4 { self.transform