From e9900af986f1bd52079dd7f64487c47a795b6d08 Mon Sep 17 00:00:00 2001 From: Matthew Gordon Date: Tue, 12 Nov 2019 07:31:02 -0500 Subject: [PATCH] Some cleanup and added unit test for pixel ray generation --- src/camera.rs | 102 ++++++++++++++++++++++++-------------------------- 1 file changed, 49 insertions(+), 53 deletions(-) diff --git a/src/camera.rs b/src/camera.rs index 53b0883..c241cb2 100644 --- a/src/camera.rs +++ b/src/camera.rs @@ -1,70 +1,60 @@ -use nalgebra::{RealField, Vector3}; +use nalgebra::{convert, RealField, Vector3}; use super::image::OutputImage; -use super::raycasting::{Intersect, IntersectionInfo, Ray}; +use super::raycasting::{Intersect, IntersectionInfo, Plane, Ray}; -struct ImageSampler + From> { +struct ImageSampler { image_height_pixels: u32, image_width_pixels: u32, - fov_width: T, - fov_height: T, - - current_row: u32, - current_column: u32, + film_width: T, + film_height: T, camera_location: Vector3, - film_plane_distance: T, + film_distance: T, } -impl + From> ImageSampler { +impl ImageSampler { pub fn new(width: u32, height: u32, camera_location: Vector3) -> ImageSampler { - let (fov_width, fov_height) = if (width > height) { - (T::from(width) / (T::from(height)), T::from(1.0)) - } else { - (T::from(1.0), T::from(width) / T::from(height)) + let (film_width, film_height) = { + let width: T = convert(width as f64); + let height: T = convert(height as f64); + let film_size: T = convert(1.0); + if width > height { + (width / height, film_size) + } else { + (film_size, width / height) + } }; ImageSampler { image_height_pixels: height, image_width_pixels: width, - current_row: 0, - current_column: 0, - film_plane_distance: T::from(1.0), - fov_width, - fov_height, + film_distance: convert(1.0), + film_width, + film_height, camera_location, } } fn scale(i: u32, n: u32, l: T) -> T { - let pixel_size = l * (T::from(1.0) / T::from(n)); - (T::from(i) + T::from(0.5)) * pixel_size + let one: T = convert(1.0); + let n: T = convert(n as f64); + let i: T = convert(i as f64); + let pixel_size: T = l * (one / n); + (i + convert(0.5)) * pixel_size } -} -impl + From> Iterator for ImageSampler { - type Item = Ray; - - fn next(&mut self) -> Option { - if self.current_row >= self.image_height_pixels { - return None; - } - - let result = Ray::new( + fn ray_for_pixel(&self, row: u32, column: u32) -> Ray { + Ray::new( self.camera_location, Vector3::new( - Self::scale(self.current_column, self.image_width_pixels, self.fov_width), - Self::scale(self.current_row, self.image_height_pixels, self.fov_height), - self.film_plane_distance, + Self::scale(column, self.image_width_pixels, self.film_width) + - self.film_width * convert(0.5), + Self::scale(row, self.image_height_pixels, self.film_height) + - self.film_height * convert(0.5), + self.film_distance, ), - ); - - self.current_column += 1; - if (self.current_column >= self.image_width_pixels) { - self.current_column = 0; - self.current_row += 1; - } - Some(result) + ) } } @@ -78,25 +68,31 @@ mod tests { #[test] fn scale_returns_correct_value_for_zero() { let correct_value = (3.0 / 10.0) / 2.0; - assert!((ImageSampler::scale(0, 10, 3.0) - correct_value).abs() < 0.0000000001) + assert!((ImageSampler::scale(0, 10, 3.0f64) - correct_value).abs() < 0.0000000001) } #[test] fn scale_returns_correct_value_for_last_pixel() { let correct_value = 3.0 - (3.0 / 10.0) / 2.0; - assert!((ImageSampler::scale(9, 10, 3.0) - correct_value).abs() < 0.0000000001) + assert!((ImageSampler::scale(9, 10, 3.0f64) - correct_value).abs() < 0.0000000001) } #[test] - fn iterates_over_correct_number_of_samples() { - let width = 100; - let height = 200; - let target = ImageSampler::new(width, height, Vector3::new(0.0, 0.0, 0.0)); - let mut count = 0; - for sample in target { - count += 1; - } - assert!(count == width * height); + fn ray_for_pixel_returns_value_that_intersects_film_plane_at_expected_location() { + let target = ImageSampler::new(800, 600, Vector3::new(0.0, 0.0, 0.0)); + let ray = target.ray_for_pixel(100, 200); + let film_plane = Plane::new(Vector3::new(0.0, 0.0, 1.0), target.film_distance); + let point_on_film_plane = match film_plane.intersect(&ray) { + Some(IntersectionInfo { location, distance }) => location, + None => panic!(), + }; + let expected_x: f64 = + ImageSampler::scale(200, 800, target.film_width) - target.film_width * 0.5; + print!("{}, {}", expected_x, point_on_film_plane); + assert!((point_on_film_plane.x - expected_x).abs() < 0.0000000001); + let expected_y = + ImageSampler::scale(100, 600, target.film_height) - target.film_height * 0.5; + assert!((point_on_film_plane.y - expected_y).abs() < 0.0000000001); } } }