Skip to main content

euv_engine/raytracing/
impl.rs

1use super::*;
2
3/// Implements factory constructors and accessors for [`Ray`] and
4/// [`Occluder`].
5impl Ray {
6    /// Creates a new ray starting at `origin` pointing in `direction`.
7    ///
8    /// `t_min` and `t_max` default to [`RAYTRACE_DEFAULT_T_MIN`] and
9    /// [`RAYTRACE_DEFAULT_T_MAX`]. `depth` defaults to 0.
10    ///
11    /// # Arguments
12    ///
13    /// - `Vector3D` - The ray origin.
14    /// - `Vector3D` - The unit direction.
15    ///
16    /// # Returns
17    ///
18    /// - `Ray` - The new ray.
19    pub fn new(origin: Vector3D, direction: Vector3D) -> Ray {
20        Ray {
21            origin,
22            direction,
23            t_min: RAYTRACE_DEFAULT_T_MIN,
24            t_max: RAYTRACE_DEFAULT_T_MAX,
25            depth: 0,
26        }
27    }
28
29    /// Computes the world-space point at distance `t` along this ray.
30    ///
31    /// # Arguments
32    ///
33    /// - `f64` - The ray parameter.
34    ///
35    /// # Returns
36    ///
37    /// - `Vector3D` - `origin + direction * t`.
38    pub fn at(&self, t: f64) -> Vector3D {
39        self.get_origin() + self.get_direction().scaled(t)
40    }
41
42    /// Returns a clone of this ray with `depth` replaced by `depth`.
43    ///
44    /// # Arguments
45    ///
46    /// - `u32` - The new recursion depth.
47    ///
48    /// # Returns
49    ///
50    /// - `Ray` - The cloned ray with updated depth.
51    pub fn with_depth(&self, depth: u32) -> Ray {
52        Ray {
53            origin: self.get_origin(),
54            direction: self.get_direction(),
55            t_min: self.get_t_min(),
56            t_max: self.get_t_max(),
57            depth,
58        }
59    }
60}
61
62/// Implements factory constructors for [`Occluder`].
63impl Occluder {
64    /// Creates a spherical occluder centered at `center` with `radius`.
65    ///
66    /// # Arguments
67    ///
68    /// - `Vector3D` - The sphere center.
69    /// - `f64` - The sphere radius.
70    /// - `Material` - The surface material.
71    ///
72    /// # Returns
73    ///
74    /// - `Occluder` - The new sphere occluder.
75    pub fn sphere(center: Vector3D, radius: f64, material: Material) -> Occluder {
76        Occluder {
77            kind: OccluderKind::Sphere,
78            center,
79            extent: Vector3D::new(radius, radius, radius),
80            material,
81        }
82    }
83
84    /// Creates an axis-aligned bounding-box occluder from `min` to `max`.
85    ///
86    /// # Arguments
87    ///
88    /// - `Vector3D` - The AABB minimum corner.
89    /// - `Vector3D` - The AABB maximum corner.
90    /// - `Material` - The surface material.
91    ///
92    /// # Returns
93    ///
94    /// - `Occluder` - The new AABB occluder.
95    pub fn aabb(min: Vector3D, max: Vector3D, material: Material) -> Occluder {
96        Occluder {
97            kind: OccluderKind::Aabb,
98            center: min,
99            extent: max,
100            material,
101        }
102    }
103
104    /// Returns a list of `(center, radius)` sphere tuples approximating
105    /// this occluder, suitable for [`soft_shadow_factor`].
106    ///
107    /// For sphere occluders this returns `(center, radius)`. For AABB
108    /// occluders the bounding sphere is computed conservatively from the
109    /// AABB extents.
110    ///
111    /// # Returns
112    ///
113    /// - `Vec<(Vector3D, f64)>` - One bounding sphere per occluder.
114    pub fn occluder_points(&self) -> Vec<(Vector3D, f64)> {
115        collect_occluder_points(std::slice::from_ref(self))
116    }
117}
118
119/// Implements the constructor and zero-allocation tracing entry points for
120/// [`RayTraceScene`].
121impl RayTraceScene {
122    /// Creates a new scene taking ownership of `occluders` and precomputing
123    /// the `(center, radius)` shadow bounding spheres used by
124    /// [`soft_shadow_factor`].
125    ///
126    /// # Arguments
127    ///
128    /// - `Vec<Occluder>` - All occluding surfaces in the scene.
129    ///
130    /// # Returns
131    ///
132    /// - `RayTraceScene` - The new scene with precomputed shadow data.
133    pub fn new(occluders: Vec<Occluder>) -> RayTraceScene {
134        let shadow_points: Vec<(Vector3D, f64)> = collect_occluder_points(&occluders);
135        RayTraceScene {
136            occluders,
137            shadow_points,
138        }
139    }
140
141    /// Iteratively traces a ray through the scene and returns the final
142    /// shaded color, using the [`RAYTRACE_DEFAULT_MAX_BOUNCES`] constant as
143    /// the bounce limit.
144    ///
145    /// Performs no heap allocation per ray or per bounce: the shadow
146    /// bounding spheres precomputed at construction are reused, and no
147    /// [`Material`] is cloned. Use [`RayTraceScene::trace_with_bounces`] to
148    /// override the bounce limit.
149    ///
150    /// # Arguments
151    ///
152    /// - `Ray` - The ray to trace.
153    /// - `&LightingUniforms` - Lighting parameters used during shading.
154    ///
155    /// # Returns
156    ///
157    /// - `Vector3D` - The final traced color.
158    pub fn trace(&self, ray: Ray, lights: &LightingUniforms) -> Vector3D {
159        self.trace_with_bounces(ray, lights, RAYTRACE_DEFAULT_MAX_BOUNCES)
160    }
161
162    /// Iteratively traces a ray through the scene with an explicit bounce
163    /// limit and returns the final shaded color.
164    ///
165    /// On a miss the ambient color scaled by the accumulated specular
166    /// throughput is added. On a hit the surface material is evaluated with
167    /// [`LightingUniforms::shade`] and, when the hit material has a
168    /// non-zero specular component, the trace continues with a reflected
169    /// ray up to `max_bounces` times (incrementing the ray's `depth` field
170    /// per bounce).
171    ///
172    /// # Arguments
173    ///
174    /// - `Ray` - The ray to trace.
175    /// - `&LightingUniforms` - Lighting parameters used during shading.
176    /// - `u32` - The maximum number of bounces allowed for this ray.
177    ///
178    /// # Returns
179    ///
180    /// - `Vector3D` - The final traced color.
181    pub fn trace_with_bounces(
182        &self,
183        ray: Ray,
184        lights: &LightingUniforms,
185        max_bounces: u32,
186    ) -> Vector3D {
187        trace_bounces(
188            ray,
189            self.get_occluders(),
190            self.get_shadow_points(),
191            lights,
192            max_bounces,
193        )
194    }
195
196    /// Finds the closest intersection between a ray and the scene
197    /// occluders.
198    ///
199    /// The winning occluder's [`Material`] is cloned exactly once, when the
200    /// returned [`Hit`] is constructed; losing candidates are never cloned.
201    ///
202    /// # Arguments
203    ///
204    /// - `&Ray` - The ray to test.
205    ///
206    /// # Returns
207    ///
208    /// - `Option<Hit>` - The closest hit, or `None` if the ray misses.
209    pub fn closest_hit(&self, ray: &Ray) -> Option<Hit> {
210        let occluders: &[Occluder] = self.get_occluders();
211        closest_hit_indexed(ray, occluders).map(
212            |(index, t, position, normal): (usize, f64, Vector3D, Vector3D)| Hit {
213                t,
214                position,
215                normal,
216                material: occluders[index].get_material().clone(),
217            },
218        )
219    }
220}