diff options
author | Julian T <julian@jtle.dk> | 2021-02-03 15:34:17 +0100 |
---|---|---|
committer | Julian T <julian@jtle.dk> | 2021-02-03 15:34:17 +0100 |
commit | 8296be848319eecd43f94900d4d12414ec189166 (patch) | |
tree | d0adad5c05692a55e2957d02eb62666bd45e011f /src/camera/camera.rs | |
parent | 32e719a517a6fea113f3e66e350c9aa60ddd98b9 (diff) |
Add simple perspective camera
Diffstat (limited to 'src/camera/camera.rs')
-rw-r--r-- | src/camera/camera.rs | 90 |
1 files changed, 90 insertions, 0 deletions
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 + ) + } +} |