aboutsummaryrefslogtreecommitdiff
path: root/src/camera/camera.rs
blob: 69fd4e0ca654c6860e2e252004e8c094087d0e76 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
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
            )
    }
}