1use rayon::{slice::ParallelSliceMut, iter::{IndexedParallelIterator, ParallelIterator}};
2use rand::{thread_rng, Rng, rngs::ThreadRng};
3use crate::prelude::*;
4
5pub struct Tracer {
6 eps : F,
7
8 scene : Box<dyn Scene>,
9}
10
11impl Tracer {
12
13 pub fn new(scene: Box<dyn Scene>) -> Self {
14 Self {
15
16 eps : 0.005,
17 scene,
18 }
19 }
20
21 pub fn render(&mut self, buffer: &mut ColorBuffer) {
23
24 const LINES: usize = 1;
25
26 let width = buffer.width;
27 let height = buffer.height as F;
28
29 buffer.pixels
30 .par_rchunks_exact_mut(width * LINES * 4)
31 .enumerate()
32 .for_each(|(j, line)| {
33 for (i, pixel) in line.chunks_exact_mut(4).enumerate() {
34 let i = j * width * LINES + i;
35
36 let x = (i % width) as F;
37 let y = height - (i / width) as F;
38
39 let xx = (x as F) / width as F;
40 let yy = ((y) as F) / height as F;
41
42 let mut rng: ThreadRng = thread_rng();
45 let cam_offset = F2::new(rng.gen(), rng.gen());
46 let coord = F2::new(xx, 1.0 - yy);
47 let mut ray = self.scene.camera().gen_ray(coord, cam_offset, width as F, height);
48
49 let mut radiance = F3::new(0.0, 0.0, 0.0);
52 let mut throughput = F3::new(1.0, 1.0, 1.0);
53 let mut state = State::new();
54 let mut light_sample = LightSampleRec::new();
55 let mut scatter_sample = ScatterSampleRec::new();
56
57 state.depth = self.scene.recursion_depth();
58
59 let alpha = 1.0;
60
61 for _i in 0..state.depth {
62
63 state.material = Material::new();
64 let hit = self.scene.closest_hit(&ray, &mut state, &mut light_sample);
65
66 if !hit {
67 radiance += self.scene.background(&ray) * throughput;
68 break;
69 }
70
71 state.finalize(&ray);
73
74 radiance += state.material.emission * throughput;
75
76 if state.is_emitter {
78 let mut mis_weight = 1.0;
79
80 if state.depth > 0 {
81 mis_weight = self.power_heuristic(&scatter_sample.pdf, &light_sample.pdf);
82 }
83
84 radiance += mis_weight * light_sample.emission * throughput;
85
86 break;
87 }
88
89 radiance += self.direct_light(&ray, &state, true, &mut rng) * throughput;
90
91 scatter_sample.f = self.disney_sample(&state, -ray.direction, &state.ffnormal, &mut scatter_sample.l, &mut scatter_sample.pdf, &mut rng);
93 if scatter_sample.pdf > 0.0 {
94 throughput = throughput * (scatter_sample.f / F3::new_x(scatter_sample.pdf));
95 } else {
96 break;
97 }
98
99 ray.direction = scatter_sample.l;
101 ray.origin = state.fhp + self.eps * ray.direction;
102
103 }
104
105 let color = [radiance.x, radiance.y, radiance.z, alpha];
106
107 #[inline(always)]
108 pub fn mix_color(a: &[F], b: &[F], v: F) -> [F; 4] {
109 [ (1.0 - v) * a[0] + b[0] * v,
110 (1.0 - v) * a[1] + b[1] * v,
111 (1.0 - v) * a[2] + b[2] * v,
112 (1.0 - v) * a[3] + b[3] * v ]
113 }
114
115 let mix = mix_color(pixel, &color, 1.0 / (buffer.frames + 1) as F);
116
117 pixel.copy_from_slice(&mix);
118 }
119 });
120
121 buffer.frames += 1;
122
123 }
124
125 fn direct_light(&self, ray: &Ray, state: &State, _is_surface: bool, rng: &mut ThreadRng) -> F3 {
127
128 let mut ld = F3::zeros();
129 let li;
130
131 let scatter_pos = state.fhp + self.eps * state.ffnormal;
132 let mut scatter_sample = ScatterSampleRec::new();
133
134 let number_lights = self.scene.number_of_lights();
135
136 if number_lights > 0 {
137 let mut random : F = rng.gen();
138 random *= self.scene.number_of_lights() as F;
139 let index = random as usize;
140
141 let analytical = self.scene.light_at(index);
142
143 let mut light_sample = LightSampleRec::new();
144
145 self.sample_light(&analytical.light, &scatter_pos, &mut light_sample, rng);
146 li = light_sample.emission;
147
148 if dot(&light_sample.direction, &light_sample.normal) < 0.0 {let shadow_ray: Ray = Ray::new(scatter_pos, light_sample.direction);
151
152 let in_shawdow = self.scene.any_hit(&shadow_ray, light_sample.dist - self.eps);
153
154 if in_shawdow == false {
155 scatter_sample.f = self.disney_eval(state, -ray.direction, &state.ffnormal, &light_sample.direction, &mut scatter_sample.pdf);
156
157 let mut mis_weight = 1.0;
158 if analytical.light.area > 0.0 {mis_weight = self.power_heuristic(&light_sample.pdf, &scatter_sample.pdf);
160 }
161
162 if scatter_sample.pdf > 0.0 {
163 ld += mis_weight * li * (scatter_sample.f / F3::new_x(light_sample.pdf));
164 }
165 }
166 }
167 }
168
169 ld
170 }
171
172 fn sample_light(&self, light: &Light, scatter_pos: &F3, light_sample: &mut LightSampleRec, rng: &mut ThreadRng) {
174
175 match light.light_type {
176 LightType::Spherical => {
177
178 fn uniform_sample_hemisphere(r1: &F, r2: &F) -> F3 {
179 let r = 0.0_f32.max(1.0 - r1 * r1).sqrt();
180 let phi = crate::TWO_PI * r2;
181 F3::new(r * phi.cos(), r * phi.sin(), *r1)
182 }
183
184 pub fn onb(n: F3, t: &mut F3, b: &mut F3) {
185 let up = if n.z.abs() < 0.999 { F3::new(0.0, 0.0, 1.0) } else { F3::new(1.0, 0.0, 0.0) };
186
187 *t = normalize(&cross(&up, &n));
188 *b = cross(&n, &t);
189 }
190
191 let r1 : F = rng.gen();
192 let r2 : F = rng.gen();
193
194 let mut sphere_center_to_surface = *scatter_pos - light.position;
195 let dist_to_sphere_center = length(&sphere_center_to_surface);
196 let mut sampled_dir = uniform_sample_hemisphere(&r1, &r2);
197
198 sphere_center_to_surface /= F3::new_x(dist_to_sphere_center);
199
200 let mut t = F3::new(0.0, 0.0, 0.0);
201 let mut b = F3::new(0.0, 0.0, 0.0);
202
203 onb(sphere_center_to_surface, &mut t, &mut b);
204 sampled_dir = sampled_dir.x * t + sampled_dir.y * b + sampled_dir.z * sphere_center_to_surface;
205
206 let light_surface_pos = light.position + light.radius * sampled_dir;
207
208 light_sample.direction = light_surface_pos - *scatter_pos;
209 light_sample.dist = length(&light_sample.direction);
210 let dist_sq = light_sample.dist * light_sample.dist;
211
212 light_sample.direction /= F3::new_x(light_sample.dist);
213 light_sample.normal = normalize(&(light_surface_pos - light.position));
214 light_sample.emission = self.scene.number_of_lights() as F * light.emission;
215 light_sample.pdf = dist_sq / (light.area * 0.5 * dot(&light_sample.normal, &light_sample.direction).abs());
216 },
217 _ => {},
218 }
219
220 }
221
222 #[inline(always)]
223 fn power_heuristic(&self, a: &F, b: &F) -> F {
224 let t = a * a;
225 return t / (b * b + t);
226 }
227
228 #[inline(always)]
229 fn mix_ptf(&self, a: &F, b: &F, v: F) -> F {
230 (1.0 - v) * a + b * v
231 }
232
233 fn gtr1(&self, ndoth: &F, a: F) -> F {
234 if a >= 1.0 {
235 return crate::INV_PI;
236 }
237 let a2 = a * a;
238 let t = 1.0 + (a2 - 1.0) * ndoth * ndoth;
239 return (a2 - 1.0) / (crate::PI * (a2).log2() * t);
240 }
241
242 fn sample_gtr1(&self, rgh: F, r1: F, _r2: F) -> F3 {
243 let a = 0.001_f32.max(rgh);
244 let a2 = a * a;
245
246 let phi = r1 * crate::TWO_PI;
247
248 let cos_theta = ((1.0 - a2.powf(1.0 - r1)) / (1.0 - a2)).sqrt();
249 let sin_theta = (1.0 - (cos_theta * cos_theta)).sqrt().clamp(0.0, 1.0);
250 let sin_phi = phi.sin();
251 let cos_phi = phi.cos();
252
253 F3::new(sin_theta * cos_phi, sin_theta * sin_phi, cos_theta)
254 }
255
256 fn sample_ggxvndf(&self, v: &F3, ax: F, ay: F, r1: F, r2: F) -> F3
257 {
258 let vh = normalize(&F3::new(ax * v.x, ay * v.y, v.z));
259
260 let lensq = vh.x * vh.x + vh.y * vh.y;
261 let t_1 = if lensq > 0.0 { F3::new(-vh.y, vh.x, 0.0).mult_f(&(1.0 / lensq.sqrt())) } else { F3::new(1.0, 0.0, 0.0) };
262 let t_2 = cross(&vh, &t_1);
263
264 let r = r1.sqrt();
265 let phi = 2.0 * crate::PI * r2;
266 let t1 = r * phi.cos();
267 let mut t2 = r * phi.sin();
268 let s = 0.5 * (1.0 + vh.z);
269 t2 = (1.0 - s) * (1.0 - t1 * t1).sqrt() + s * t2;
270
271 let nh = t1 * t_1 + t2 * t_2 + (0.0_f32.max(1.0 - t1 * t1 - t2 * t2)).sqrt() * vh;
272
273 normalize(&F3::new(ax * nh.x, ay * nh.y, 0.0_f32.max(nh.z)))
274 }
275
276 fn smithg(&self, ndotv: &F, alphag: F) -> F {
277 let a = alphag * alphag;
278 let b = ndotv * ndotv;
279 (2.0 * ndotv) / (ndotv + (a + b - a * b).sqrt())
280 }
281
282 fn luminance(&self, c: &F3) -> F {
285 0.212671 * c.x + 0.715160 * c.y + 0.072169 * c.z
286 }
287
288 fn schlick_fresnel(&self, u: F) -> F {
289 let m = (1.0 - u).clamp(0.0, 1.0);
290 let m2 = m * m;
291 m2 * m2 * m
292 }
293
294 fn gtr2aniso(&self, ndoth: &F, hdotx: &F, hdoty: &F, ax: &F, ay: &F) -> F {
295 let a = hdotx / ax;
296 let b = hdoty / ay;
297 let c = a * a + b * b + ndoth * ndoth;
298 1.0 / (crate::PI * ax * ay * c * c)
299 }
300
301 fn smithganiso(&self, ndotv: &F, vdotx: &F, vdoty: &F, ax: &F, ay: &F) -> F {
302 let a = vdotx * ax;
303 let b = vdoty * ay;
304 let c = ndotv;
305 (2.0 * ndotv) / (ndotv + (a * a + b * b + c * c).sqrt())
306 }
307
308 fn dielectric_fresnel(&self, cos_theta_i: F, eta: F) -> F {
309 let sin_theta_tsq = eta * eta * (1.0 - cos_theta_i * cos_theta_i);
310
311 if sin_theta_tsq > 1.0 {
313 return 1.0;
314 }
315
316 let cos_theta_t = (1.0 - sin_theta_tsq).max(0.0).sqrt();
317
318 let rs = (eta * cos_theta_t - cos_theta_i) / (eta * cos_theta_t + cos_theta_i);
319 let rp = (eta * cos_theta_i - cos_theta_t) / (eta * cos_theta_i + cos_theta_t);
320
321 0.5 * (rs * rs + rp * rp)
322 }
323
324 fn cosine_sample_hemisphere(&self, r1: F, r2: F) -> F3
325 {
326 let mut dir = F3::zeros();
327 let r = r1.sqrt();
328 let phi = crate::TWO_PI * r2;
329 dir.x = r * phi.cos();
330 dir.y = r * phi.sin();
331 dir.z = 0.0_f32.max(1.0 - dir.x * dir.x - dir.y * dir.y).sqrt();
332 dir
333 }
334
335 fn get_spec_color(&self, material: &Material, eta: F, spec_col: &mut F3, sheen_col: &mut F3) {
336 let lum = self.luminance(&material.rgb);
337 let ctint = if lum > 0.0 { material.rgb / F3::new_x(lum) } else { F3::new(1.0, 1.0, 1.0) };
338 let f0 = (1.0 - eta) / (1.0 + eta);
339 *spec_col = mix(&(f0 * f0 * mix(&F3::new(1.0, 1.0, 1.0), &ctint, &material.specular_tint)), &material.rgb, &material.metallic);
340 *sheen_col = mix(&F3::new(1.0, 1.0, 1.0), &ctint, &material.sheen_tint);
341 }
342
343 fn eval_diffuse(&self, material: &Material, c_sheen: &F3, v: &F3, l: &F3, h: &F3, pdf: &mut F) -> F3 {
344 *pdf = 0.0;
345 if l.z <= 0.0 {
346 return F3::zeros();
347 }
348
349 let fl = self.schlick_fresnel(l.z);
351 let fv = self.schlick_fresnel(v.z);
352 let fh = self.schlick_fresnel(dot(&l, &h));
353 let fd90 = 0.5 + 2.0 * dot(&l, &h) * dot(&l, &h) * material.roughness;
354 let fd = self.mix_ptf(&1.0, &fd90, fl) * self.mix_ptf(&1.0, &fd90, fv);
355
356 let fss90 = dot(&l, &h) * dot(&l, &h) * material.roughness;
358 let fss = self.mix_ptf(&1.0, &fss90, fl) * self.mix_ptf(&1.0, &fss90, fv);
359 let ss = 1.25 * (fss * (1.0 / (l.z + v.z) - 0.5) + 0.5);
360
361 let fsheen = fh * material.sheen * *c_sheen;
363
364 *pdf = l.z * crate::INV_PI;
365 (1.0 - material.metallic) * (1.0 - material.spec_trans) * (crate::INV_PI * self.mix_ptf(&fd, &ss, material.subsurface) * material.rgb + fsheen)
366 }
367
368 fn eval_spec_reflection(&self, material: &Material, eta: F, spec_col: &F3, v: &F3, l: &F3, h: &F3, pdf: &mut F) -> F3 {
369 *pdf = 0.0;
370 if l.z <= 0.0 {
371 return F3::zeros();
372 }
373
374 let fm = self.disney_fresnel(material, eta, dot(&l, &h), dot(&v, &h));
375 let f = mix(&spec_col, &F3::new(1.0, 1.0, 1.0), &fm);
376 let d = self.gtr2aniso(&h.z, &h.x, &h.y, &material.ax, &material.ay);
377 let g1 = self.smithganiso(&v.z.abs(), &v.x, &v.y, &material.ax, &material.ay);
378 let g2 = g1 * self.smithganiso(&l.z.abs(), &l.x, &l.y, &material.ax, &material.ay);
379
380 *pdf = g1 * d / (4.0 * v.z);
381 d * g2 * f / F3::new_x(4.0 * l.z * v.z)
382 }
383
384 fn eval_spec_refraction(&self, material: &Material, eta: F, v: &F3, l: &F3, h: &F3, pdf: &mut F) -> F3 {
385 *pdf = 0.0;
386 if l.z >= 0.0 {
387 return F3::zeros();
388 }
389
390 let f = self.dielectric_fresnel(dot(&v, &h).abs(), eta);
391 let d = self.gtr2aniso(&h.z, &h.x, &h.y, &material.ax, &material.ay);
392 let g1 = self.smithganiso(&v.z.abs(), &v.x, &v.y, &material.ax, &material.ay);
393 let g2 = g1 * self.smithganiso(&l.z.abs(), &l.x, &l.y, &material.ax, &material.ay);
394 let mut denom = dot(&l, &h) + dot(&v, &h) * eta;
395 denom *= denom;
396 let eta2 = eta * eta;
397 let jacobian = dot(&l, &h).abs() / denom;
398
399 *pdf = g1 * 0.0_f32.max(dot(&v, &h)) * d * jacobian / v.z;
400
401 (1.0 - material.metallic) * material.spec_trans * (1.0 - f) * d * g2 * dot(&v, &h).abs() * jacobian * eta2 / (l.z * v.z).abs() * pow(&material.rgb, &F3::new(0.5, 0.5, 0.5))
402 }
403
404 fn eval_clearcoat(&self, material: &Material, v: &F3, l: &F3, h: &F3, pdf: &mut F) -> F3
405 {
406 *pdf = 0.0;
407 if l.z <= 0.0 {
408 return F3::zeros();
409 }
410
411 let fh = self.dielectric_fresnel(dot(&v, &h), 1.0 / 1.5);
412 let f = self.mix_ptf(&0.04, &1.0, fh);
413 let d = self.gtr1(&h.z, material.clearcoat_roughness);
414 let g = self.smithg(&l.z, 0.25) * self.smithg(&v.z, 0.25);
415 let jacobian = 1.0 / (4.0 * dot(&v, &h));
416
417 *pdf = d * h.z * jacobian;
418 material.clearcoat * f * d * g / (4.0 * l.z * v.z) * F3::new(0.25, 0.25, 0.25)
419 }
420
421 fn get_lobe_probabilities(&self, material: &Material, _eta: &F3, spec_col: &F3, approx_fresnel: F, diffuse_wt: &mut F, spec_reflect_wt: &mut F, spec_refract_wt: &mut F, clearcoat_wt: &mut F)
422 {
423 *diffuse_wt = self.luminance(&material.rgb) * (1.0 - material.metallic) * (1.0 - material.spec_trans);
424 *spec_reflect_wt = self.luminance(&mix(&spec_col, &F3::new(1.0, 1.0, 1.0), &approx_fresnel));
425 *spec_refract_wt = (1.0 - approx_fresnel) * (1.0 - material.metallic) * material.spec_trans * self.luminance(&material.rgb);
426 *clearcoat_wt = 0.25 * material.clearcoat * (1.0 - material.metallic);
427 let total_wt = *diffuse_wt + *spec_reflect_wt + *spec_refract_wt + *clearcoat_wt;
428
429 *diffuse_wt /= total_wt;
430 *spec_reflect_wt /= total_wt;
431 *spec_refract_wt /= total_wt;
432 *clearcoat_wt /= total_wt;
433 }
434
435 fn disney_fresnel(&self, material: &Material, eta: F, ldot_h: F, vdot_h: F) -> F {
436 let metallic_fresnel = self.schlick_fresnel(ldot_h);
437 let dielectric_fresnel = self.dielectric_fresnel(vdot_h.abs(), eta);
438 self.mix_ptf(&dielectric_fresnel, &metallic_fresnel, material.metallic)
439 }
440
441 fn disney_sample(&self, state: &State, mut v: F3, n: &F3, l: &mut F3, pdf: &mut F, rng: &mut ThreadRng) -> F3 {
442
443 *pdf = 0.0;
444 let f;
445
446 let mut r1 : F = rng.gen();
447 let r2 : F = rng.gen();
448
449 fn onb(n: &F3, t: &mut F3, b: &mut F3) {
450 let up = if n.z.abs() < 0.999 { F3::new(0.0, 0.0, 1.0) } else { F3::new(1.0, 0.0, 0.0) };
451
452 *t = normalize(&cross(&up, n));
453 *b = cross(n, t);
454 }
455
456 fn to_local(x: &F3, y: &F3, z: &F3, v: &F3) -> F3 {
457 F3::new(dot(v, x), dot(v, y), dot(v, z))
458 }
459
460 fn to_world(x: &F3, y: &F3, z: &F3, v: &F3) -> F3 {
461 v.x * *x + v.y * *y + v.z * *z
462 }
463
464 fn reflect(i: F3, n: F3) -> F3 {
465 i - F3::new(2.0, 2.0, 2.0) * n * F3::new_x(dot(&n, &i))
466 }
467
468 fn refract(i: F3, n: F3, eta: F) -> F3 {
469 let k = 1.0 - eta * eta * (1.0 - dot(&n, &i) * dot(&n, &i));
470 if k < 0.0 {
471 F3::zeros()
472 } else {
473 eta * i - (eta * dot(&n, &i) + k.sqrt()) * n
474 }
475 }
476
477 let mut t = F3::zeros();
478 let mut b = F3::zeros();
479
480 onb(&n, &mut t, &mut b);
481 v = to_local(&t, &b, n, &v); let mut spec_col = F3::zeros();
485 let mut sheen_col = F3::zeros();
486 self.get_spec_color(&state.material, state.eta, &mut spec_col, &mut sheen_col);
487
488 let mut diffuse_wt = 0.0; let mut spec_reflect_wt = 0.0; let mut spec_refract_wt = 0.0; let mut clearcoat_wt = 0.0;
490
491 let approx_fresnel = self.disney_fresnel(&state.material, state.eta, v.z, v.z);
492 self.get_lobe_probabilities(&state.material, &F3::new_x(state.eta), &spec_col, approx_fresnel, &mut diffuse_wt, &mut spec_reflect_wt, &mut spec_refract_wt, &mut clearcoat_wt);
493
494 let mut cdf = [0.0, 0.0, 0.0, 0.0];
496 cdf[0] = diffuse_wt;
497 cdf[1] = cdf[0] + clearcoat_wt;
498 cdf[2] = cdf[1] + spec_reflect_wt;
499 cdf[3] = cdf[2] + spec_refract_wt;
500
501 if r1 < cdf[0] { r1 /= cdf[0];
503 *l = self.cosine_sample_hemisphere(r1, r2);
504
505 let h = normalize(&(*l + v));
506 f = self.eval_diffuse(&state.material, &sheen_col, &v, &l, &h, pdf);
507 *pdf *= diffuse_wt;
508 } else
509 if r1 < cdf[1] {r1 = (r1 - cdf[0]) / (cdf[1] - cdf[0]);
511
512 let mut h = self.sample_gtr1(state.material.clearcoat_roughness, r1, r2);
513
514 if h.z < 0.0 {
515 h = -h;
516 }
517
518 *l = normalize(&reflect(-v, h));
519 f = self.eval_clearcoat(&state.material, &v, l, &h, pdf);
520 *pdf *= clearcoat_wt;
521 } else {
523 r1 = (r1 - cdf[1]) / (1.0 - cdf[1]);
524 let mut h = self.sample_ggxvndf(&v, state.material.ax, state.material.ay, r1, r2);
525
526 if h.z < 0.0 {
527 h = -h;
528 }
529
530 let fresnel = self.disney_fresnel(&state.material, state.eta, dot(l, &h), dot(&v, &h));
532 let ff = 1.0 - ((1.0 - fresnel) * state.material.spec_trans * (1.0 - state.material.metallic));
533
534 let rand : F = rng.gen();
535
536 if rand < ff {
537 *l = normalize(&reflect(-v, h));
538
539 f = self.eval_spec_reflection(&state.material, state.eta, &spec_col, &v, l, &h, pdf);
540 *pdf *= ff;
541 } else {
542 *l = normalize(&refract(-v, h, state.eta));
543
544 f = self.eval_spec_refraction(&state.material, state.eta, &v, l, &h, pdf);
545 *pdf *= 1.0 - ff;
546 }
547
548 *pdf *= spec_reflect_wt + spec_refract_wt;
549 }
550
551 *l = to_world(&t, &b, &n, &l);
552 dot(n, l).abs() * f
553 }
554
555 fn disney_eval(&self, state: &State, v: F3, n: &F3, l: &F3, bsdf_pdf: &mut F) -> F3 {
556 *bsdf_pdf = 0.0;
557 let mut f = F3::zeros();
558
559 fn onb(n: &F3, t: &mut F3, b: &mut F3) {
560 let up = if n.z.abs() < 0.999 { F3::new(0.0, 0.0, 1.0) } else { F3::new(1.0, 0.0, 0.0) };
561
562 *t = normalize(&cross(&up, &n));
563 *b = cross(&n, &t);
564 }
565
566 fn to_local(x: &F3, y: &F3, z: &F3, v: &F3) -> F3 {
567 F3::new(dot(v, x), dot(v, y), dot(v, z))
568 }
569
570 let mut t = F3::zeros();
571 let mut b = F3::zeros();
572
573 onb(n, &mut t, &mut b);
574 let v = to_local(&t, &b, n, &v); let l = to_local(&t, &b, n, l);
576
577 let mut h;
578 if l.z > 0.0 {
579 h = normalize(&(l + v));
580 } else {
581 h = normalize(&(l + state.eta * v));
582 }
583
584 if h.z < 0.0 {
585 h = -h;
586 }
587
588 let mut spec_col = F3::zeros();
590 let mut sheen_col = F3::zeros();
591 self.get_spec_color(&state.material, state.eta, &mut spec_col, &mut sheen_col);
592
593 let mut diffuse_wt = 0.0; let mut spec_reflect_wt = 0.0; let mut spec_refract_wt = 0.0; let mut clearcoat_wt = 0.0;
595
596 let fresnel = self.disney_fresnel(&state.material, state.eta, dot(&l, &h), dot(&v, &h));
597 self.get_lobe_probabilities(&state.material, &F3::new_x(state.eta), &spec_col, fresnel, &mut diffuse_wt, &mut spec_reflect_wt, &mut spec_refract_wt, &mut clearcoat_wt);
598
599 let mut pdf = 0.0;
600
601 if diffuse_wt > 0.0 && l.z > 0.0 {
603 f += self.eval_diffuse(&state.material, &sheen_col, &v, &l, &h, &mut pdf);
604 *bsdf_pdf += pdf * diffuse_wt;
605 }
606
607 if spec_reflect_wt > 0.0 && l.z > 0.0 && v.z > 0.0 {
609 f += self.eval_spec_reflection(&state.material, state.eta, &spec_col, &v, &l, &h, &mut pdf);
610 *bsdf_pdf += pdf * spec_reflect_wt;
611 }
612
613 if spec_refract_wt > 0.0 && l.z < 0.0 {
615 f += self.eval_spec_refraction(&state.material, state.eta, &v, &l, &h, &mut pdf);
616 *bsdf_pdf += pdf * spec_refract_wt;
617 }
618
619 if clearcoat_wt > 0.0 && l.z > 0.0 && v.z > 0.0 {
621 f +=self.eval_clearcoat(&state.material, &v, &l, &h, &mut pdf);
622 *bsdf_pdf += pdf * clearcoat_wt;
623 }
624
625 l.z.abs() * f
626 }
627
628 pub fn scene(&mut self) -> &mut Box<dyn Scene> {
630 &mut self.scene
631 }
632
633}