camera: implement Camera::ray_for_pixel.

This commit is contained in:
Bill Thiede 2021-07-17 21:34:09 -07:00
parent 39f7f77b74
commit 5911610064

View File

@ -1,10 +1,10 @@
use crate::matrices::Matrix4x4;
use crate::{matrices::Matrix4x4, rays::Ray, tuples::Tuple};
pub struct Camera {
hsize: usize,
vsize: usize,
field_of_view: f32,
transform: Matrix4x4,
pub transform: Matrix4x4,
pixel_size: f32,
half_width: f32,
half_height: f32,
@ -70,4 +70,55 @@ impl Camera {
pub fn pixel_size(&self) -> f32 {
self.pixel_size
}
/// Calculate ray that starts at the camera and passes through the (x,y)
/// pixel on the canvas.
///
/// # Examples
/// ```
/// use std::f32::consts::PI;
///
/// use rtchallenge::{camera::Camera, matrices::Matrix4x4, tuples::Tuple};
///
/// // Constructing a ray through the center of the canvas.
/// let c = Camera::new(201, 101, PI / 2.);
/// let r = c.ray_for_pixel(100, 50);
/// assert_eq!(r.origin, Tuple::point(0., 0., 0.));
/// assert_eq!(r.direction, Tuple::vector(0., 0., -1.));
///
/// // Constructing a ray through the corner of the canvas.
/// let c = Camera::new(201, 101, PI / 2.);
/// let r = c.ray_for_pixel(0, 0);
/// assert_eq!(r.origin, Tuple::point(0., 0., 0.));
/// assert_eq!(r.direction, Tuple::vector(0.66519, 0.33259, -0.66851));
///
/// // 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.);
/// let r = c.ray_for_pixel(100, 50);
/// assert_eq!(r.origin, Tuple::point(0., 2., -5.));
/// assert_eq!(
/// r.direction,
/// Tuple::vector(2_f32.sqrt() / 2., 0., -2_f32.sqrt() / 2.)
/// );
/// ```
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 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 direction = (pixel - origin).normalize();
Ray::new(origin, direction)
}
}