pbrt_r3/integrators/
path.rs1use 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 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 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 if bsdf.num_components(BSDF_ALL & !(BSDF_SPECULAR as u32)) > 0 {
123 PATHS.with(|stat| stat.add_denom(1)); 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)); }
137 assert!(beta.y() >= 0.0);
138 l += ld;
140 }
141
142 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 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 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 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 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 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}