rust_pathtracer/
material.rs

1use crate::prelude::*;
2
3use rhai::{Engine, FnPtr};
4
5// Medium
6
7#[derive(PartialEq, Copy, Clone, Debug)]
8pub enum MediumType {
9    None,
10    Absorb,
11    Scatter,
12    Emissive
13}
14
15/// The Medium struct for volumetric objects.
16#[derive(PartialEq, Copy, Clone, Debug)]
17pub struct Medium {
18    pub medium_type             : MediumType,
19    pub density                 : F,
20    pub color                   : F3,
21    pub anisotropy              : F,
22}
23
24impl Medium {
25
26    pub fn new() -> Self {
27        Self {
28            medium_type         : MediumType::None,
29            density             : 0.0,
30            color               : F3::new(0.0, 0.0, 0.0),
31            anisotropy          : 0.0,
32        }
33    }
34}
35
36// Material
37
38#[derive(PartialEq, Copy, Clone, Debug)]
39pub enum AlphaMode
40{
41    Opaque,
42    Blend,
43    Mask
44}
45
46/// The material struct holds all BSDF properties as well as the Medium.
47#[derive(Clone, Debug)]
48pub struct Material {
49    pub rgb                     : F3,
50    pub anisotropic             : F,
51    pub emission                : F3,
52
53    pub metallic                : F,
54    pub roughness               : F,
55    pub subsurface              : F,
56    pub specular_tint           : F,
57
58    pub sheen                   : F,
59    pub sheen_tint              : F,
60    pub clearcoat               : F,
61    pub clearcoat_gloss         : F,
62    /// Do not use clearcoat_roughness directly, it is for internal use only. Use clearcoat_gloss.
63    pub clearcoat_roughness     : F,
64
65    pub spec_trans              : F,
66    pub ior                     : F,
67
68    pub opacity                 : F,
69    pub alpha_mode              : AlphaMode,
70    pub alpha_cutoff            : F,
71
72    pub ax                      : F,
73    pub ay                      : F,
74
75    pub medium                  : Medium,
76
77    pub procedural              : Option<FnPtr>,
78}
79
80impl Material {
81
82    pub fn new() -> Self {
83
84        Self {
85            rgb                 : F3::new(1.5, 1.5, 1.5),
86            emission            : F3::new(0.0, 0.0, 0.0),
87
88            anisotropic         : 0.0,
89            metallic            : 0.0,
90            roughness           : 0.5,
91            subsurface          : 0.0,
92            specular_tint       : 0.0,
93
94            sheen               : 0.0,
95            sheen_tint          : 0.0,
96
97            clearcoat           : 0.0,
98            clearcoat_gloss     : 0.0,
99            clearcoat_roughness : 0.0,
100            spec_trans          : 0.0,
101            ior                 : 1.45,
102
103            opacity             : 1.0,
104            alpha_mode          : AlphaMode::Opaque,
105            alpha_cutoff        : 0.0,
106
107            medium              : Medium::new(),
108
109            ax                  : 0.0,
110            ay                  : 0.0,
111
112            procedural          : None,
113        }
114    }
115
116    /// Material post-processing, called by the tracer after calling Scene::closest_hit()
117    pub fn finalize(&mut self) {
118
119        self.roughness = self.roughness.max(0.01);
120
121        fn mix_ptf(a: &F, b: &F, v: F) -> F {
122            (1.0 - v) * a + b * v
123        }
124
125        self.clearcoat_roughness = mix_ptf(&0.1, &0.001, self.clearcoat_gloss); // Remapping from gloss to roughness
126        self.medium.anisotropy = self.medium.anisotropy.clamp(-0.9, 0.9);
127
128        let aspect = (1.0 - self.anisotropic * 0.9).sqrt();
129        self.ax = (self.roughness / aspect).max(0.001);
130        self.ay = (self.roughness * aspect).max(0.001);
131    }
132
133    /// Mixes this material with another material
134    pub fn mix(&self, other: &Material, v: F) -> Material {
135        let mut m = Material::new();
136
137        m.rgb = mix(&self.rgb, &other.rgb, &v);
138        m.emission = mix(&self.emission, &other.emission, &v);
139
140        m.anisotropic = mix_f(&self.anisotropic, &other.anisotropic, &v);
141        m.metallic = mix_f(&self.metallic, &other.metallic, &v);
142        m.roughness = mix_f(&self.roughness, &other.roughness, &v);
143        m.subsurface = mix_f(&self.subsurface, &other.subsurface, &v);
144        m.specular_tint = mix_f(&self.specular_tint, &other.specular_tint, &v);
145
146        m.sheen = mix_f(&self.sheen, &other.sheen, &v);
147        m.sheen_tint = mix_f(&self.sheen_tint, &other.sheen_tint, &v);
148
149        m.clearcoat = mix_f(&self.clearcoat, &other.clearcoat, &v);
150        m.clearcoat_gloss = mix_f(&self.clearcoat_gloss, &other.clearcoat_gloss, &v);
151        m.spec_trans = mix_f(&self.spec_trans, &other.spec_trans, &v);
152        m.ior = mix_f(&self.ior, &other.ior, &v);
153
154        m
155    }
156
157    // --------- Getter / Setter
158
159    pub fn get_rgb(&mut self) -> F3 {
160        self.rgb
161    }
162
163    pub fn set_rgb(&mut self, new_val: F3) {
164        self.rgb = new_val;
165    }
166
167    pub fn get_emission(&mut self) -> F3 {
168        self.emission
169    }
170
171    pub fn set_emission(&mut self, new_val: F3) {
172        self.emission = new_val;
173    }
174
175    pub fn get_anisotropic(&mut self) -> F {
176        self.anisotropic
177    }
178
179    pub fn set_anisotropic(&mut self, new_val: F) {
180        self.anisotropic = new_val;
181    }
182
183    pub fn get_metallic(&mut self) -> F {
184        self.metallic
185    }
186
187    pub fn set_metallic(&mut self, new_val: F) {
188        self.metallic = new_val;
189    }
190
191    pub fn get_roughness(&mut self) -> F {
192        self.roughness
193    }
194
195    pub fn set_roughness(&mut self, new_val: F) {
196        self.roughness = new_val;
197    }
198
199    pub fn get_subsurface(&mut self) -> F {
200        self.subsurface
201    }
202
203    pub fn set_subsurface(&mut self, new_val: F) {
204        self.subsurface = new_val;
205    }
206
207    pub fn get_specular_tint(&mut self) -> F {
208        self.specular_tint
209    }
210
211    pub fn set_specular_tint(&mut self, new_val: F) {
212        self.specular_tint = new_val;
213    }
214
215    pub fn get_sheen(&mut self) -> F {
216        self.sheen
217    }
218
219    pub fn set_sheen(&mut self, new_val: F) {
220        self.sheen = new_val;
221    }
222
223    pub fn get_sheen_tint(&mut self) -> F {
224        self.sheen_tint
225    }
226
227    pub fn set_sheen_tint(&mut self, new_val: F) {
228        self.sheen_tint = new_val;
229    }
230
231    pub fn get_clearcoat(&mut self) -> F {
232        self.clearcoat
233    }
234
235    pub fn set_clearcoat(&mut self, new_val: F) {
236        self.clearcoat = new_val;
237    }
238
239    pub fn get_clearcoat_gloss(&mut self) -> F {
240        self.clearcoat_gloss
241    }
242
243    pub fn set_clearcoat_gloss(&mut self, new_val: F) {
244        self.clearcoat_gloss = new_val;
245    }
246
247    pub fn get_spec_trans(&mut self) -> F {
248        self.spec_trans
249    }
250
251    pub fn set_spec_trans(&mut self, new_val: F) {
252        self.spec_trans = new_val;
253    }
254
255    pub fn get_ior(&mut self) -> F {
256        self.ior
257    }
258
259    pub fn set_ior(&mut self, new_val: F) {
260        self.ior = new_val;
261    }
262
263    pub fn get_procedural(&mut self) -> FnPtr {
264        if let Some(procedural) = &self.procedural {
265            procedural.clone()
266        } else {
267            FnPtr::new("empty").ok().unwrap()
268        }
269    }
270
271    pub fn set_procedural(&mut self, new_val: FnPtr) {
272        self.procedural = Some(new_val)
273    }
274
275    /// Register to the engine
276    pub fn register(engine: &mut Engine) {
277        engine.register_type_with_name::<Material>("Material")
278            .register_fn("Material", Material::new)
279
280            .register_get_set("rgb", Material::get_rgb, Material::set_rgb)
281            .register_get_set("emission", Material::get_emission, Material::set_emission)
282
283            .register_get_set("anisotropic", Material::get_anisotropic, Material::set_anisotropic)
284            .register_get_set("roughness", Material::get_roughness, Material::set_roughness)
285            .register_get_set("metallic", Material::get_metallic, Material::set_metallic)
286            .register_get_set("subsurface", Material::get_subsurface, Material::set_subsurface)
287            .register_get_set("specular_tint", Material::get_specular_tint, Material::set_specular_tint)
288
289            .register_get_set("sheen", Material::get_sheen, Material::set_sheen)
290            .register_get_set("sheen_tint", Material::get_sheen_tint, Material::set_sheen_tint)
291            .register_get_set("clearcoat", Material::get_clearcoat, Material::set_clearcoat)
292            .register_get_set("clearcoat_gloss", Material::get_clearcoat_gloss, Material::set_clearcoat_gloss)
293
294            .register_get_set("transmission", Material::get_spec_trans, Material::set_spec_trans)
295            .register_get_set("ior", Material::get_ior, Material::set_ior)
296
297            .register_get_set("procedural", Material::get_procedural, Material::set_procedural);
298    }
299}