pbrt_r3/integrators/
bdpt.rs

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
49/*
50#[inline]
51fn buffer_index(s: i32, t: i32) -> usize {
52    let above = s + t - 2;
53    assert!(above >= 0);
54    return (s + above * (5 + above) / 2) as usize;
55}
56*/
57
58impl 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        // Compute a reverse mapping from light pointers to offsets into the
68        // scene lights vector (and, equivalently, offsets into
69        // lightDistr). Added after book text was finalized; this is critical
70        // to reasonable performance with 100s+ of light sources.
71        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        // Partition the image into tiles
90        let mut tile_indices = Vec::new();
91        {
92            //let film = self.camera.as_ref().get_film();
93            //let film = film.read().unwrap();
94            let sampler = self.sampler.clone();
95            //let sample_bounds = film.get_sample_bounds();
96            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                    //let camera = self.camera.clone();
116                    //let camera = Arc::downgrade(&camera);
117                    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        // Allocate buffers for debug visualization
138        // let buffer_count = ((1 + max_depth) * (6 * max_depth) / 2) as usize;
139        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                    //println!("Creating film for depth {}, s {}, t {} as {}", depth, s, t, filename);
164                    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            //assert!(weight_films.len() == buffer_count);
179        }
180
181        let max_depth = self.max_depth as usize;
182        // Render and write the output image to disk
183        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            //let filename = proxy_film.as_ref().lock().unwrap().get_filename();
188
189            //let total = tile_indices.len();
190
191            {
192                tile_indices
193                    .par_iter_mut()
194                    .for_each(|(tile_bounds, tile_sampler)| {
195                        //let camera = self.camera.clone();
196                        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                                    //println!("p_pixel: {:?}", p_pixel);
219                                    // Generate a single sample using BDPT
220                                    let p_film =
221                                        Point2f::new(p_pixel.x as Float, p_pixel.y as Float)
222                                            + tile_sampler.get_2d();
223
224                                    // Trace the camera subpath
225                                    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                                    // Get a distribution for sampling the light at the
238                                    // start of the light subpath. Because the light path
239                                    // follows multiple bounces, basing the sampling
240                                    // distribution on any of the vertices of the camera
241                                    // path is unlikely to be a good strategy. We use the
242                                    // PowerLightDistribution by default here, which
243                                    // doesn't use the point passed to it.
244                                    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                                    // Now trace the light subpath
254
255                                    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                                            // Execute the $(s, t)$ connection strategy and
281                                            // update _L_
282                                            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                                                //assert!(!l_path.is_black());
297                                                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 p =
397    let pixel_bounds = camera.get_film().read().unwrap().get_sample_bounds();
398    //todo
399    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}