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}