Skip to main content

euv_engine/lighting/
struct.rs

1use super::*;
2
3/// A light source contributing illumination to a shaded point.
4///
5/// The fields have meanings that depend on [`LightType`]. Directions point
6/// **toward the light source** except for the spot cone axis, which points
7/// outward along the beam; see [`LightType`] for the full convention.
8///
9/// - For [`LightType::Directional`], `position` is unused and `direction` is
10///   the unit vector from the surface toward the light.
11/// - For [`LightType::Point`], `position` is the world position of the light
12///   and `direction` is unused.
13/// - For [`LightType::Spot`], `position` is the lamp position, `direction`
14///   is the cone axis pointing outward along the beam, and `spot_cos`
15///   carries `cos(half_angle)` of the cone.
16///
17/// A light is never mutated during shading:
18/// [`LightingUniforms::shade_with_visibility`] reads it and derives
19/// per-point quantities into locals.
20#[derive(Clone, Data, Debug, New, PartialEq)]
21pub struct Light {
22    /// The kind of light source (directional, point, or spot).
23    #[get(type(copy))]
24    #[get_mut(pub(crate))]
25    #[set(pub(crate))]
26    pub kind: LightType,
27    /// The world position of the light.
28    #[get(type(copy))]
29    #[get_mut(pub(crate))]
30    #[set(pub(crate))]
31    pub position: Vector3D,
32    /// Unit direction. Toward the light for [`LightType::Directional`];
33    /// the outward cone axis for [`LightType::Spot`]; unused for
34    /// [`LightType::Point`].
35    #[get(type(copy))]
36    #[get_mut(pub(crate))]
37    #[set(pub(crate))]
38    pub direction: Vector3D,
39    /// RGB intensity multiplier applied to the contribution.
40    #[get(pub(crate), type(copy))]
41    #[get_mut(pub(crate))]
42    #[set(pub(crate))]
43    pub(crate) color: Vector3D,
44    /// Overall intensity scalar in the range 0.0..=infinity.
45    #[get(type(copy))]
46    #[get_mut(pub(crate))]
47    #[set(pub(crate))]
48    pub intensity: f64,
49    /// Inverse-square falloff factor for point and spot lights.
50    ///
51    /// `pub` rather than `pub(crate)`: the getter is an out-of-crate
52    /// consumer's entry point (`tests/`, and the ray tracer's data stage),
53    /// and a narrowed field behind a public getter would silently
54    /// re-expose it.
55    #[get(type(copy))]
56    pub falloff: f64,
57    /// `cos(half_angle)` for spot lights; zero for other kinds.
58    #[get(type(copy))]
59    #[get_mut(pub(crate))]
60    #[set(pub(crate))]
61    pub spot_cos: f64,
62}
63
64/// A material describing how a surface responds to illumination.
65#[derive(Clone, Data, Debug, New, PartialEq)]
66pub struct Material {
67    /// The shading model applied during evaluation.
68    #[get(pub(crate), type(copy))]
69    #[get_mut(pub(crate))]
70    #[set(pub(crate))]
71    pub(crate) kind: MaterialKind,
72    /// The base reflectance color in the range 0.0..=1.0 per channel.
73    #[get(pub(crate), type(copy))]
74    #[get_mut(pub(crate))]
75    #[set(pub(crate))]
76    pub(crate) albedo: Vector3D,
77    /// Specular intensity multiplier in the range 0.0..=1.0.
78    #[get(pub(crate), type(copy))]
79    #[get_mut(pub(crate))]
80    #[set(pub(crate))]
81    pub(crate) specular: f64,
82    /// Phong specular exponent; larger values produce tighter highlights.
83    #[get(pub(crate), type(copy))]
84    #[get_mut(pub(crate))]
85    #[set(pub(crate))]
86    pub(crate) shininess: f64,
87    /// Self-illumination term used when a surface is also a light source
88    /// (e.g. an emissive sphere visible through ray tracing).
89    #[get(pub(crate), type(copy))]
90    #[get_mut(pub(crate))]
91    #[set(pub(crate))]
92    pub(crate) emissive: Vector3D,
93}
94
95/// One light's contribution at a single shaded point, resolved once and
96/// then shared by every term that light feeds.
97///
98/// A [`Light`] stores the **authored** values: the intensity before any
99/// falloff, and — for a spot light — the outward cone axis rather than the
100/// direction toward the lamp. Shading needs different numbers: the
101/// surface-to-light direction and the intensity after falloff. Deriving
102/// them into locals keeps the [`Light`] itself un-cloned, but it leaves
103/// each term reading a bare pair of `Vector3D` / `f64` parameters whose
104/// roles no longer match the field names they came from.
105///
106/// A `LightSample` names the derived pair next to the colour they apply
107/// to, so [`lambert_term`] and [`phong_specular_term`] take one borrowed
108/// argument instead of three loose ones and neither rebuilds what the
109/// shading loop already measured. The caller still decides the values:
110/// [`compute_lambert`] samples the authored direction and intensity
111/// directly, while [`LightingUniforms::shade_with_visibility`] samples the
112/// derived direction and the falloff-attenuated intensity.
113///
114/// Every field is a `Copy` scalar or vector, so a sample is built on the
115/// stack and never allocates.
116#[derive(Clone, Copy, Debug, Getter)]
117pub(crate) struct LightSample {
118    /// The light's RGB color.
119    #[get(pub(crate), type(copy))]
120    pub(crate) color: Vector3D,
121    /// The intensity, already scaled by any falloff the caller applied.
122    #[get(pub(crate), type(copy))]
123    pub(crate) intensity: f64,
124    /// The unit direction from the shaded surface toward the light.
125    #[get(pub(crate), type(copy))]
126    pub(crate) direction: Vector3D,
127}
128
129/// The bounded segment a shadow ray travels: a start, a unit direction, and
130/// the length of the leg it covers.
131///
132/// [`soft_shadow_factor`] measures all three once per light and then tests
133/// every occluder sphere against the same segment, which is what keeps the
134/// open-interval bounds identical across the loop — [`EPSILON`] at the near
135/// end and `distance - EPSILON` at the far end, so a root landing exactly
136/// on an endpoint still counts as a miss.
137///
138/// The triple is declared here rather than borrowed from the ray tracer. A
139/// ray-tracing `Ray` carries `t_min` / `t_max` / `depth` for scene
140/// traversal, while a shadow leg here is defined by the two endpoints the
141/// light and the shaded point supply; naming that pair as one value keeps
142/// `lighting` free of a dependency on `raytracing` and of the cycle a
143/// shared `Ray` would risk forming.
144#[derive(Clone, Copy, Debug, Getter)]
145pub(crate) struct ShadowSegment {
146    /// The shaded point the segment starts from.
147    #[get(pub(crate), type(copy))]
148    pub(crate) origin: Vector3D,
149    /// The unit direction from `origin` toward the light.
150    #[get(pub(crate), type(copy))]
151    pub(crate) direction: Vector3D,
152    /// The distance from `origin` to the light.
153    #[get(pub(crate), type(copy))]
154    pub(crate) distance: f64,
155}
156
157/// Bundles of uniforms supplied to lighting and shading routines.
158#[derive(Clone, Data, Debug, New, PartialEq)]
159pub struct LightingUniforms {
160    /// All lights contributing to the scene.
161    #[get(pub(crate))]
162    #[get_mut(pub(crate))]
163    #[set(pub(crate))]
164    pub(crate) lights: Vec<Light>,
165    /// Ambient light contribution applied to every shaded point.
166    ///
167    /// The raytracing integration suites set the ambient term from outside
168    /// the crate, so the setter stays public; the getter and mut-getter have
169    /// no external consumer and stay crate-internal.
170    #[get(pub(crate), type(copy))]
171    #[get_mut(pub(crate))]
172    pub ambient: Vector3D,
173    /// View position used for specular term calculation.
174    #[get(pub(crate), type(copy))]
175    #[get_mut(pub(crate))]
176    #[set(pub(crate))]
177    pub(crate) eye: Vector3D,
178}