From 8296be848319eecd43f94900d4d12414ec189166 Mon Sep 17 00:00:00 2001 From: Julian T Date: Wed, 3 Feb 2021 15:34:17 +0100 Subject: Add simple perspective camera --- src/camera/camera.rs | 90 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/camera/mod.rs | 4 ++- src/core/vector3.rs | 29 ++++++++++++++++- src/lib.rs | 4 ++- src/main.rs | 13 ++++++++ 5 files changed, 137 insertions(+), 3 deletions(-) create mode 100644 src/camera/camera.rs diff --git a/src/camera/camera.rs b/src/camera/camera.rs new file mode 100644 index 0000000..69fd4e0 --- /dev/null +++ b/src/camera/camera.rs @@ -0,0 +1,90 @@ +//! Generates rays from screen coordinates +//! +//! Generates rays in world space from screen coordinates. +//! Future versions should also simulate depth of field. +//! +//! # Examples +//! +//! ``` +//! use pathtrace::camera::Camera; +//! use pathtrace::core::{Vector3f, Vector2f}; +//! +//! let cam = Camera::new( +//! Vector3f::new(10.0), +//! Vector3f::new(0.0), +//! Vector3f::new_xyz(0.0, 1.0, 0.0), +//! 90.0, Vector2f::new(10.0), +//! ); +//! +//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0)); +//! let dir = r.direction; +//! +//! assert!( +//! dir.x == -0.44792563 && +//! dir.y == -0.659974 && +//! dir.z == -0.6031559 +//! ); +//! +//! ``` +use crate::Float; +use crate::core::{Vector3f, Vector2f, Ray}; + +/// A simple perspective camera +pub struct Camera { + /// The camera origin in the screen + origin: Vector3f, + /// Vector from camera origin to the screen lower left corner + screen_origin: Vector3f, + /// Scaling vectors from screen_origin + qx: Vector3f, + qy: Vector3f, +} + +impl Camera { + /// Create a new camera look at a target + /// + /// The field of view specifies how wide the image should be. + /// Currently must be between [0; 180[. + pub fn new(origin: Vector3f, target: Vector3f, up: Vector3f, fov: Float, screensize: Vector2f) -> Camera { + // Calculate translation vectors + let forward = (target - origin).norm(); + let right = up.cross(&origin).norm(); + let newup = forward.cross(&right).norm(); + + // Calculate screen size from fov + let aspect = screensize.y / screensize.x; + let width = 2.0 * (fov / 2.0).to_radians().tan(); + let height = aspect * width; + + // Calculate screen scaling vectors + let qx = right * (width / (screensize.x - 1.0)); + let qy = newup * (height / (screensize.y - 1.0)); + + let screen_origin = forward - (right * (width/2.0)) - (newup * (height/2.0)); + + Camera { + origin, + screen_origin, + qx, + qy, + } + } + + /// Generates a ray a screen space point + /// + /// The point coordinates should be between [0,0] (lower left corner) and [screensize.x, + /// screensize.y] (upper right corner) + /// + /// Will return a ray and a weight + /// + /// The direction of the returned way is normalized + pub fn generate_ray(&self, point: &Vector2f) -> (Ray, Float) { + let mut dir = self.screen_origin + (self.qx * point.x) + (self.qy * point.y); + dir.norm_in(); + + ( + Ray { origin: self.origin, direction: dir }, + 1.0 + ) + } +} diff --git a/src/camera/mod.rs b/src/camera/mod.rs index 4865a36..b7c982b 100644 --- a/src/camera/mod.rs +++ b/src/camera/mod.rs @@ -7,4 +7,6 @@ pub mod film; //pub mod filter; -//pub mod camera; +pub mod camera; + +pub use camera::Camera; diff --git a/src/core/vector3.rs b/src/core/vector3.rs index 915765d..6f4d6ab 100644 --- a/src/core/vector3.rs +++ b/src/core/vector3.rs @@ -2,7 +2,8 @@ //! //! Also add more 3d math things needed for shading and 3d calculations. use crate::{Float, Number}; -use std::ops::{Sub, Add, DivAssign}; +use std::ops::{Mul, Sub, Add, DivAssign}; +use std::fmt; #[derive(Clone, Copy)] pub struct Vector3 { @@ -49,6 +50,17 @@ impl Add for Vector3 { } } +impl Mul for Vector3 { + type Output = Self; + fn mul(self, op: T) -> Self::Output { + Self::Output::new_xyz( + self.x * op, + self.y * op, + self.z * op, + ) + } +} + impl DivAssign for Vector3 { fn div_assign(&mut self, op: T) { self.x /= op; @@ -57,6 +69,12 @@ impl DivAssign for Vector3 { } } +impl fmt::Display for Vector3 { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_fmt(format_args!("[{}, {}, {}]", self.x, self.y, self.z)) + } +} + impl Vector3f { /// Calculates the length times itself /// @@ -97,4 +115,13 @@ impl Vector3f { new.norm_in(); new } + + pub fn cross(&self, op: &Self) -> Self { + Self::new_xyz( + self.y * op.z - self.z * op.y, + self.z * op.x - self.x * op.z, + self.x * op.y - self.y * op.x, + ) + + } } diff --git a/src/lib.rs b/src/lib.rs index df9bc1d..c3025b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ mod scene; use std::ops::{Add, Sub, Mul, DivAssign}; use std::cmp; +use std::fmt; /// Trait used to implement generics /// @@ -14,7 +15,8 @@ pub trait Number: Sub + Add + Mul + - DivAssign + DivAssign + + fmt::Display {} impl Number for i32 {} diff --git a/src/main.rs b/src/main.rs index 524f613..22998dd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,17 @@ +use pathtrace::camera::Camera; +use pathtrace::core::{Vector3f, Vector2f}; fn main() { + + let cam = Camera::new( + Vector3f::new(10.0), + Vector3f::new(0.0), + Vector3f::new_xyz(0.0, 1.0, 0.0), + 90.0, Vector2f::new(10.0), + ); + + let (r, _) = cam.generate_ray(Vector2f::new(5.0)); + + println!("r: {}, norm: {}", r.direction, r.direction.norm()); } -- cgit v1.2.3