1use super::subpath::*;
2use crate::core::prelude::*;
3
4use crate::filters::create_box_filter;
5use log::*;
6use rayon::iter::IntoParallelRefMutIterator;
7use rayon::iter::ParallelIterator;
8use std::collections::HashMap;
9use std::path::Path;
10use std::sync::Arc;
11use std::sync::Mutex;
12use std::sync::RwLock;
13use std::time::Instant;
14
15const UPDATE_DISPLAY_INTERVAL: u128 = 1000;
16
17pub struct BDPTIntegrator {
18 sampler: Arc<RwLock<dyn Sampler>>,
19 camera: Arc<dyn Camera>,
20 max_depth: i32,
21 visualize_strategies: bool,
22 visualize_weights: bool,
23 pixel_bounds: Bounds2i,
24 light_sample_strategy: String,
25}
26
27impl BDPTIntegrator {
28 pub fn new(
29 sampler: &Arc<RwLock<dyn Sampler>>,
30 camera: &Arc<dyn Camera>,
31 max_depth: i32,
32 visualize_strategies: bool,
33 visualize_weights: bool,
34 pixel_bounds: &Bounds2i,
35 light_sample_sterategy: &str,
36 ) -> Self {
37 BDPTIntegrator {
38 sampler: sampler.clone(),
39 camera: camera.clone(),
40 max_depth,
41 visualize_strategies: visualize_strategies,
42 visualize_weights: visualize_weights,
43 pixel_bounds: *pixel_bounds,
44 light_sample_strategy: light_sample_sterategy.to_string(),
45 }
46 }
47}
48
49impl Integrator for BDPTIntegrator {
59 fn render(&mut self, scene: &Scene) {
60 let light_distribution =
61 create_light_sample_distribution(&self.light_sample_strategy, scene);
62 if let Err(e) = light_distribution {
63 error!("Unable to create light sample distribution: {}", e);
64 return;
65 }
66 let light_distribution = light_distribution.unwrap();
67 let mut light_to_index = LightIndexMap::default();
72 for (i, light) in scene.lights.iter().enumerate() {
73 let light = light.as_ref();
74 let light_ptr = light as *const dyn Light;
75 let key = LightKeyType::from(light_ptr);
76 light_to_index.insert(key, i);
77 }
78
79 let cropped_pixel_bounds = {
80 let film = self.camera.get_film();
81 let film = film.read().unwrap();
82 film.cropped_pixel_bounds.clone()
83 };
84
85 let samples_per_pixel = self.sampler.read().unwrap().get_samples_per_pixel();
86
87 let prev_time = Arc::new(Mutex::new(Instant::now()));
88
89 let mut tile_indices = Vec::new();
91 {
92 let sampler = self.sampler.clone();
95 let sample_bounds = self.pixel_bounds;
97 let sample_extent = sample_bounds.diagonal();
98 const TILE_SIZE: i32 = 16;
99 let n_tiles = Point2i::from((
100 (sample_extent.x + TILE_SIZE - 1) / TILE_SIZE,
101 (sample_extent.y + TILE_SIZE - 1) / TILE_SIZE,
102 ));
103 tile_indices.reserve((n_tiles.x * n_tiles.y) as usize);
104 for y in 0..n_tiles.y {
105 for x in 0..n_tiles.x {
106 let x0 = sample_bounds.min.x + x * TILE_SIZE;
107 let x1 = i32::min(x0 + TILE_SIZE, sample_bounds.max.x);
108 let y0 = sample_bounds.min.y + y * TILE_SIZE;
109 let y1 = i32::min(y0 + TILE_SIZE, sample_bounds.max.y);
110 let tile_bounds = Bounds2i::from(((x0, y0), (x1, y1)));
111
112 let seed = (y * n_tiles.x + x) as u32;
113 let s = sampler.read().unwrap().clone_with_seed(seed);
114 let proxy = ProxySampler::new(&s);
115 tile_indices.push((tile_bounds, proxy));
118 }
119 }
120 }
121
122 {
123 let camera = self.camera.as_ref();
124 let film = camera.get_film();
125 let mut film = film.write().unwrap();
126 film.render_start();
127 }
128
129 let total_tiles = tile_indices.len();
130 let reporter = Arc::new(RwLock::new(ProgressReporter::new(total_tiles, "Rendering")));
131
132 let max_depth = self.max_depth;
133 let visualize_strategies = self.visualize_strategies;
134 let visualize_weights = self.visualize_weights;
135 let visualize_info = visualize_strategies || visualize_weights;
136
137 let mut weight_films = HashMap::new();
140 if visualize_info {
141 let camera = self.camera.as_ref();
142 let film = camera.get_film();
143 let film = film.read().unwrap();
144 let filename = film.filename.clone();
145 let filename = Path::new(&filename);
146 let dir = filename.parent().unwrap();
147 let basename = filename.file_stem().unwrap();
148 let dir = dir.join(basename);
149 let full_resolution = film.full_resolution;
150 let diagonal = film.diagonal * 1000.0;
151
152 let _ = std::fs::create_dir(&dir);
153 for depth in 0..=max_depth {
154 for s in 0..=(depth + 2) {
155 let t = depth + 2 - s;
156 if t == 0 || (s == 1 && t == 1) {
157 continue;
158 }
159
160 let filename =
161 dir.join(format!("bdpt_d{:>02}_s{:>02}_t{:02}.exr", depth, s, t));
162 let filename = filename.to_str().unwrap();
163 let crop_window = Bounds2f::from(((0.0, 0.0), (1.0, 1.0)));
165 let filter = create_box_filter(&ParamSet::new()).unwrap();
166 let new_film = Film::new(
167 &full_resolution,
168 &crop_window,
169 &filter,
170 diagonal,
171 filename,
172 1.0,
173 Float::INFINITY,
174 );
175 weight_films.insert((s, t), Arc::new(RwLock::new(new_film)));
176 }
177 }
178 }
180
181 let max_depth = self.max_depth as usize;
182 if !scene.lights.is_empty() {
184 let camera = self.camera.clone();
185 let film: Arc<RwLock<Film>> = camera.as_ref().get_film();
186 let proxy_film = Arc::new(Mutex::new(ProxyFilm::new(&film)));
187 {
192 tile_indices
193 .par_iter_mut()
194 .for_each(|(tile_bounds, tile_sampler)| {
195 let mut arena = MemoryArena::new();
197
198 let mut camera_vertices = Vec::with_capacity(max_depth + 2);
199 let mut light_vertices = Vec::with_capacity(max_depth + 1);
200
201 let x0 = tile_bounds.min.x;
202 let x1 = tile_bounds.max.x;
203 let y0 = tile_bounds.min.y;
204 let y1 = tile_bounds.max.y;
205
206 let mut film_tile = proxy_film
207 .as_ref()
208 .lock()
209 .unwrap()
210 .get_film_tile(tile_bounds);
211
212 for yy in y0..y1 {
213 for xx in x0..x1 {
214 let p_pixel = Point2i::from((xx, yy));
215 tile_sampler.start_pixel(&p_pixel);
216
217 loop {
218 let p_film =
221 Point2f::new(p_pixel.x as Float, p_pixel.y as Float)
222 + tile_sampler.get_2d();
223
224 camera_vertices.clear();
226 light_vertices.clear();
227
228 let n_camera = generate_camera_subpath(
229 scene,
230 tile_sampler,
231 &mut arena,
232 max_depth + 2,
233 &camera,
234 &p_film,
235 &mut camera_vertices,
236 );
237 if n_camera <= 0 {
245 break;
246 }
247
248 let camera_vertex = camera_vertices[0].as_ref();
249
250 let p = camera_vertex.get_p();
251 let light_distribution = light_distribution.as_ref();
252 let light_distr = light_distribution.lookup(&p);
253 let time = camera_vertex.get_time();
256 let n_light = generate_light_subpath(
257 scene,
258 tile_sampler,
259 &mut arena,
260 max_depth + 1,
261 time,
262 &light_distr,
263 &light_to_index,
264 &mut light_vertices,
265 );
266
267 let mut l = Spectrum::zero();
268 let n_camera = n_camera as i32;
269 let n_light = n_light as i32;
270 for t in 1..=n_camera {
271 for s in 0..=n_light {
272 let depth = (s + t) - 2;
273
274 if (s == 1 && t == 1)
275 || depth < 0
276 || depth > max_depth as i32
277 {
278 continue;
279 }
280 if let Some((l_path, mis_weight, p_film_new)) =
283 connect_bdpt(
284 scene,
285 &light_vertices,
286 &camera_vertices,
287 s,
288 t,
289 &light_distr,
290 &light_to_index,
291 &camera,
292 tile_sampler,
293 &p_film,
294 )
295 {
296 if visualize_info {
298 let value = if visualize_strategies {
299 if mis_weight <= 0.0 {
300 Spectrum::zero()
301 } else {
302 l_path / mis_weight
303 }
304 } else {
305 l_path
306 };
307 let mut film = weight_films
308 .get(&(s, t))
309 .unwrap()
310 .write()
311 .unwrap();
312 film.add_splat(&p_film_new, &value);
313 }
314 if t != 1 {
315 l += l_path;
316 } else {
317 let mut film =
318 proxy_film.as_ref().lock().unwrap();
319 film.add_splat(&p_film_new, &l_path);
320 }
321 }
322 }
323 }
324
325 film_tile.add_sample(&p_film, &l, 1.0);
326 arena.reset();
327
328 if !tile_sampler.start_next_sample() {
329 break;
330 }
331 }
332 }
333 }
334
335 {
336 let mut film = proxy_film.as_ref().lock().unwrap();
337 film.merge_film_tile(&film_tile);
338 film.update_display(&film_tile.pixel_bounds);
339 {
340 let mut prev_time = prev_time.lock().unwrap();
341 let elapsed = prev_time.elapsed();
342 if elapsed.as_millis() > UPDATE_DISPLAY_INTERVAL {
343 film.merge_splats(1.0 / samples_per_pixel as Float);
344 film.update_display(&cropped_pixel_bounds);
345 *prev_time = Instant::now();
346 }
347 }
348 }
349 {
350 let mut reporter = reporter.write().unwrap();
351 reporter.update(1);
352 }
353 });
354 }
355 }
356
357 {
358 let camera = self.camera.as_ref();
359 let film = camera.get_film();
360 let mut film = film.as_ref().write().unwrap();
361 film.merge_splats(1.0 / samples_per_pixel as Float);
362 film.update_display(&cropped_pixel_bounds);
363 film.render_end();
364 film.write_image();
365 }
366
367 if visualize_info {
368 for film in weight_films.values() {
369 let mut film = film.write().unwrap();
370 film.merge_splats(1.0);
371 film.write_image();
372 }
373 }
374
375 {
376 let mut reporter = reporter.write().unwrap();
377 reporter.done();
378 }
379 }
380
381 fn get_camera(&self) -> Arc<dyn Camera> {
382 return self.camera.clone();
383 }
384}
385
386unsafe impl Sync for BDPTIntegrator {}
387
388pub fn create_bdpt_integrator(
389 params: &ParamSet,
390 sampler: &Arc<RwLock<dyn Sampler>>,
391 camera: &Arc<dyn Camera>,
392) -> Result<Arc<RwLock<dyn Integrator>>, PbrtError> {
393 let max_depth = params.find_one_int("maxdepth", 5);
394 let visualize_strategies = params.find_one_bool("visualizestrategies", false);
395 let visualize_weights = params.find_one_bool("visualizeweights", false);
396 let pixel_bounds = camera.get_film().read().unwrap().get_sample_bounds();
398 let light_sample_strategy = params.find_one_string("lightsamplestrategy", "power");
400 return Ok(Arc::new(RwLock::new(BDPTIntegrator::new(
401 sampler,
402 camera,
403 max_depth,
404 visualize_strategies,
405 visualize_weights,
406 &pixel_bounds,
407 &light_sample_strategy,
408 ))));
409}