Some cleanup and added unit test for pixel ray generation

This commit is contained in:
Matthew Gordon 2019-11-12 07:31:02 -05:00
parent 041c940931
commit e9900af986
1 changed files with 49 additions and 53 deletions

View File

@ -1,70 +1,60 @@
use nalgebra::{RealField, Vector3}; use nalgebra::{convert, RealField, Vector3};
use super::image::OutputImage; use super::image::OutputImage;
use super::raycasting::{Intersect, IntersectionInfo, Ray}; use super::raycasting::{Intersect, IntersectionInfo, Plane, Ray};
struct ImageSampler<T: RealField + From<u32> + From<f64>> { struct ImageSampler<T: RealField> {
image_height_pixels: u32, image_height_pixels: u32,
image_width_pixels: u32, image_width_pixels: u32,
fov_width: T, film_width: T,
fov_height: T, film_height: T,
current_row: u32,
current_column: u32,
camera_location: Vector3<T>, camera_location: Vector3<T>,
film_plane_distance: T, film_distance: T,
} }
impl<T: RealField + From<u32> + From<f64>> ImageSampler<T> { impl<T: RealField> ImageSampler<T> {
pub fn new(width: u32, height: u32, camera_location: Vector3<T>) -> ImageSampler<T> { pub fn new(width: u32, height: u32, camera_location: Vector3<T>) -> ImageSampler<T> {
let (fov_width, fov_height) = if (width > height) { let (film_width, film_height) = {
(T::from(width) / (T::from(height)), T::from(1.0)) let width: T = convert(width as f64);
} else { let height: T = convert(height as f64);
(T::from(1.0), T::from(width) / T::from(height)) let film_size: T = convert(1.0);
if width > height {
(width / height, film_size)
} else {
(film_size, width / height)
}
}; };
ImageSampler { ImageSampler {
image_height_pixels: height, image_height_pixels: height,
image_width_pixels: width, image_width_pixels: width,
current_row: 0, film_distance: convert(1.0),
current_column: 0, film_width,
film_plane_distance: T::from(1.0), film_height,
fov_width,
fov_height,
camera_location, camera_location,
} }
} }
fn scale(i: u32, n: u32, l: T) -> T { fn scale(i: u32, n: u32, l: T) -> T {
let pixel_size = l * (T::from(1.0) / T::from(n)); let one: T = convert(1.0);
(T::from(i) + T::from(0.5)) * pixel_size 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<T: RealField + From<u32> + From<f64>> Iterator for ImageSampler<T> { fn ray_for_pixel(&self, row: u32, column: u32) -> Ray<T> {
type Item = Ray<T>; Ray::new(
fn next(&mut self) -> Option<Self::Item> {
if self.current_row >= self.image_height_pixels {
return None;
}
let result = Ray::new(
self.camera_location, self.camera_location,
Vector3::new( Vector3::new(
Self::scale(self.current_column, self.image_width_pixels, self.fov_width), Self::scale(column, self.image_width_pixels, self.film_width)
Self::scale(self.current_row, self.image_height_pixels, self.fov_height), - self.film_width * convert(0.5),
self.film_plane_distance, 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] #[test]
fn scale_returns_correct_value_for_zero() { fn scale_returns_correct_value_for_zero() {
let correct_value = (3.0 / 10.0) / 2.0; 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] #[test]
fn scale_returns_correct_value_for_last_pixel() { fn scale_returns_correct_value_for_last_pixel() {
let correct_value = 3.0 - (3.0 / 10.0) / 2.0; 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] #[test]
fn iterates_over_correct_number_of_samples() { fn ray_for_pixel_returns_value_that_intersects_film_plane_at_expected_location() {
let width = 100; let target = ImageSampler::new(800, 600, Vector3::new(0.0, 0.0, 0.0));
let height = 200; let ray = target.ray_for_pixel(100, 200);
let target = ImageSampler::new(width, height, Vector3::new(0.0, 0.0, 0.0)); let film_plane = Plane::new(Vector3::new(0.0, 0.0, 1.0), target.film_distance);
let mut count = 0; let point_on_film_plane = match film_plane.intersect(&ray) {
for sample in target { Some(IntersectionInfo { location, distance }) => location,
count += 1; None => panic!(),
} };
assert!(count == width * height); 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);
} }
} }
} }