aboutsummaryrefslogtreecommitdiff
path: root/src/camera/camera.rs
blob: e770efe5b7e761639b0387de737e9251d25b7690 (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
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Generates rays from screen coordinates
//!
//! Generates rays in world space from screen coordinates.
//! Future versions should also simulate depth of field.
//!
//! # Examples
//!
//! ```
//! use rendering::camera::{CameraSettings, Camera};
//! use rendering::core::{Vector3f, Vector2f, Vector2i};
//!
//! let set = CameraSettings {
//!     origin: Vector3f::new(10.0),
//!     target: Vector3f::new(0.0),
//!     up: Vector3f::new_xyz(0.0, 1.0, 0.0),
//!     fov: 90.0, 
//!     filmsize: Vector2i::new(10),
//!     focus: None,
//!     aperture: 0.0,
//! };
//!
//! let cam = Camera::new(&set);
//!
//! let (r, _) = cam.generate_ray(&Vector2f::new(5.0));
//! let dir = r.direction;
//!
//! assert!(
//!     dir.x == -0.6031558065478413 &&
//!     dir.y == -0.6599739684616743 &&
//!     dir.z == -0.4479257014065748
//!     );
//!
//! ```
use crate::Float;
use crate::core::{Vector3f, Vector2f, Vector2i, 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 of the film plane
    screen_origin: Vector3f,
    /// Scaling vectors from screen_origin
    qx: Vector3f,
    qy: Vector3f,
}

/// Settings for initializing camera
pub struct CameraSettings {
    /// Where rays originate from
    pub origin: Vector3f,
    /// Point where center of image is pointed at
    pub target: Vector3f,
    /// Vector that will be up in the resulting image
    pub up: Vector3f,
    /// The vertical field of view in degrees.
    /// Currently must be between [0; 180[.
    pub fov: Float,
    /// The film aspect ratio, height / width
    pub filmsize: Vector2i,
    /// The lens aperture
    pub aperture: Float,
    /// The distance to the focus plane
    ///
    /// if None it will be set to the distance between origin and target
    pub focus: Option<Float>,
}

impl Camera {
    /// Create a new camera look at a target
    pub fn new(set: &CameraSettings) -> Camera {
        let filmsize = Vector2f::from(set.filmsize);
        // Calculate translation vectors
        let mut forward = set.target - set.origin;

        let focus = set.focus.unwrap_or(forward.len());

        forward.norm_in();

        let right = set.up.cross(&forward).norm();
        let newup = forward.cross(&right).norm();

        let aspect = (filmsize.y) / (filmsize.x);
        // Calculate screen size from fov and focus distance
        let width = 2.0 * focus * (set.fov / 2.0).to_radians().tan();
        let height = aspect * width;

        // Calculate screen scaling vectors
        let qx = right * (width / (filmsize.x - 1.0));
        let qy = newup * (height / (filmsize.y - 1.0));

        let screen_origin = forward * focus - (right * (width/2.0)) + (newup * (height/2.0));

        Camera {
            origin: set.origin,
            screen_origin,
            qx,
            qy,
        }
    }

    /// Generates a ray a screen space point
    ///
    /// The point coordinates should be between [0,1) with (0, 0) being the upper let 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
            )
    }
}