1use crate::core::prelude::*;
2
3use std::sync::Arc;
4use std::sync::RwLock;
5
6thread_local!(static PATH_LENGTH: StatIntDistribution = StatIntDistribution::new("Integrator/Path length"));
7thread_local!(static VOLUME_INTERACTIONS: StatCounter = StatCounter::new("Integrator/Volume interactions"));
8thread_local!(static SURFACE_INTERACTIONS: StatCounter = StatCounter::new("Integrator/Surface interactions"));
9
10pub struct VolPathIntegrator {
11 pub base: BaseSamplerIntegrator,
12 pub max_depth: u32,
13 pub rr_threshold: Float,
14 pub light_sample_strategy: String,
15 pub light_distribution: Option<Arc<dyn LightDistribution>>,
16}
17
18impl VolPathIntegrator {
19 pub fn new(
20 max_depth: u32,
21 camera: &Arc<dyn Camera>,
22 sampler: &Arc<RwLock<dyn Sampler>>,
23 pixel_bounds: &Bounds2i,
24 rr_threshold: Float,
25 light_sample_strategy: String,
26 ) -> Self {
27 let base = BaseSamplerIntegrator::new(camera, sampler, pixel_bounds);
28 VolPathIntegrator {
29 base,
30 max_depth,
31 rr_threshold,
32 light_sample_strategy,
33 light_distribution: None,
34 }
35 }
36}
37
38impl Integrator for VolPathIntegrator {
39 fn render(&mut self, scene: &Scene) {
40 BaseSamplerIntegrator::render(self, scene);
41 }
42 fn get_camera(&self) -> Arc<dyn Camera> {
43 self.base.camera.clone()
44 }
45}
46
47unsafe impl Sync for VolPathIntegrator {}
48
49impl SamplerIntegrator for VolPathIntegrator {
50 fn preprocess(&mut self, scene: &Scene, _sampler: &mut dyn Sampler) {
51 match create_light_sample_distribution(&self.light_sample_strategy, 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 rr_threshold = self.rr_threshold;
72 let mut l = Spectrum::zero();
73 let mut beta = Spectrum::one();
74 let mut ray = r.clone();
75 let mut specular_bounce = false;
76
77 let light_distr = self.light_distribution.clone();
78 let light_distr = light_distr.unwrap();
79
80 let mut eta_scale = 1.0;
88
89 let mut bounces = 0;
90 loop {
91 let mut found_intersection = scene.intersect(&ray.ray);
95
96 let mut mi = None;
97 if let Some(medium) = ray.ray.medium.as_ref() {
99 let (spec, m) = medium.sample(&ray.ray, sampler, arena);
101 if let Some(mut m) = m {
102 m.medium_interface = MediumInterface::from(medium);
103 mi = Some(m);
104 }
105 beta *= spec;
106 }
107 if beta.is_black() {
108 break;
109 }
110
111 if let Some(mi) = mi {
112 if bounces >= self.max_depth {
114 break;
115 }
116
117 VOLUME_INTERACTIONS.with(|c| c.inc());
118
119 let light_distrib = light_distr.lookup(&mi.p);
121 let mit = Interaction::from(&mi);
122 let ld = beta
123 * uniform_sample_one_light(
124 &mit,
125 scene,
126 arena,
127 sampler,
128 true,
129 Some(&light_distrib),
130 );
131 l += ld;
132 let wo = -ray.ray.d;
133 let (_pdf, wi) = mi.phase.sample_p(&wo, &sampler.get_2d());
134 ray = mi.spawn_ray(&wi).into();
135 specular_bounce = false;
136 } else {
137 SURFACE_INTERACTIONS.with(|c| c.inc());
138 if bounces == 0 || specular_bounce {
142 if let Some(isect) = found_intersection.as_ref() {
144 l += beta * isect.le(&-ray.ray.d);
145 } else {
146 for light in scene.infinite_lights.iter() {
147 l += beta * light.le(&ray);
148 }
149 }
150 }
151
152 if found_intersection.is_none() || bounces >= self.max_depth {
154 break;
155 }
156
157 let isect = found_intersection.as_mut().unwrap();
159 isect.compute_scattering_functions(&ray, arena, TransportMode::Radiance, true);
161 if isect.bsdf.is_none() {
162 ray = isect.spawn_ray(&ray.ray.d).into();
163 continue;
164 }
165 let distrib = light_distr.lookup(&isect.p);
166
167 {
169 let tisect = Interaction::from(isect as &SurfaceInteraction);
170 let ld = beta
171 * uniform_sample_one_light(
172 &tisect,
173 scene,
174 arena,
175 sampler,
176 true,
177 Some(&distrib),
178 );
179 l += ld;
180 }
181
182 let bsdf = isect.bsdf.as_ref().unwrap();
184 let wo = -ray.ray.d;
185 if let Some((f, wi, pdf, flags)) = bsdf.sample_f(&wo, &sampler.get_2d(), BSDF_ALL) {
186 if f.is_black() || pdf == 0.0 {
187 break;
188 }
189 beta *= f * (wi.abs_dot(&isect.shading.n) / pdf);
190 specular_bounce = (flags & BSDF_SPECULAR) != 0;
193 let flag_s = (flags & BSDF_SPECULAR) != 0;
194 let flag_t = (flags & BSDF_TRANSMISSION) != 0;
195 if flag_s && flag_t {
196 let eta = bsdf.eta;
197 if Vector3f::dot(&wo, &isect.n) > 0.0 {
201 eta_scale *= eta * eta;
202 } else {
203 eta_scale *= 1.0 / (eta * eta);
204 }
205 }
206
207 ray = isect.spawn_ray(&wi).into();
208
209 if let Some(bssrdf) = isect.bssrdf.as_ref() {
211 if flag_t {
212 if let Some((s, pi, pdf)) = bssrdf.as_ref().sample_s(
214 scene,
215 sampler.get_1d(),
216 &sampler.get_2d(),
217 arena,
218 ) {
219 if s.is_black() || pdf == 0.0 {
220 break;
221 }
222 beta *= s / pdf;
223 let distrib = light_distr.lookup(&pi.p);
227 let pit = Interaction::from(&pi);
228 l += beta
229 * uniform_sample_one_light(
230 &pit,
231 scene,
232 arena,
233 sampler,
234 true,
235 Some(&distrib),
236 );
237
238 if let Some(pi_bsdf) = pi.bsdf.as_ref() {
240 if let Some((f, wi, pdf, flags)) =
241 pi_bsdf.sample_f(&pi.wo, &sampler.get_2d(), BSDF_ALL)
242 {
243 if f.is_black() || pdf == 0.0 {
244 break;
245 }
246 beta *= f * (wi.abs_dot(&pi.shading.n) / pdf);
247 specular_bounce = (flags & BSDF_SPECULAR) != 0;
249 ray = pi.spawn_ray(&wi).into();
250 } else {
251 break;
252 }
253 } else {
254 break;
255 }
256 }
257 }
258 }
259 } else {
260 break;
261 }
262 }
263
264 let rr_beta = beta * eta_scale;
267 if rr_beta.max_component_value() < rr_threshold && bounces > 3 {
268 let q = Float::max(0.05, 1.0 - rr_beta.max_component_value());
269 if sampler.get_1d() < q {
270 break;
271 }
272 beta /= 1.0 - q;
273 assert!(beta.max_component_value().is_finite());
274 }
275
276 bounces += 1;
277 } PATH_LENGTH.with(|c| c.add(bounces as u64));
280
281 return l;
282 }
283
284 fn get_sampler(&self) -> Arc<RwLock<dyn Sampler>> {
285 return self.base.sampler.clone();
286 }
287
288 fn get_pixel_bounds(&self) -> Bounds2i {
289 return self.base.pixel_bounds;
290 }
291}
292
293pub fn create_volpath_integrator(
294 params: &ParamSet,
295 sampler: &Arc<RwLock<dyn Sampler>>,
296 camera: &Arc<dyn Camera>,
297) -> Result<Arc<RwLock<dyn Integrator>>, PbrtError> {
298 let max_depth = params.find_one_int("maxdepth", 5) as u32;
299
300 let pixel_bounds = camera.get_film().read().unwrap().get_sample_bounds();
301
302 let rr_threshold = params.find_one_float("rrthreshold", 1.0);
303 let light_sample_strategy = params.find_one_string("lightsamplestrategy", "spatial");
304 return Ok(Arc::new(RwLock::new(VolPathIntegrator::new(
305 max_depth,
306 camera,
307 sampler,
308 &pixel_bounds,
309 rr_threshold,
310 light_sample_strategy,
311 ))));
312}