camera: cache inverse tranform for huge speed up on ray_for_pixel.

WIP parallel render function.
This commit is contained in:
Bill Thiede 2021-07-17 23:16:12 -07:00
parent 2395c96e01
commit 538b8ad364
4 changed files with 47 additions and 6 deletions

View File

@ -441,6 +441,7 @@ dependencies = [
"anyhow",
"criterion",
"png",
"rayon",
"thiserror",
]

View File

@ -10,9 +10,13 @@ edition = "2018"
anyhow = "1.0.41"
criterion = "0.3.4"
png = "0.16.8"
rayon = "1.5.1"
thiserror = "1.0.25"
[[bench]]
name = "matrices"
harness = false
[profile.release]
debug = true

View File

@ -25,7 +25,7 @@ fn main() -> Result<()> {
let from = Tuple::point(0., 1.5, -5.);
let to = Tuple::point(0., 1., 0.);
let up = Tuple::point(0., 1., 0.);
camera.transform = view_transform(from, to, up);
camera.set_transform(view_transform(from, to, up));
let mut floor = Sphere::default();
floor.set_transform(Matrix4x4::scaling(10., 0.01, 10.));

View File

@ -1,10 +1,15 @@
use std::sync::Mutex;
use rayon::iter::{IntoParallelIterator, ParallelIterator};
use crate::{canvas::Canvas, matrices::Matrix4x4, rays::Ray, tuples::Tuple, world::World, BLACK};
pub struct Camera {
hsize: usize,
vsize: usize,
field_of_view: f32,
pub transform: Matrix4x4,
transform: Matrix4x4,
inverse_transform: Matrix4x4,
pixel_size: f32,
half_width: f32,
half_height: f32,
@ -50,6 +55,7 @@ impl Camera {
vsize,
field_of_view,
transform: Matrix4x4::identity(),
inverse_transform: Matrix4x4::identity(),
pixel_size,
half_height,
half_width,
@ -67,6 +73,10 @@ impl Camera {
pub fn transform(&self) -> Matrix4x4 {
self.transform
}
pub fn set_transform(&mut self, t: Matrix4x4) {
self.transform = t;
self.inverse_transform = t.inverse();
}
pub fn pixel_size(&self) -> f32 {
self.pixel_size
}
@ -94,7 +104,7 @@ impl Camera {
///
/// // Constructing a ray when the camera is transformed.
/// let mut c = Camera::new(201, 101, PI / 2.);
/// c.transform = Matrix4x4::rotation_y(PI / 4.) * Matrix4x4::translation(0., -2., 5.);
/// c.set_transform(Matrix4x4::rotation_y(PI / 4.) * Matrix4x4::translation(0., -2., 5.));
/// let r = c.ray_for_pixel(100, 50);
/// assert_eq!(r.origin, Tuple::point(0., 2., -5.));
/// assert_eq!(
@ -115,8 +125,8 @@ 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).
let pixel = self.transform.inverse() * Tuple::point(world_x, world_y, -1.);
let origin = self.transform.inverse() * Tuple::point(0., 0., 0.);
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)
@ -141,11 +151,37 @@ impl Camera {
/// let from = Tuple::point(0., 0., -5.);
/// let to = Tuple::point(0., 0., 0.);
/// let up = Tuple::vector(0., 1., 0.);
/// c.transform = view_transform(from, to, up);
/// c.set_transform(view_transform(from, to, up));
/// let image = c.render(&w);
/// assert_eq!(image.get(5, 5), Color::new(0.38066, 0.47583, 0.2855));
/// ```
pub fn render(&self, w: &World) -> Canvas {
self.render_serial(w)
}
#[allow(dead_code)]
fn render_parallel(&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")
}
fn render_serial(&self, w: &World) -> Canvas {
let mut image = Canvas::new(self.hsize, self.vsize, BLACK);
for y in 0..self.vsize {
for x in 0..self.hsize {