solstrale/
camera.rs

1//! Provides a camera used by raytracer to shoot rays into the scene
2
3use crate::geo::vec3::{random_in_unit_disc, Vec3, ZERO_VECTOR};
4use crate::geo::{Ray, Uv};
5use crate::util::degrees_to_radians;
6
7/// Contains all needed parameters for constructing a camera
8pub struct CameraConfig {
9    /// Vertical field of view in degrees
10    pub vertical_fov_degrees: f64,
11    /// Radius of the lens of the camera, affects the depth of field
12    pub aperture_size: f64,
13    /// Point where the camera is located
14    pub look_from: Vec3,
15    /// Point where the camera is looking
16    pub look_at: Vec3,
17    /// Direction pointing "up" for the camera
18    pub up: Vec3,
19}
20
21impl Default for CameraConfig {
22    fn default() -> Self {
23        CameraConfig {
24            vertical_fov_degrees: 50.0,
25            aperture_size: 0.0,
26            look_from: ZERO_VECTOR,
27            look_at: ZERO_VECTOR,
28            up: Vec3::new(0., 1., 0.),
29        }
30    }
31}
32
33/// Contains all data needed to describe a cameras position, field of view and
34/// where it is pointing
35pub struct Camera {
36    origin: Vec3,
37    lower_left_corner: Vec3,
38    horizontal: Vec3,
39    vertical: Vec3,
40    u: Vec3,
41    v: Vec3,
42    lens_radius: f64,
43}
44
45impl Camera {
46    /// Create a new camera instance
47    pub fn new(image_width: usize, image_height: usize, c: &CameraConfig) -> Camera {
48        let aspect_ratio = image_width as f64 / image_height as f64;
49        let theta = degrees_to_radians(c.vertical_fov_degrees);
50        let h = (theta / 2.).tan();
51        let view_port_height = 2. * h;
52        let view_port_width = aspect_ratio * view_port_height;
53
54        let look_v = c.look_from - c.look_at;
55        let focus_distance = look_v.length();
56        let w = look_v.unit();
57        let u = c.up.unit().cross(w).unit();
58        let v = w.cross(u);
59
60        let horizontal = (u * view_port_width) * focus_distance;
61        let vertical = (v * view_port_height) * focus_distance;
62        let lower_left_corner =
63            c.look_from - (horizontal / 2.) - (vertical / 2.) - (w * focus_distance);
64
65        Camera {
66            origin: c.look_from,
67            lower_left_corner,
68            horizontal,
69            vertical,
70            u,
71            v,
72            lens_radius: c.aperture_size / 2.,
73        }
74    }
75
76    /// A function for generating a ray for a certain u/v for the raytraced image
77    pub fn get_ray(&self, uv: Uv) -> Ray {
78        let offset = if self.lens_radius > 0. {
79            let rd = random_in_unit_disc() * self.lens_radius;
80            self.u * rd.x + self.v * rd.y
81        } else {
82            ZERO_VECTOR
83        };
84
85        let r_dir = self.lower_left_corner + (self.horizontal * uv.u) + (self.vertical * uv.v)
86            - self.origin
87            - offset;
88        Ray::new(self.origin + offset, r_dir)
89    }
90}