diff --git a/src/camera.rs b/src/camera.rs new file mode 100644 index 0000000..a26808b --- /dev/null +++ b/src/camera.rs @@ -0,0 +1,113 @@ +#![allow(dead_code)] + +use std::f32::consts::FRAC_PI_4; + +use math::{Vector, Point, Matrix4x4}; +use ray::Ray; +use lerp::lerp_slice; + +#[derive(Debug)] +pub struct Camera { + transforms: Vec, + fovs: Vec, + tfovs: Vec, + aperture_radii: Vec, + focus_distances: Vec, +} + +impl Camera { + pub fn new(transforms: Vec, + fovs: Vec, + mut aperture_radii: Vec, + mut focus_distances: Vec) + -> Camera { + assert!(transforms.len() != 0, "Camera has no transform(s)!"); + assert!(fovs.len() != 0, "Camera has no fov(s)!"); + + // Aperture needs focus distance and vice-versa. + if aperture_radii.len() == 0 || focus_distances.len() == 0 { + aperture_radii = vec![0.0]; + focus_distances = vec![1.0]; + + if aperture_radii.len() == 0 && focus_distances.len() != 0 { + println!("WARNING: camera has aperture radius but no focus distance. Disabling \ + focal blur."); + } else if aperture_radii.len() != 0 && focus_distances.len() == 0 { + println!("WARNING: camera has focus distance but no aperture radius. Disabling \ + focal blur."); + } + } + + // Can't have focus distance of zero. + if focus_distances.iter().any(|d| *d == 0.0) { + println!("WARNING: camera focal distance is zero or less. Disabling focal blur."); + aperture_radii = vec![0.0]; + focus_distances = vec![1.0]; + } + + // Convert angle fov into linear fov. + let tfovs = fovs.iter().map(|n| (n / 2.0).sin() / (n / 2.0).cos()).collect(); + + Camera { + transforms: transforms, + fovs: fovs, + tfovs: tfovs, + aperture_radii: aperture_radii, + focus_distances: focus_distances, + } + } + + pub fn generate_ray(&self, x: f32, y: f32, time: f32, u: f32, v: f32) -> Ray { + // Get time-interpolated camera settings + let transform = lerp_slice(&self.transforms, time); + let tfov = lerp_slice(&self.tfovs, time); + let aperture_radius = lerp_slice(&self.aperture_radii, time); + let focus_distance = lerp_slice(&self.focus_distances, time); + + // Ray origin + let orig = { + let (u, v) = square_to_circle(aperture_radius * ((u * 2.0) - 1.0), + aperture_radius * ((v * 2.0) - 1.0)); + Point::new(u, v, 0.0) + }; + + // Ray direction + let dir = Vector::new((x * tfov) - (orig[0] / focus_distance), + (y * tfov) - (orig[1] / focus_distance), + 1.0) + .normalized(); + + Ray::new(orig * transform, dir * transform, time) + } +} + + +/// Maps the unit square to the unit circle. +/// NOTE: x and y should be distributed within [-1, 1], +/// not [0, 1]. +fn square_to_circle(x: f32, y: f32) -> (f32, f32) { + debug_assert!(x >= -1.0 && x <= 1.0); + debug_assert!(y >= -1.0 && y <= 1.0); + + if x == 0.0 && y == 0.0 { + return (0.0, 0.0); + } + + let (radius, angle) = { + if x > y.abs() { + // Quadrant 1 + (x, (y / x) * FRAC_PI_4) + } else if y > x.abs() { + // Quadrant 2 + (y, (2.0 - (x / y)) * FRAC_PI_4) + } else if x < -(y.abs()) { + // Quadrant 3 + (-x, (4.0 + (y / x)) * FRAC_PI_4) + } else { + // Quadrant 4 + (-y, (6.0 - (x / y)) * FRAC_PI_4) + } + }; + + return (radius * angle.cos(), radius * angle.sin()); +} diff --git a/src/main.rs b/src/main.rs index ef7c26e..a64d228 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,6 +7,7 @@ mod lerp; mod float4; mod ray; mod bbox; +mod camera; mod data_tree; mod image; mod triangle; @@ -95,6 +96,9 @@ fn main() { let cx = halton::sample(0, i) * 512.0; let cy = halton::sample(1, i) * 512.0; let cz = halton::sample(2, i) * 512.0; + // let cx = x as f32 * xinc; + // let cy = y as f32 * yinc; + // let cz = 1.0; triangles.push((Point::new(cx, cy, cz + 1.0), Point::new(cx + xinc, cy, cz + 1.1), Point::new(cx, cy + yinc, cz + 1.2))); @@ -137,7 +141,8 @@ fn main() { offset + si as u32), 1.5), 0.0), - Vector::new(0.0, 0.0, 1.0)); + Vector::new(0.0, 0.0, 1.0), + 0.0); ray.id = si as u32; rays.push(ray); isects.push((false, 0.0, 0.0)); diff --git a/src/ray.rs b/src/ray.rs index d44d822..ef1cc24 100644 --- a/src/ray.rs +++ b/src/ray.rs @@ -17,13 +17,13 @@ pub struct Ray { } impl Ray { - pub fn new(orig: Point, dir: Vector) -> Ray { + pub fn new(orig: Point, dir: Vector, time: f32) -> Ray { Ray { orig: orig, dir: dir, dir_inv: Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / dir.co }, max_t: std::f32::INFINITY, - time: 0.0, + time: time, id: 0, flags: 0, } @@ -32,5 +32,6 @@ impl Ray { pub fn transform(&mut self, mat: &Matrix4x4) { self.orig = self.orig * *mat; self.dir = self.dir * *mat; + self.dir_inv = Vector { co: Float4::new(1.0, 1.0, 1.0, 1.0) / self.dir.co }; } }