From 5911610064f838e70be9a881babb5b6b971b2a87 Mon Sep 17 00:00:00 2001 From: Bill Thiede Date: Sat, 17 Jul 2021 21:34:09 -0700 Subject: [PATCH] camera: implement Camera::ray_for_pixel. --- rtchallenge/src/camera.rs | 55 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 53 insertions(+), 2 deletions(-) diff --git a/rtchallenge/src/camera.rs b/rtchallenge/src/camera.rs index 55a8604..8c005f8 100644 --- a/rtchallenge/src/camera.rs +++ b/rtchallenge/src/camera.rs @@ -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) + } }