1use crate::geo::vec3::{random_in_unit_disc, Vec3, ZERO_VECTOR};
4use crate::geo::{Ray, Uv};
5use crate::util::degrees_to_radians;
6
7pub struct CameraConfig {
9 pub vertical_fov_degrees: f64,
11 pub aperture_size: f64,
13 pub look_from: Vec3,
15 pub look_at: Vec3,
17 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
33pub 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 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 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}