rust_pathtracer/
scene.rs

1use crate::prelude::*;
2
3/// A trait based scene abstraction.
4#[allow(unused)]
5pub trait Scene : Sync + Send {
6
7    fn new() -> Self where Self: Sized;
8
9    /// Background color for the given ray
10    fn background(&self, ray: &Ray) -> F3;
11
12    /// Closest hit should return the state.hit_dist, state.normal and fill out the state.material as needed
13    fn closest_hit(&self, ray: &Ray, state: &mut State, light: &mut LightSampleRec) -> bool;
14
15    /// Used for shadow rays.
16    fn any_hit(&self, ray: &Ray, max_dist: F) -> bool;
17
18    /// Return the camera for the scene
19    fn camera(&self) -> &Box<dyn Camera3D>;
20
21    /// Return the number of lights in the scene
22    fn number_of_lights(&self) -> usize;
23
24    /// Return a reference for the light at the given index
25    fn light_at(&self, index: usize) -> &AnalyticalLight;
26
27    /// The recursion depth for the path tracer
28    fn recursion_depth(&self) -> u16 {
29        4
30    }
31
32    fn to_linear(&self, c: F3) -> F3 {
33        F3::new(c.x.powf(2.2), c.y.powf(2.2), c.z.powf(2.2))
34    }
35
36    fn sample_lights(&self, ray: &Ray, state: &mut State, light_sample: &mut LightSampleRec, lights: &Vec<AnalyticalLight>) -> bool {
37
38        // Based on https://www.scratchapixel.com/lessons/3d-basic-rendering/minimal-ray-tracer-rendering-simple-shapes/ray-sphere-intersection
39        fn sphere(ray: &Ray, center: F3, radius: F) -> Option<F> {
40            let l = center - ray.origin;
41            let tca = l.dot(&ray.direction);
42            let d2 = l.dot(&l) - tca * tca;
43            let radius2 = radius * radius;
44            if d2 > radius2 {
45                return None;
46            }
47            let thc = (radius2 - d2).sqrt();
48            let mut t0 = tca - thc;
49            let mut t1 = tca + thc;
50
51            if t0 > t1 {
52                std::mem::swap(&mut t0, &mut t1);
53            }
54
55            if t0 < 0.0 {
56                t0 = t1;
57                if t0 < 0.0 {
58                    return None;
59                }
60            }
61
62            Some(t0)
63    }
64
65        let mut hit = false;
66        let mut dist = state.hit_dist;
67
68        for light in lights {
69            if light.light.light_type == LightType::Spherical {
70                if let Some(d) = sphere(ray, light.light.position, light.light.radius) {
71                    if d < dist {
72                        dist = d;
73                        let hit_point = ray.at(&d);
74                        let cos_theta = dot(&-ray.direction, &normalize(&(hit_point - light.light.position)));
75                        light_sample.pdf = (dist * dist) / (light.light.area * cos_theta * 0.5);
76                        light_sample.emission = light.light.emission;
77                        state.is_emitter = true;
78                        state.hit_dist = d;
79                        hit = true;
80                    }
81                }
82            }
83        }
84
85        hit
86    }
87
88    fn as_any(&mut self) -> &mut dyn std::any::Any;
89
90}