bs_trace/trace/
camera.rs

1use super::prelude::*;
2use crate::image::prelude::*;
3use crate::linalg::ray::Ray;
4use crate::trace::sampling::SamplingStrategy;
5use crate::trace::world::World;
6use rand::RngCore;
7use std::sync::mpsc::Receiver;
8use std::sync::{mpsc, Arc, Mutex};
9use std::thread;
10use std::thread::JoinHandle;
11
12/// A camera in 3D space, with an associated viewport.
13///
14/// The const generic parameters `W` and `H` represent the width and height of the viewport in
15/// pixels.
16pub struct Camera<const W: usize, const H: usize> {
17    aspect_ratio: f64,
18    origin: Vec3,
19    right: Vec3,
20    up: Vec3,
21    forward: Vec3,
22    viewport_top_left: Vec3,
23    pub gamma: f64,
24}
25
26impl<const W: usize, const H: usize> Camera<W, H> {
27    /// Constructs a camera with the given origin, orientation and focal length.
28    ///
29    /// # Safety
30    ///
31    /// It is the responsibility of the caller to ensure that the `right` and `up` vectors are
32    /// orthonormal.
33    pub unsafe fn new_unchecked(
34        origin: Vec3,
35        right: Vec3,
36        up: Vec3,
37        focal_length: f64,
38        gamma: f64,
39    ) -> Self {
40        let aspect_ratio = W as f64 / H as f64;
41        let forward = right.cross(up);
42
43        let viewport_middle = origin + forward * focal_length;
44        let viewport_top_left = viewport_middle - right * 0.5 + up * 0.5 / aspect_ratio;
45
46        Self {
47            aspect_ratio,
48            origin,
49            right,
50            up,
51            forward,
52            viewport_top_left,
53            gamma,
54        }
55    }
56
57    pub fn sampling_rays_for_pixel(
58        &self,
59        rng: &mut Box<dyn RngCore>,
60        sampling_strategy: Arc<dyn SamplingStrategy>,
61        x: usize,
62        y: usize,
63    ) -> Vec<Ray<f64, 3>> {
64        sampling_strategy
65            .get_sample_positions(rng, x, y)
66            .into_iter()
67            .map(|(x, y)| {
68                let direction =
69                    self.viewport_top_left + self.right * x - self.up * y / self.aspect_ratio;
70                Ray::new(self.origin, direction)
71            })
72            .collect()
73    }
74
75    fn trace_sample(&self, rng: &mut Box<dyn RngCore>, world: &World, x: f64, y: f64) -> Colour {
76        let direction = self.viewport_top_left + self.right * x - self.up * y / self.aspect_ratio;
77        let ray = Ray::new(self.origin, direction);
78        world.trace(rng, &ray)
79    }
80
81    fn spawn_threads<S: SamplingStrategy + Clone + 'static>(
82        &'static self,
83        sampling_strategy: S,
84        world: Arc<World>,
85        num_threads: usize,
86    ) -> (Vec<JoinHandle<()>>, Receiver<Vec<Colour>>) {
87        let (tx, rx) = mpsc::channel::<Vec<Colour>>();
88        let lines_numbers = Arc::new(Mutex::new(0..H));
89        let join_handles = (0..num_threads).map(|_| {
90            let line_nums = lines_numbers.clone();
91            let tx = tx.clone();
92            let sampling_strategy = sampling_strategy.clone();
93        });
94        thread::spawn(move || {
95            let mut rng: Box<dyn RngCore> = Box::new(rand::thread_rng());
96            world.trace(&mut rng, &Ray::new(self.origin, self.forward));
97        });
98        todo!()
99    }
100}