1use super::*;
2
3impl Ray {
6 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 pub fn at(&self, t: f64) -> Vector3D {
39 self.get_origin() + self.get_direction().scaled(t)
40 }
41
42 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
62impl Occluder {
64 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 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 pub fn occluder_points(&self) -> Vec<(Vector3D, f64)> {
115 let (mn, mx): (Vector3D, Vector3D) = occluder_aabb_extents(self);
116 let cx: f64 = (mn.get_x() + mx.get_x()) * 0.5;
117 let cy: f64 = (mn.get_y() + mx.get_y()) * 0.5;
118 let cz: f64 = (mn.get_z() + mx.get_z()) * 0.5;
119 let ex: f64 = (mx.get_x() - mn.get_x()) * 0.5;
120 let ey: f64 = (mx.get_y() - mn.get_y()) * 0.5;
121 let ez: f64 = (mx.get_z() - mn.get_z()) * 0.5;
122 let r: f64 = (ex * ex + ey * ey + ez * ez).sqrt();
123 vec![(Vector3D::new(cx, cy, cz), r)]
124 }
125}
126
127#[cfg(test)]
128mod tests {
129 use super::*;
130
131 #[test]
133 fn trace_miss_returns_ambient() {
134 let eye: Vector3D = Vector3D::new(0.0, 0.0, 0.0);
135 let mut lights: LightingUniforms = LightingUniforms::with_eye(eye);
136 lights.set_ambient(Vector3D::new(0.2, 0.4, 0.6));
137 let ray: Ray = Ray::new(Vector3D::new(0.0, 0.0, 0.0), Vector3D::new(1.0, 0.0, 0.0));
138 let occluders: [Occluder; 0] = [];
139 let color: Vector3D = trace(ray, &occluders, &lights, RAYTRACE_DEFAULT_MAX_BOUNCES);
140 assert!(
141 (color.get_x() - 0.2).abs() < EPSILON,
142 "expected ambient red 0.2, got {}",
143 color.get_x(),
144 );
145 assert!(
146 (color.get_y() - 0.4).abs() < EPSILON,
147 "expected ambient green 0.4, got {}",
148 color.get_y(),
149 );
150 assert!(
151 (color.get_z() - 0.6).abs() < EPSILON,
152 "expected ambient blue 0.6, got {}",
153 color.get_z(),
154 );
155 }
156
157 #[test]
160 fn trace_emissive_sphere() {
161 let eye: Vector3D = Vector3D::new(0.0, 0.0, 5.0);
162 let mut lights: LightingUniforms = LightingUniforms::with_eye(eye);
163 lights.set_ambient(Vector3D::zero());
164 let sphere_material: Material = Material::emissive(Vector3D::new(1.0, 0.0, 0.0));
165 let sphere: Occluder = Occluder::sphere(Vector3D::zero(), 1.0, sphere_material);
166 let ray: Ray = Ray::new(Vector3D::new(0.0, 0.0, 5.0), Vector3D::new(0.0, 0.0, -1.0));
167 let occluders: [Occluder; 1] = [sphere];
168 let color: Vector3D = trace(ray, &occluders, &lights, RAYTRACE_DEFAULT_MAX_BOUNCES);
169 assert!(
170 (color.get_x() - 1.0).abs() < EPSILON,
171 "expected emissive red 1.0, got {}",
172 color.get_x(),
173 );
174 assert!(
175 color.get_y().abs() < EPSILON,
176 "expected emissive green 0.0, got {}",
177 color.get_y(),
178 );
179 assert!(
180 color.get_z().abs() < EPSILON,
181 "expected emissive blue 0.0, got {}",
182 color.get_z(),
183 );
184 }
185
186 #[test]
189 fn trace_reflection_single_bounce() {
190 let eye: Vector3D = Vector3D::new(0.0, 0.0, 10.0);
191 let mut lights: LightingUniforms = LightingUniforms::with_eye(eye);
192 lights.set_ambient(Vector3D::zero());
193 let mirror_material: Material = Material::phong(Vector3D::zero(), 1.0, 32.0);
194 let mirror: Occluder = Occluder::sphere(Vector3D::zero(), 1.0, mirror_material);
195 let emissive_material: Material = Material::emissive(Vector3D::new(0.0, 1.0, 0.0));
199 let emissive: Occluder =
200 Occluder::sphere(Vector3D::new(0.0, 0.0, 15.0), 1.0, emissive_material);
201 let ray: Ray = Ray::new(Vector3D::new(0.0, 0.0, 10.0), Vector3D::new(0.0, 0.0, -1.0));
202 let occluders: [Occluder; 2] = [mirror, emissive];
203 let color: Vector3D = trace(ray, &occluders, &lights, RAYTRACE_DEFAULT_MAX_BOUNCES);
204 assert!(
205 color.get_y() > 0.0,
206 "expected bounce to bring back some green, got {}",
207 color.get_y(),
208 );
209 assert!(
210 color.get_x().abs() < EPSILON,
211 "expected red ~0 (no red light), got {}",
212 color.get_x(),
213 );
214 assert!(
215 color.get_z().abs() < EPSILON,
216 "expected blue ~0 (no blue light), got {}",
217 color.get_z(),
218 );
219 }
220}