1use rand::{rngs::StdRng, Rng};
2use rand_distr::UnitDisc;
3
4use crate::shape::Ray;
5
6#[derive(Copy, Clone, Debug)]
8pub struct Camera {
9 pub eye: glm::DVec3,
11
12 pub direction: glm::DVec3,
14
15 pub up: glm::DVec3,
17
18 pub fov: f64,
20
21 pub aperture: f64,
23
24 pub focal_distance: f64,
26}
27
28impl Default for Camera {
29 fn default() -> Self {
30 Self {
31 eye: glm::vec3(0.0, 0.0, 10.0),
32 direction: glm::vec3(0.0, 0.0, -1.0),
33 up: glm::vec3(0.0, 1.0, 0.0), fov: std::f64::consts::FRAC_PI_6,
35 aperture: 0.0,
36 focal_distance: 0.0,
37 }
38 }
39}
40
41impl Camera {
42 pub fn look_at(eye: glm::DVec3, center: glm::DVec3, up: glm::DVec3, fov: f64) -> Self {
44 let direction = (center - eye).normalize();
45 let up = (up - up.dot(&direction) * direction).normalize();
46 Self {
47 eye,
48 direction,
49 up,
50 fov,
51 aperture: 0.0,
52 focal_distance: 0.0,
53 }
54 }
55
56 pub fn focus(mut self, focal_point: glm::DVec3, aperture: f64) -> Self {
58 self.focal_distance = (focal_point - self.eye).dot(&self.direction);
59 self.aperture = aperture;
60 self
61 }
62
63 pub fn cast_ray(&self, x: f64, y: f64, rng: &mut StdRng) -> Ray {
65 let d = (self.fov / 2.0).tan().recip();
67 let right = glm::cross(&self.direction, &self.up).normalize();
68 let mut origin = self.eye;
69 let mut new_dir = d * self.direction + x * right + y * self.up;
70 if self.aperture > 0.0 {
71 let focal_point = origin + new_dir.normalize() * self.focal_distance;
73 let [x, y]: [f64; 2] = rng.sample(UnitDisc);
74 origin += (x * right + y * self.up) * self.aperture;
75 new_dir = focal_point - origin;
76 }
77 Ray {
78 origin,
79 dir: new_dir.normalize(),
80 }
81 }
82}