rs_pbrt/materials/
disney.rs

1use std::f32;
2use std::sync::Arc;
3
4use num::Zero;
5
6use crate::core::geometry::{spherical_direction, vec3_abs_dot_vec3f, vec3_dot_vec3f};
7use crate::core::geometry::{Point2f, Vector3f, XYEnum};
8use crate::core::interaction::SurfaceInteraction;
9use crate::core::material::{Material, TransportMode};
10use crate::core::microfacet::{MicrofacetDistribution, TrowbridgeReitzDistribution};
11use crate::core::paramset::TextureParams;
12use crate::core::pbrt::{clamp_t, lerp};
13use crate::core::pbrt::{Float, Spectrum};
14use crate::core::reflection::reflect;
15use crate::core::reflection::{abs_cos_theta, fr_schlick, vec3_same_hemisphere_vec3};
16use crate::core::reflection::{
17    Bsdf, Bxdf, BxdfType, DisneyFresnel, Fresnel, LambertianTransmission, MicrofacetReflection,
18    MicrofacetTransmission, SpecularTransmission,
19};
20use crate::core::texture::Texture;
21
22pub struct DisneyMaterial {
23    color: Arc<dyn Texture<Spectrum> + Send + Sync>,
24    // base_color: Arc<TextureFloat>,
25    metallic: Arc<dyn Texture<Float> + Send + Sync>,
26    eta: Arc<dyn Texture<Float> + Send + Sync>,
27    roughness: Arc<dyn Texture<Float> + Send + Sync>,
28    specular_tint: Arc<dyn Texture<Float> + Send + Sync>,
29    anisotropic: Arc<dyn Texture<Float> + Send + Sync>,
30    sheen: Arc<dyn Texture<Float> + Send + Sync>,
31    sheen_tint: Arc<dyn Texture<Float> + Send + Sync>,
32    clearcoat: Arc<dyn Texture<Float> + Send + Sync>,
33    clearcoat_gloss: Arc<dyn Texture<Float> + Send + Sync>,
34    spec_trans: Arc<dyn Texture<Float> + Send + Sync>,
35    scatter_distance: Arc<dyn Texture<Spectrum> + Send + Sync>,
36    flatness: Arc<dyn Texture<Float> + Send + Sync>,
37    diff_trans: Arc<dyn Texture<Float> + Send + Sync>,
38    bump_map: Option<Arc<dyn Texture<Float> + Send + Sync>>,
39    thin: bool,
40}
41
42impl DisneyMaterial {
43    pub fn create(mp: &mut TextureParams) -> Arc<Material> {
44        let color = mp.get_spectrum_texture("color", Spectrum::from(0.5));
45        let metallic = mp.get_float_texture("metallic", 0.0);
46        let eta = mp.get_float_texture("eta", 1.5);
47        let roughness = mp.get_float_texture("roughness", 0.5);
48        let specular_tint = mp.get_float_texture("speculartint", 0.0);
49        let anisotropic = mp.get_float_texture("anisotropic", 0.0);
50        let sheen = mp.get_float_texture("sheen", 0.0);
51        let sheen_tint = mp.get_float_texture("sheentint", 0.5);
52        let clearcoat = mp.get_float_texture("clearcoat", 0.0);
53        let clearcoat_gloss = mp.get_float_texture("clearcoatgloss", 1.0);
54        let spec_trans = mp.get_float_texture("spectrans", 0.0);
55        let scatter_distance = mp.get_spectrum_texture("scatterdistance", Spectrum::from(0.0));
56        let thin = mp.find_bool("thin", false);
57        let flatness = mp.get_float_texture("flatness", 0.0);
58        let diff_trans = mp.get_float_texture("difftrans", 1.0);
59        let bump_map = mp.get_float_texture_or_null("bumpmap");
60
61        Arc::new(Material::Disney(Box::new(DisneyMaterial {
62            color,
63            metallic,
64            eta,
65            roughness,
66            specular_tint,
67            anisotropic,
68            sheen,
69            sheen_tint,
70            clearcoat,
71            clearcoat_gloss,
72            spec_trans,
73            scatter_distance,
74            flatness,
75            diff_trans,
76            bump_map,
77            thin,
78        })))
79    }
80    // Material
81    pub fn compute_scattering_functions(
82        &self,
83        si: &mut SurfaceInteraction,
84        mode: TransportMode,
85        _allow_multiple_lobes: bool,
86        _material: Option<Arc<Material>>,
87        scale_opt: Option<Spectrum>,
88    ) {
89        let mut use_scale: bool = false;
90        let mut sc: Spectrum = Spectrum::default();
91        if let Some(scale) = scale_opt {
92            use_scale = true;
93            sc = scale;
94        }
95        if let Some(ref bump) = self.bump_map {
96            Material::bump(bump, si);
97        }
98        // diffuse
99        let c = self.color.evaluate(si).clamp(0.0, f32::INFINITY);
100        let metallic_weight = self.metallic.evaluate(si);
101        let e = self.eta.evaluate(si);
102        let strans = self.spec_trans.evaluate(si);
103        let diffuse_weight = (1.0 - metallic_weight) * (1.0 - strans);
104        let dt = self.diff_trans.evaluate(si) / 2.0; // 0: all diffuse is reflected -> 1, transmitted
105        let rough = self.roughness.evaluate(si);
106        let lum = c.y();
107        // normalize lum. to isolate hue+sat
108        let c_tint = if lum > 0.0 {
109            c / lum
110        } else {
111            Spectrum::new(1.0)
112        };
113        let sheen_weight = self.sheen.evaluate(si);
114        let c_sheen = if sheen_weight > 0.0 {
115            let stint = self.sheen_tint.evaluate(si);
116            lerp(stint, Spectrum::new(1.0), c_tint)
117        } else {
118            Spectrum::zero()
119        };
120        let flat = self.flatness.evaluate(si);
121        let sd = self.scatter_distance.evaluate(si);
122        let aspect = Float::sqrt(1.0 - self.anisotropic.evaluate(si) * 0.9);
123        let spec_tint = self.specular_tint.evaluate(si);
124        let cc = self.clearcoat.evaluate(si);
125        let gloss: Float = lerp(self.clearcoat_gloss.evaluate(si), 0.1, 0.001);
126        si.bsdf = Some(Bsdf::new(si, 1.0));
127        if let Some(bsdf) = &mut si.bsdf {
128            if diffuse_weight > 0.0 {
129                if self.thin {
130                    // Blend between DisneyDiffuse and fake subsurface based on flatness. Additionally,
131                    // weight using diff_trans.
132                    if use_scale {
133                        bsdf.add(Bxdf::DisDiff(DisneyDiffuse::new(
134                            diffuse_weight * (1.0 - flat) * (1.0 - dt) * c,
135                            Some(sc),
136                        )));
137                        bsdf.add(Bxdf::DisSS(DisneyFakeSS::new(
138                            diffuse_weight * flat * (1.0 - dt) * c,
139                            rough,
140                            Some(sc),
141                        )));
142                    } else {
143                        bsdf.add(Bxdf::DisDiff(DisneyDiffuse::new(
144                            diffuse_weight * (1.0 - flat) * (1.0 - dt) * c,
145                            None,
146                        )));
147                        bsdf.add(Bxdf::DisSS(DisneyFakeSS::new(
148                            diffuse_weight * flat * (1.0 - dt) * c,
149                            rough,
150                            None,
151                        )));
152                    }
153                } else if sd.is_black() {
154                    // No subsurface scattering; use regular (Fresnel modified) diffuse.
155                    if use_scale {
156                        bsdf.add(Bxdf::DisDiff(DisneyDiffuse::new(
157                            diffuse_weight * c,
158                            Some(sc),
159                        )));
160                    } else {
161                        bsdf.add(Bxdf::DisDiff(DisneyDiffuse::new(diffuse_weight * c, None)));
162                    }
163                } else {
164                    // Use a BSSRDF instead.
165                    if use_scale {
166                        bsdf.add(Bxdf::SpecTrans(SpecularTransmission::new(
167                            Spectrum::from(1.0),
168                            1.0,
169                            e,
170                            mode,
171                            Some(sc),
172                        )));
173                    } else {
174                        bsdf.add(Bxdf::SpecTrans(SpecularTransmission::new(
175                            Spectrum::from(1.0),
176                            1.0,
177                            e,
178                            mode,
179                            None,
180                        )));
181                    }
182                    // TODO: BSSRDF
183                }
184
185                // Retro-reflection.
186                if use_scale {
187                    bsdf.add(Bxdf::DisRetro(DisneyRetro::new(
188                        diffuse_weight * c,
189                        rough,
190                        Some(sc),
191                    )));
192                } else {
193                    bsdf.add(Bxdf::DisRetro(DisneyRetro::new(
194                        diffuse_weight * c,
195                        rough,
196                        None,
197                    )));
198                }
199                // Sheen (if enabled).
200                if sheen_weight > 0.0 {
201                    if use_scale {
202                        bsdf.add(Bxdf::DisSheen(DisneySheen::new(
203                            diffuse_weight * sheen_weight * c_sheen,
204                            Some(sc),
205                        )));
206                    } else {
207                        bsdf.add(Bxdf::DisSheen(DisneySheen::new(
208                            diffuse_weight * sheen_weight * c_sheen,
209                            None,
210                        )));
211                    }
212                }
213            }
214
215            // Create the microfacet distribution for metallic and/or specular transmission.
216            let ax = Float::max(0.001, sqr(rough) / aspect);
217            let ay = Float::max(0.001, sqr(rough) * aspect);
218            let distrib =
219                MicrofacetDistribution::DisneyMicrofacet(DisneyMicrofacetDistribution::new(ax, ay));
220
221            // Specular is Trowbridge-Reitz with a modified Fresnel function
222            let cspec0 = lerp(
223                metallic_weight,
224                schlick_r0_from_eta(e) * lerp(spec_tint, Spectrum::new(1.0), c_tint),
225                c,
226            );
227            let fresnel = Fresnel::Disney(DisneyFresnel::new(cspec0, metallic_weight, e));
228            if use_scale {
229                bsdf.add(Bxdf::MicrofacetRefl(MicrofacetReflection::new(
230                    c,
231                    distrib,
232                    fresnel,
233                    Some(sc),
234                )));
235            } else {
236                bsdf.add(Bxdf::MicrofacetRefl(MicrofacetReflection::new(
237                    c, distrib, fresnel, None,
238                )));
239            }
240            // Clearcoat
241            if cc > 0.0 {
242                if use_scale {
243                    bsdf.add(Bxdf::DisClearCoat(DisneyClearCoat::new(
244                        cc,
245                        gloss,
246                        Some(sc),
247                    )));
248                } else {
249                    bsdf.add(Bxdf::DisClearCoat(DisneyClearCoat::new(cc, gloss, None)));
250                }
251            }
252
253            // BTDF
254            if strans > 0.0 {
255                // Walter et al.'s model, with the provided transmissive term scaled by sqrt(color), so
256                // that after two refractions we're back to the provided color.
257                let t = strans * c.sqrt();
258                if self.thin {
259                    // Scale roughness based on IOR (Burley 2015, Figure 15).
260                    let rscaled = (0.65 * e - 0.35) * rough;
261                    let ax = Float::max(0.001, sqr(rscaled) / aspect);
262                    let ay = Float::max(0.001, sqr(rscaled) * aspect);
263                    let scaled_distrib = MicrofacetDistribution::TrowbridgeReitz(
264                        TrowbridgeReitzDistribution::new(ax, ay, true),
265                    );
266                    if use_scale {
267                        bsdf.add(Bxdf::MicrofacetTrans(MicrofacetTransmission::new(
268                            t,
269                            scaled_distrib,
270                            1.0,
271                            e,
272                            mode,
273                            Some(sc),
274                        )));
275                    } else {
276                        bsdf.add(Bxdf::MicrofacetTrans(MicrofacetTransmission::new(
277                            t,
278                            scaled_distrib,
279                            1.0,
280                            e,
281                            mode,
282                            None,
283                        )));
284                    }
285                } else {
286                    let distrib = MicrofacetDistribution::DisneyMicrofacet(
287                        DisneyMicrofacetDistribution::new(ax, ay),
288                    );
289                    if use_scale {
290                        bsdf.add(Bxdf::MicrofacetTrans(MicrofacetTransmission::new(
291                            t,
292                            distrib,
293                            1.0,
294                            e,
295                            mode,
296                            Some(sc),
297                        )));
298                    } else {
299                        bsdf.add(Bxdf::MicrofacetTrans(MicrofacetTransmission::new(
300                            t, distrib, 1.0, e, mode, None,
301                        )));
302                    }
303                }
304            }
305
306            if self.thin {
307                // Lambertian, weighted by (1.0 - diff_trans}
308                if use_scale {
309                    bsdf.add(Bxdf::LambertianTrans(LambertianTransmission::new(
310                        dt * c,
311                        Some(sc),
312                    )));
313                // bxdf_idx += 1;
314                } else {
315                    bsdf.add(Bxdf::LambertianTrans(LambertianTransmission::new(
316                        dt * c,
317                        None,
318                    )));
319                    // bxdf_idx += 1;
320                }
321            }
322        }
323    }
324}
325// DisneyDiffuse
326
327#[derive(Debug, Clone, Copy)]
328pub struct DisneyDiffuse {
329    pub r: Spectrum,
330    pub sc_opt: Option<Spectrum>,
331}
332
333impl DisneyDiffuse {
334    pub fn new(r: Spectrum, sc_opt: Option<Spectrum>) -> Self {
335        DisneyDiffuse { r, sc_opt }
336    }
337    pub fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {
338        let fo = schlick_weight(abs_cos_theta(wo));
339        let fi = schlick_weight(abs_cos_theta(wi));
340
341        // Diffuse fresnel - go from 1 at normal incidence to .5 at grazing.
342        // Burley 2015, eq (4).
343        if let Some(sc) = self.sc_opt {
344            sc * self.r * f32::consts::FRAC_1_PI * (1.0 - fo / 2.0) * (1.0 - fi / 2.0)
345        } else {
346            self.r * f32::consts::FRAC_1_PI * (1.0 - fo / 2.0) * (1.0 - fi / 2.0)
347        }
348    }
349    pub fn get_type(&self) -> u8 {
350        BxdfType::BsdfReflection as u8 | BxdfType::BsdfDiffuse as u8
351    }
352}
353
354// DisneyFakeSS
355
356#[derive(Debug, Clone, Copy)]
357pub struct DisneyFakeSS {
358    pub r: Spectrum,
359    pub roughness: Float,
360    pub sc_opt: Option<Spectrum>,
361}
362
363impl DisneyFakeSS {
364    pub fn new(r: Spectrum, roughness: Float, sc_opt: Option<Spectrum>) -> Self {
365        DisneyFakeSS {
366            r,
367            roughness,
368            sc_opt,
369        }
370    }
371    pub fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {
372        let mut wh = *wi + *wo;
373        if wh.x == 0.0 && wh.y == 0.0 && wh.z == 0.0 {
374            return Spectrum::from(0.0);
375        }
376        wh = wh.normalize();
377        let cos_theta_d = vec3_dot_vec3f(wi, &wh);
378
379        // Fss90 used to "flatten" retroreflection based on roughness
380        let fss90 = cos_theta_d * cos_theta_d * self.roughness;
381        let fo = schlick_weight(abs_cos_theta(wo));
382        let fi = schlick_weight(abs_cos_theta(wi));
383        let fss = lerp(fo, 1.0, fss90) * lerp(fi, 1.0, fss90);
384        // 1.25 scale is used to (roughly) preserve albedo
385        let ss = 1.25 * (fss * (1.0 / (abs_cos_theta(wo) + abs_cos_theta(wi)) - 0.5) + 0.5);
386
387        if let Some(sc) = self.sc_opt {
388            sc * self.r * f32::consts::FRAC_1_PI * ss
389        } else {
390            self.r * f32::consts::FRAC_1_PI * ss
391        }
392    }
393    pub fn get_type(&self) -> u8 {
394        BxdfType::BsdfReflection as u8 | BxdfType::BsdfDiffuse as u8
395    }
396}
397
398// DisneyRetro
399
400#[derive(Debug, Clone, Copy)]
401pub struct DisneyRetro {
402    pub r: Spectrum,
403    pub roughness: Float,
404    pub sc_opt: Option<Spectrum>,
405}
406
407impl DisneyRetro {
408    pub fn new(r: Spectrum, roughness: Float, sc_opt: Option<Spectrum>) -> Self {
409        DisneyRetro {
410            r,
411            roughness,
412            sc_opt,
413        }
414    }
415    pub fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {
416        let mut wh = *wi + *wo;
417        if wh.x == 0.0 && wh.y == 0.0 && wh.z == 0.0 {
418            return Spectrum::from(0.0);
419        }
420        wh = wh.normalize();
421        let cos_theta_d = vec3_dot_vec3f(wi, &wh);
422        let fo = schlick_weight(abs_cos_theta(wo));
423        let fi = schlick_weight(abs_cos_theta(wi));
424        let rr = 2.0 * self.roughness * cos_theta_d * cos_theta_d;
425
426        // Burley 2015, eq (4).
427        if let Some(sc) = self.sc_opt {
428            sc * self.r * f32::consts::FRAC_1_PI * rr * (fo + fi + fo * fi * (rr - 1.0))
429        } else {
430            self.r * f32::consts::FRAC_1_PI * rr * (fo + fi + fo * fi * (rr - 1.0))
431        }
432    }
433    pub fn get_type(&self) -> u8 {
434        BxdfType::BsdfReflection as u8 | BxdfType::BsdfDiffuse as u8
435    }
436}
437
438// DisneySheen
439
440#[derive(Debug, Clone, Copy)]
441pub struct DisneySheen {
442    pub r: Spectrum,
443    pub sc_opt: Option<Spectrum>,
444}
445
446impl DisneySheen {
447    pub fn new(r: Spectrum, sc_opt: Option<Spectrum>) -> Self {
448        DisneySheen { r, sc_opt }
449    }
450    pub fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {
451        let mut wh = *wi + *wo;
452        if wh.x == 0.0 && wh.y == 0.0 && wh.z == 0.0 {
453            return Spectrum::from(0.0);
454        }
455        wh = wh.normalize();
456        let cos_theta_d = vec3_dot_vec3f(wi, &wh);
457
458        if let Some(sc) = self.sc_opt {
459            sc * self.r * schlick_weight(cos_theta_d)
460        } else {
461            self.r * schlick_weight(cos_theta_d)
462        }
463    }
464    pub fn get_type(&self) -> u8 {
465        BxdfType::BsdfReflection as u8 | BxdfType::BsdfDiffuse as u8
466    }
467}
468
469// DisneyClearCoat
470
471#[derive(Debug, Clone, Copy)]
472pub struct DisneyClearCoat {
473    pub weight: Float,
474    pub gloss: Float,
475    pub sc_opt: Option<Spectrum>,
476}
477
478impl DisneyClearCoat {
479    pub fn new(weight: Float, gloss: Float, sc_opt: Option<Spectrum>) -> Self {
480        DisneyClearCoat {
481            weight,
482            gloss,
483            sc_opt,
484        }
485    }
486    pub fn f(&self, wo: &Vector3f, wi: &Vector3f) -> Spectrum {
487        let mut wh = *wi + *wo;
488        if wh.x == 0.0 && wh.y == 0.0 && wh.z == 0.0 {
489            return Spectrum::from(0.0);
490        }
491        wh = wh.normalize();
492
493        // Clearcoat has ior = 1.5 hardcoded -> F0 = 0.04. It then uses the
494        // gtr1 distribution, which has even fatter tails than Trowbridge-Reitz
495        // (which is GTR2).
496        let dr = gtr1(abs_cos_theta(&wh), self.gloss);
497        let fr = fr_schlick(0.04, vec3_dot_vec3f(wo, &wh));
498        // The geometric term always based on alpha = 0.25.
499        let gr = smith_g_ggx(abs_cos_theta(wo), 0.25) * smith_g_ggx(abs_cos_theta(wi), 0.25);
500
501        if let Some(sc) = self.sc_opt {
502            sc * Spectrum::from(self.weight * gr * fr * dr / 4.0)
503        } else {
504            Spectrum::from(self.weight * gr * fr * dr / 4.0)
505        }
506    }
507    pub fn sample_f(
508        &self,
509        wo: &Vector3f,
510        wi: &mut Vector3f,
511        u: &Point2f,
512        pdf: &mut Float,
513        _sampled_type: &mut u8,
514    ) -> Spectrum {
515        if wo.z == 0.0 {
516            return Spectrum::zero();
517        }
518
519        let alpha2 = self.gloss * self.gloss;
520        let cos_theta = Float::sqrt(Float::max(
521            0.0,
522            (1.0 - Float::powf(alpha2, 1.0 - u[XYEnum::X])) / (1.0 - alpha2),
523        ));
524        let sin_theta = Float::sqrt(Float::max(0.0, 1.0 - cos_theta * cos_theta));
525        let phi = 2.0 * f32::consts::PI * u[XYEnum::Y];
526        let mut wh = spherical_direction(sin_theta, cos_theta, phi);
527        if !vec3_same_hemisphere_vec3(wo, &wh) {
528            wh = -wh;
529        }
530        *wi = reflect(wo, &wh);
531
532        if !vec3_same_hemisphere_vec3(wo, wi) {
533            return Spectrum::zero();
534        }
535
536        *pdf = self.pdf(wo, wi);
537
538        if let Some(sc) = self.sc_opt {
539            sc * self.f(wo, wi)
540        } else {
541            self.f(wo, wi)
542        }
543    }
544    pub fn pdf(&self, wo: &Vector3f, wi: &Vector3f) -> Float {
545        if !vec3_same_hemisphere_vec3(wo, wi) {
546            return 0.0;
547        }
548
549        let mut wh = *wo + *wi;
550        if wh.x == 0.0 && wh.y == 0.0 && wh.z == 0.0 {
551            return 0.0;
552        }
553        wh = wh.normalize();
554
555        // The sampling routine samples wh exactly from the gtr1 distribution.
556        // Thus, the final value of the PDF is just the value of the
557        // distribution for wh converted to a mesure with respect to the
558        // surface normal.
559        let dr = gtr1(abs_cos_theta(&wh), self.gloss);
560        dr * abs_cos_theta(&wh) / (4.0 * vec3_dot_vec3f(wo, &wh))
561    }
562    pub fn get_type(&self) -> u8 {
563        BxdfType::BsdfReflection as u8 | BxdfType::BsdfGlossy as u8
564    }
565}
566
567#[derive(Default, Clone, Copy)]
568pub struct DisneyMicrofacetDistribution {
569    pub inner: TrowbridgeReitzDistribution,
570}
571
572impl DisneyMicrofacetDistribution {
573    pub fn new(alphax: Float, alphay: Float) -> DisneyMicrofacetDistribution {
574        DisneyMicrofacetDistribution {
575            inner: TrowbridgeReitzDistribution::new(alphax, alphay, true),
576        }
577    }
578    pub fn d(&self, wh: &Vector3f) -> Float {
579        self.inner.d(wh)
580    }
581    pub fn lambda(&self, wh: &Vector3f) -> Float {
582        self.inner.lambda(wh)
583    }
584    pub fn g1(&self, w: &Vector3f) -> Float {
585        1.0 as Float / (1.0 as Float + self.lambda(w))
586    }
587    pub fn g(&self, wi: &Vector3f, wo: &Vector3f) -> Float {
588        // Disney uses the separable masking-shadowing model.
589        self.g1(wi) * self.g1(wo)
590    }
591    pub fn pdf(&self, wo: &Vector3f, wh: &Vector3f) -> Float {
592        if self.get_sample_visible_area() {
593            self.d(wh) * self.g1(wo) * vec3_abs_dot_vec3f(wo, wh) / abs_cos_theta(wo)
594        } else {
595            self.d(wh) * abs_cos_theta(wh)
596        }
597    }
598    pub fn sample_wh(&self, wo: &Vector3f, u: &Point2f) -> Vector3f {
599        self.inner.sample_wh(wo, u)
600    }
601    pub fn get_sample_visible_area(&self) -> bool {
602        self.inner.get_sample_visible_area()
603    }
604}
605
606/// https://seblagarde.wordpress.com/2013/04/29/memo-on-fresnel-equations/
607///
608/// The Schlick Fresnel approximation is:
609///
610/// R = R(0) + (1 - R(0)) (1 - cos theta)^5,
611///
612/// where R(0) is the reflectance at normal indicence.
613fn schlick_weight(cos_theta: Float) -> Float {
614    let m = clamp_t(1.0 - cos_theta, 0.0, 1.0);
615    (m * m) * (m * m) * m
616}
617
618// For a dielectric, R(0) = (eta - 1)^2 / (eta + 1)^2, assuming we're
619// coming from air.
620fn schlick_r0_from_eta(eta: Float) -> Float {
621    sqr(eta - 1.0) / sqr(eta + 1.0)
622}
623
624fn gtr1(cos_theta: Float, alpha: Float) -> Float {
625    let alpha2 = alpha * alpha;
626
627    (alpha2 - 1.0)
628        / (f32::consts::PI * Float::log10(alpha2) * (1.0 + (alpha2 - 1.0) * cos_theta * cos_theta))
629}
630
631fn smith_g_ggx(cos_theta: Float, alpha: Float) -> Float {
632    let alpha2 = alpha * alpha;
633    let cos_theta2 = cos_theta * cos_theta;
634
635    1.0 / (cos_theta + Float::sqrt(alpha2 + cos_theta2 - alpha2 * cos_theta2))
636}
637
638fn sqr(x: Float) -> Float {
639    x * x
640}