1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
//std
use std::sync::Arc;
// pbrt
use crate::core::interaction::SurfaceInteraction;
use crate::core::material::{Material, TransportMode};
use crate::core::microfacet::{MicrofacetDistribution, TrowbridgeReitzDistribution};
use crate::core::paramset::TextureParams;
use crate::core::pbrt::{Float, Spectrum};
use crate::core::reflection::{Bsdf, Bxdf, FresnelBlend};
use crate::core::texture::Texture;

// see substrate.h

pub struct SubstrateMaterial {
    pub kd: Arc<dyn Texture<Spectrum> + Sync + Send>, // default: 0.5
    pub ks: Arc<dyn Texture<Spectrum> + Sync + Send>, // default: 0.5
    pub nu: Arc<dyn Texture<Float> + Sync + Send>,    // default: 0.1
    pub nv: Arc<dyn Texture<Float> + Sync + Send>,    // default: 0.1
    pub bump_map: Option<Arc<dyn Texture<Float> + Send + Sync>>,
    pub remap_roughness: bool,
}

impl SubstrateMaterial {
    pub fn new(
        kd: Arc<dyn Texture<Spectrum> + Send + Sync>,
        ks: Arc<dyn Texture<Spectrum> + Send + Sync>,
        nu: Arc<dyn Texture<Float> + Sync + Send>,
        nv: Arc<dyn Texture<Float> + Sync + Send>,
        bump_map: Option<Arc<dyn Texture<Float> + Sync + Send>>,
        remap_roughness: bool,
    ) -> Self {
        SubstrateMaterial {
            kd,
            ks,
            nu,
            nv,
            bump_map,
            remap_roughness,
        }
    }
    pub fn create(mp: &mut TextureParams) -> Arc<Material> {
        let kd: Arc<dyn Texture<Spectrum> + Sync + Send> =
            mp.get_spectrum_texture("Kd", Spectrum::new(0.5));
        let ks: Arc<dyn Texture<Spectrum> + Sync + Send> =
            mp.get_spectrum_texture("Ks", Spectrum::new(0.5));
        let uroughness: Arc<dyn Texture<Float> + Sync + Send> =
            mp.get_float_texture("uroughness", 0.1);
        let vroughness: Arc<dyn Texture<Float> + Sync + Send> =
            mp.get_float_texture("vroughness", 0.1);
        let bump_map = mp.get_float_texture_or_null("bumpmap");
        let remap_roughness: bool = mp.find_bool("remaproughness", true);
        Arc::new(Material::Substrate(Box::new(SubstrateMaterial::new(
            kd,
            ks,
            uroughness,
            vroughness,
            bump_map,
            remap_roughness,
        ))))
    }
    // Material
    pub fn compute_scattering_functions(
        &self,
        si: &mut SurfaceInteraction,
        // arena: &mut Arena,
        _mode: TransportMode,
        _allow_multiple_lobes: bool,
        _material: Option<Arc<Material>>,
        scale_opt: Option<Spectrum>,
    ) {
        let mut use_scale: bool = false;
        let mut sc: Spectrum = Spectrum::default();
        if let Some(scale) = scale_opt {
            use_scale = true;
            sc = scale;
        }
        if let Some(ref bump) = self.bump_map {
            Material::bump(bump, si);
        }
        let d: Spectrum = self
            .kd
            .evaluate(si)
            .clamp(0.0 as Float, std::f32::INFINITY as Float);
        let s: Spectrum = self
            .ks
            .evaluate(si)
            .clamp(0.0 as Float, std::f32::INFINITY as Float);
        let mut roughu: Float = self.nu.evaluate(si);
        let mut roughv: Float = self.nv.evaluate(si);
        si.bsdf = Some(Bsdf::new(si, 1.0));
        if let Some(bsdf) = &mut si.bsdf {
            if !d.is_black() || !s.is_black() {
                if self.remap_roughness {
                    roughu = TrowbridgeReitzDistribution::roughness_to_alpha(roughu);
                    roughv = TrowbridgeReitzDistribution::roughness_to_alpha(roughv);
                }
                let distrib: Option<MicrofacetDistribution> =
                    Some(MicrofacetDistribution::TrowbridgeReitz(
                        TrowbridgeReitzDistribution::new(roughu, roughv, true),
                    ));
                if use_scale {
                    bsdf.add(Bxdf::FresnelBlnd(FresnelBlend::new(
                        d,
                        s,
                        distrib,
                        Some(sc),
                    )));
                } else {
                    bsdf.add(Bxdf::FresnelBlnd(FresnelBlend::new(d, s, distrib, None)));
                }
            }
        }
    }
}