pbrt_r3/integrators/
path.rs

1use crate::core::prelude::*;
2
3use std::ops::Deref;
4use std::sync::Arc;
5use std::sync::RwLock;
6
7thread_local!(static PATHS: StatPercent = StatPercent::new("Integrator/Zero-radiance paths"));
8thread_local!(static PATH_LENGTH: StatIntDistribution = StatIntDistribution::new("Integrator/Path length"));
9
10pub struct PathIntegrator {
11    base: BaseSamplerIntegrator,
12    light_distribution: Option<Arc<dyn LightDistribution>>,
13    max_depth: i32,
14    rr_threshold: Float,
15    light_sample_sterategy: String,
16}
17
18impl PathIntegrator {
19    pub fn new(
20        max_depth: i32,
21        camera: &Arc<dyn Camera>,
22        sampler: &Arc<RwLock<dyn Sampler>>,
23        pixel_bounds: &Bounds2i,
24        rr_threshold: Float,
25        light_sample_sterategy: &str,
26    ) -> Self {
27        PathIntegrator {
28            base: BaseSamplerIntegrator::new(camera, sampler, pixel_bounds),
29            light_distribution: None,
30            max_depth,
31            rr_threshold,
32            light_sample_sterategy: String::from(light_sample_sterategy),
33        }
34    }
35}
36
37impl Integrator for PathIntegrator {
38    fn render(&mut self, scene: &Scene) {
39        BaseSamplerIntegrator::render(self, scene);
40    }
41
42    fn get_camera(&self) -> Arc<dyn Camera> {
43        return self.base.camera.clone();
44    }
45}
46
47unsafe impl Sync for PathIntegrator {}
48
49impl SamplerIntegrator for PathIntegrator {
50    fn preprocess(&mut self, scene: &Scene, _sampler: &mut dyn Sampler) {
51        match create_light_sample_distribution(&self.light_sample_sterategy, scene) {
52            Ok(distrib) => {
53                self.light_distribution = Some(distrib);
54            }
55            Err(e) => {
56                println!("{:}", e);
57            }
58        }
59    }
60
61    fn li(
62        &self,
63        r: &RayDifferential,
64        scene: &Scene,
65        sampler: &mut dyn Sampler,
66        arena: &mut MemoryArena,
67        _depth: i32,
68    ) -> Spectrum {
69        let _p = ProfilePhase::new(Prof::SamplerIntegratorLi);
70
71        let light_distribution = self.light_distribution.as_ref();
72        if light_distribution.is_none() {
73            return Spectrum::zero();
74        }
75        let light_distribution = light_distribution.unwrap();
76
77        let max_depth = self.max_depth;
78        let mut ray = r.clone();
79        let mut eta_scale = 1.0;
80
81        let mut l = Spectrum::zero();
82        let mut beta = Spectrum::one();
83        let mut specular_bounce = false;
84        let mut bounces = 0;
85        loop {
86            let found_intersection = scene.intersect(&ray.ray);
87            if bounces == 0 || specular_bounce {
88                if let Some(isect) = found_intersection.as_ref() {
89                    let w = -(ray.ray.d);
90                    l += beta * isect.le(&w);
91                } else {
92                    for light in scene.infinite_lights.iter() {
93                        l += beta * light.le(&ray);
94                    }
95                }
96                assert!(beta.y() >= 0.0);
97                assert!(l.y() >= -1e-5);
98            }
99
100            // Terminate path if ray escaped or _maxDepth_ was reached
101            if found_intersection.is_none() || bounces >= max_depth {
102                break;
103            }
104
105            let mut isect = found_intersection.unwrap();
106            isect.compute_scattering_functions(&ray, arena, TransportMode::Radiance, true);
107
108            let tisect = Interaction::from(&isect);
109            //let isect = tisect.as_surface_interaction().unwrap();
110
111            if isect.bsdf.is_none() {
112                ray = isect.spawn_ray(&ray.ray.d).into();
113                continue;
114            }
115
116            let bsdf = isect.bsdf.as_ref().unwrap();
117            let distrib = light_distribution.lookup(&isect.p);
118
119            // assert!(beta.max_component_value() <= 1.0);
120            // Sample illumination from lights to find path contribution.
121            // (But skip this for perfectly specular BSDFs.)
122            if bsdf.num_components(BSDF_ALL & !(BSDF_SPECULAR as u32)) > 0 {
123                PATHS.with(|stat| stat.add_denom(1)); //totalPaths
124
125                let ld = beta
126                    * uniform_sample_one_light(
127                        &tisect,
128                        scene,
129                        arena,
130                        sampler,
131                        false,
132                        Some(distrib.deref()),
133                    );
134                if ld.is_black() {
135                    PATHS.with(|stat| stat.add_num(1)); //zeroRadiancePaths
136                }
137                assert!(beta.y() >= 0.0);
138                //assert!(ld.y() >= -1e-5); //Should be true
139                l += ld;
140            }
141
142            // Sample BSDF to get new path direction
143            let wo = -ray.ray.d;
144            if let Some((f, wi, pdf, flags)) = bsdf.sample_f(&wo, &sampler.get_2d(), BSDF_ALL) {
145                if f.is_black() || pdf == 0.0 {
146                    break;
147                }
148                assert!(isect.n.length() > 0.0);
149                assert!(isect.shading.n.length() > 0.0);
150                assert!(Float::is_finite(beta.y()));
151
152                beta *= f * (Vector3f::abs_dot(&wi, &isect.shading.n) / pdf);
153                assert!(Float::is_finite(beta.y()));
154
155                specular_bounce = (flags & BSDF_SPECULAR) != 0;
156                let flag_s = (flags & BSDF_SPECULAR) != 0;
157                let flag_t = (flags & BSDF_TRANSMISSION) != 0;
158
159                if flag_s && flag_t {
160                    let eta = bsdf.eta;
161                    // Update the term that tracks radiance scaling for refraction
162                    // depending on whether the ray is entering or leaving the
163                    // medium.
164                    eta_scale *= if Vector3f::dot(&wo, &isect.n) > 0.0 {
165                        eta * eta
166                    } else {
167                        1.0 / (eta * eta)
168                    }
169                }
170                ray = isect.spawn_ray(&wi).into();
171
172                // Account for subsurface scattering, if applicable
173                if let Some(bssrdf) = isect.bssrdf.as_ref() {
174                    if flag_t {
175                        let light_distribution = light_distribution.as_ref();
176                        if let Some((s, pi, pdf)) =
177                            bssrdf.sample_s(scene, sampler.get_1d(), &sampler.get_2d(), arena)
178                        {
179                            if s.is_black() || pdf == 0.0 {
180                                break;
181                            }
182                            beta *= s / pdf;
183
184                            let tpi = Interaction::from(&pi);
185                            //let pi = tisect.as_surface_interaction().unwrap();
186
187                            // Account for the direct subsurface scattering component
188                            let distrib = light_distribution.lookup(&pi.p);
189                            l += beta
190                                * uniform_sample_one_light(
191                                    &tpi,
192                                    scene,
193                                    arena,
194                                    sampler,
195                                    false,
196                                    Some(distrib.as_ref()),
197                                );
198
199                            // Account for the indirect subsurface scattering component
200                            if let Some(pi_bsdf) = pi.bsdf.as_ref() {
201                                if let Some((f, wi, pdf, flags)) =
202                                    pi_bsdf.sample_f(&pi.wo, &sampler.get_2d(), BSDF_ALL)
203                                {
204                                    assert!(isect.n.length() > 0.0);
205                                    assert!(isect.shading.n.length() > 0.0);
206
207                                    if f.is_black() || pdf == 0.0 {
208                                        break;
209                                    }
210
211                                    beta *= f * (Vector3f::abs_dot(&wi, &pi.shading.n) / pdf);
212                                    assert!(Float::is_finite(beta.y()));
213                                    specular_bounce = (flags & BSDF_SPECULAR) != 0;
214                                    ray = pi.spawn_ray(&wi).into();
215                                } else {
216                                    break;
217                                }
218                            }
219                        } else {
220                            break;
221                        }
222                    }
223                }
224
225                // Possibly terminate the path with Russian roulette.
226                // Factor out radiance scaling due to refraction in rrBeta.
227                let rr_threshold = self.rr_threshold;
228                let rr_beta = beta * eta_scale;
229                if rr_beta.max_component_value() < rr_threshold && bounces > 3 {
230                    let q = Float::max(0.05, 1.0 - rr_beta.max_component_value());
231                    if sampler.get_1d() < q {
232                        break;
233                    }
234                    beta /= 1.0 - q;
235                }
236            } else {
237                break;
238            }
239            bounces += 1;
240        }
241
242        {
243            PATH_LENGTH.with(|stat| stat.add(bounces as u64));
244        }
245
246        return l;
247    }
248
249    fn get_sampler(&self) -> Arc<RwLock<dyn Sampler>> {
250        return Arc::clone(&self.base.sampler);
251    }
252
253    fn get_pixel_bounds(&self) -> Bounds2i {
254        return self.base.pixel_bounds;
255    }
256}
257
258pub fn create_path_integrator(
259    params: &ParamSet,
260    sampler: &Arc<RwLock<dyn Sampler>>,
261    camera: &Arc<dyn Camera>,
262) -> Result<Arc<RwLock<dyn Integrator>>, PbrtError> {
263    let max_depth = params.find_one_int("maxdepth", 5);
264    let film = camera.as_ref().get_film();
265    let pixel_bounds = film.read().unwrap().get_sample_bounds();
266
267    let rr_threshold = params.find_one_float("rrthreshold", 1.0);
268    let light_strategy = params.find_one_string("lightsamplestrategy", "spatial");
269    return Ok(Arc::new(RwLock::new(PathIntegrator::new(
270        max_depth,
271        camera,
272        sampler,
273        &pixel_bounds,
274        rr_threshold,
275        &light_strategy,
276    ))));
277}