pbrt_r3/accelerators/kdtree/
accel.rs

1use crate::core::error::*;
2use crate::core::geometry::*;
3use crate::core::interaction::*;
4use crate::core::param_set::*;
5use crate::core::primitive::*;
6use crate::core::profile::*;
7use crate::core::shape::*;
8
9use std::sync::Arc;
10
11// KDTreeAccel Local Declarations
12#[derive(Clone, Copy, Debug)]
13enum KDAccelNode {
14    Leaf {
15        primitive_indices_offset: usize,
16        n_primitives: usize,
17    },
18    Interior {
19        axis: u8,
20        split: Float,
21        above_child: usize,
22    },
23}
24
25impl KDAccelNode {
26    fn init_leaf(prim_nums: &[usize], primitive_indices: &mut Vec<usize>) -> Self {
27        let primitive_indices_offset = primitive_indices.len();
28        let n_primitives = prim_nums.len();
29        for i in 0..prim_nums.len() {
30            primitive_indices.push(prim_nums[i]);
31        }
32        KDAccelNode::Leaf {
33            primitive_indices_offset,
34            n_primitives,
35        }
36    }
37    fn init_interior(axis: u8, split: Float, above_child: usize) -> Self {
38        KDAccelNode::Interior {
39            axis,
40            split,
41            above_child,
42        }
43    }
44}
45
46#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug)]
47enum EdgeType {
48    Start,
49    End,
50}
51
52impl Default for EdgeType {
53    fn default() -> Self {
54        EdgeType::Start
55    }
56}
57
58#[derive(Clone, Copy, Default, Debug)]
59struct BoundEdge {
60    t: Float,
61    prim_num: usize,
62    edge_type: EdgeType,
63}
64
65struct KDTreeBuilder {
66    pub isect_cost: i32,
67    pub traversal_cost: i32,
68    pub max_prims: usize,
69    pub empty_bonus: Float,
70}
71
72impl KDTreeBuilder {
73    pub fn build_tree(
74        &mut self,
75        node_bounds: &Bounds3f,
76        all_prim_bounds: &[Bounds3f],
77        prim_nums: &[usize],
78        nodes: &mut Vec<KDAccelNode>,
79        primitive_indices: &mut Vec<usize>,
80        depth: i32,
81        bad_refines: u32,
82    ) -> usize {
83        let node_num = nodes.len();
84        // Initialize leaf node if termination criteria met
85        let n_primitives = prim_nums.len();
86        if n_primitives <= self.max_prims || depth == 0 {
87            nodes.push(KDAccelNode::init_leaf(prim_nums, primitive_indices));
88            return node_num;
89        }
90
91        // Choose split axis position for interior node
92        if let Some((best_axis, t_split, primes0, primes1, bad_refines)) =
93            self.choose_split_axis(node_bounds, all_prim_bounds, prim_nums, bad_refines)
94        {
95            assert!(best_axis < 3);
96
97            // Recursively initialize children nodes
98            let mut bounds0 = node_bounds.clone();
99            let mut bounds1 = node_bounds.clone();
100            bounds0.max[best_axis] = t_split;
101            bounds1.min[best_axis] = t_split;
102            nodes.push(KDAccelNode::init_interior(best_axis as u8, t_split, 0)); //dummy value
103
104            let left_index = self.build_tree(
105                &bounds0,
106                all_prim_bounds,
107                &primes0,
108                nodes,
109                primitive_indices,
110                depth - 1,
111                bad_refines,
112            );
113            let right_index = self.build_tree(
114                &bounds1,
115                all_prim_bounds,
116                &primes1,
117                nodes,
118                primitive_indices,
119                depth - 1,
120                bad_refines,
121            );
122            assert_eq!(node_num + 1, left_index);
123            nodes[node_num] = KDAccelNode::init_interior(best_axis as u8, t_split, right_index);
124        } else {
125            nodes.push(KDAccelNode::init_leaf(prim_nums, primitive_indices));
126        }
127        return node_num;
128    }
129
130    fn choose_split_axis(
131        &self,
132        node_bounds: &Bounds3f,
133        all_prim_bounds: &[Bounds3f],
134        prim_nums: &[usize],
135        bad_refines: u32,
136    ) -> Option<(usize, Float, Vec<usize>, Vec<usize>, u32)> {
137        let n_primitives = prim_nums.len();
138
139        // Choose split axis position for interior node
140        let mut best_axis: i32 = -1;
141        let mut best_offset: i64 = -1;
142        let mut best_cost = Float::INFINITY;
143        let old_cost = self.isect_cost as Float * n_primitives as Float;
144        let total_sa = node_bounds.surface_area();
145        let inv_total_sa = 1.0 / total_sa;
146        let traversal_cost = self.traversal_cost as Float;
147        let isect_cost = self.isect_cost as Float;
148        let empty_bonus = self.empty_bonus;
149        let d = node_bounds.max - node_bounds.min;
150
151        // Initialize interior node and continue recursion
152        let mut edges = vec![vec![BoundEdge::default(); 2 * n_primitives]; 3];
153
154        // Choose which axis to split along
155        let mut axis = node_bounds.maximum_extent();
156        let mut retries = 0;
157        loop {
158            // Initialize edges for _axis_
159            for i in 0..n_primitives {
160                let pn = prim_nums[i];
161                let bounds = &all_prim_bounds[pn];
162                edges[axis][2 * i + 0] = BoundEdge {
163                    t: bounds.min[axis],
164                    prim_num: pn,
165                    edge_type: EdgeType::Start,
166                };
167                edges[axis][2 * i + 1] = BoundEdge {
168                    t: bounds.max[axis],
169                    prim_num: pn,
170                    edge_type: EdgeType::End,
171                };
172            }
173
174            // Sort _edges_ for _axis_
175            edges[axis].sort_by(|a, b| {
176                if a.t == b.t {
177                    return a.edge_type.partial_cmp(&b.edge_type).unwrap();
178                } else {
179                    return a.t.partial_cmp(&b.t).unwrap();
180                }
181            });
182
183            let n_edges = 2 * n_primitives;
184
185            // Compute cost of all splits for _axis_ to find best
186            let mut n_below = 0;
187            let mut n_above = n_primitives;
188            for i in 0..n_edges {
189                if edges[axis][i].edge_type == EdgeType::End {
190                    n_above -= 1;
191                }
192                let edge_t = edges[axis][i].t;
193                if edge_t > node_bounds.min[axis] && edge_t < node_bounds.max[axis] {
194                    //Compute cost for split at _i_th edge
195                    let other_axis0 = (axis + 1) % 3;
196                    let other_axis1 = (axis + 2) % 3;
197                    let below_sa = 2.0
198                        * (d[other_axis0] * d[other_axis1]
199                            + (edge_t - node_bounds.min[axis]) * (d[other_axis0] + d[other_axis1]));
200                    let above_sa = 2.0
201                        * (d[other_axis0] * d[other_axis1]
202                            + (node_bounds.max[axis] - edge_t) * (d[other_axis0] + d[other_axis1]));
203                    let p_below = below_sa * inv_total_sa;
204                    let p_above = above_sa * inv_total_sa;
205                    let eb = if n_above == 0 || n_below == 0 {
206                        empty_bonus
207                    } else {
208                        0.0
209                    };
210                    let cost = traversal_cost
211                        * isect_cost
212                        * (1.0 - eb)
213                        * (p_below * n_below as Float + p_above * n_above as Float);
214                    if cost < best_cost {
215                        best_cost = cost;
216                        best_axis = axis as i32;
217                        best_offset = i as i64;
218                    }
219                }
220                if edges[axis][i].edge_type == EdgeType::Start {
221                    n_below += 1;
222                }
223            }
224
225            // Create leaf if no good splits were found
226            if best_axis < 0 && retries < 2 {
227                retries += 1;
228                axis = (axis + 1) % 3;
229                continue;
230            }
231            break;
232        }
233
234        let mut bad_refines = bad_refines;
235        if best_cost > old_cost {
236            bad_refines += 1;
237        }
238        if (best_cost > 4.0 * old_cost && n_primitives < 16) || bad_refines == 3 {
239            return None;
240        }
241
242        if best_axis >= 0 && best_offset >= 0 {
243            let best_axis = best_axis as usize;
244            let best_offset = best_offset as usize;
245            let t_split = edges[best_axis][best_offset].t;
246
247            // Classify primitives with respect to split
248            let mut primes0 = Vec::new();
249            let mut primes1 = Vec::new();
250            for i in 0..best_offset {
251                if edges[best_axis][i].edge_type == EdgeType::Start {
252                    primes0.push(edges[best_axis][i].prim_num);
253                }
254            }
255            for i in (best_offset + 1)..(2 * n_primitives) {
256                if edges[best_axis][i].edge_type == EdgeType::End {
257                    primes1.push(edges[best_axis][i].prim_num);
258                }
259            }
260
261            let length0 = primes0.len();
262            let length1 = primes1.len();
263            if length0 == 0 || length1 == 0 {
264                return None;
265            }
266
267            return Some((best_axis, t_split, primes0, primes1, bad_refines));
268        } else {
269            return None;
270        }
271    }
272}
273
274pub struct KDTreeAccel {
275    primitives: Vec<Arc<dyn Primitive>>,
276    primitive_indices: Vec<usize>,
277    nodes: Vec<KDAccelNode>,
278    bounds: Bounds3f,
279}
280
281impl KDTreeAccel {
282    pub fn new(
283        prims: &[Arc<dyn Primitive>],
284        isect_cost: i32,
285        traversal_cost: i32,
286        empty_bonus: Float,
287        max_primes: i32,
288        max_depth: i32,
289    ) -> Self {
290        // Build kd-tree for accelerator
291        let _p = ProfilePhase::new(Prof::AccelConstruction);
292
293        let primitives = prims.to_vec();
294
295        let max_depth = if max_depth <= 0 {
296            (8.0 + 1.3 * (prims.len() as f32).log2()).round() as i32
297        } else {
298            max_depth
299        };
300
301        // Compute bounds for kd-tree construction
302        let mut bounds = prims[0].world_bound();
303        let mut prim_bounds: Vec<Bounds3f> = Vec::with_capacity(primitives.len());
304        for prim in primitives.iter() {
305            let b = prim.world_bound();
306            bounds = Bounds3f::union(&bounds, &b);
307            prim_bounds.push(b);
308        }
309
310        // Initialize _primNums_ for kd-tree construction
311        let mut prim_nums = vec![0; primitives.len()];
312        for i in 0..primitives.len() {
313            prim_nums[i] = i;
314        }
315
316        // Start recursive construction of kd-tree
317        let mut builder = KDTreeBuilder {
318            isect_cost,
319            traversal_cost,
320            max_prims: max_primes as usize,
321            empty_bonus,
322        };
323        let mut nodes = Vec::new();
324        let mut primitive_indices = Vec::new();
325        let root_index = builder.build_tree(
326            &bounds,
327            &prim_bounds,
328            &mut prim_nums,
329            &mut nodes,
330            &mut primitive_indices,
331            max_depth,
332            0,
333        );
334        assert!(root_index == 0);
335        return KDTreeAccel {
336            primitives,
337            primitive_indices,
338            nodes,
339            bounds,
340        };
341    }
342}
343
344type KDTodo = (usize, Float, Float);
345
346//pbrt-r3:
347#[inline]
348fn safe_recip(x: Float) -> Float {
349    if x != 0.0 {
350        return Float::recip(x);
351    } else {
352        return 0.0; //std::f32::INFINITY;
353    }
354}
355
356impl Primitive for KDTreeAccel {
357    fn world_bound(&self) -> Bounds3f {
358        return self.bounds;
359    }
360    fn intersect(&self, r: &Ray) -> Option<SurfaceInteraction> {
361        let _p = ProfilePhase::new(Prof::AccelIntersect);
362
363        // Compute initial parametric range of ray inside kd-tree extent
364        let (t_min, t_max) = self.bounds.intersect_p(r)?;
365
366        let mut isect = None;
367        // Prepare to traverse kd-tree for ray
368        let inv_dir = [safe_recip(r.d.x), safe_recip(r.d.y), safe_recip(r.d.z)];
369        const MAX_TODO: usize = 64;
370        let mut todo: [KDTodo; MAX_TODO] = [(0, 0.0, 0.0); MAX_TODO];
371        let mut todo_pos: i32 = 0;
372        todo[todo_pos as usize] = (0, t_min, t_max);
373        while todo_pos >= 0 {
374            let (node_num, t_min, t_max) = todo[todo_pos as usize];
375            todo_pos -= 1;
376            let node = &self.nodes[node_num];
377            // Bail out if we found a hit closer than the current node
378            if r.t_max.get() < t_min {
379                break;
380            }
381            match node {
382                KDAccelNode::Interior {
383                    axis,
384                    split,
385                    above_child,
386                } => {
387                    // Process kd-tree interior node
388
389                    // Compute parametric distance along ray to split plane
390                    let split = *split;
391                    let axis = *axis as usize;
392                    let above_child = *above_child as usize;
393                    let t_plane = (split - r.o[axis]) * inv_dir[axis];
394                    let below_first = r.o[axis] < split || (r.o[axis] == split && r.d[axis] <= 0.0);
395                    let (first_index, second_index) = if below_first {
396                        (node_num + 1, above_child)
397                    } else {
398                        (above_child, node_num + 1)
399                    };
400
401                    // Advance to next child node, possibly enqueue other child
402                    if t_plane > t_max || t_plane < 0.0 {
403                        todo_pos += 1;
404                        todo[todo_pos as usize] = (first_index, t_min, t_max);
405                    } else if t_plane < t_min {
406                        todo_pos += 1;
407                        todo[todo_pos as usize] = (second_index, t_min, t_max);
408                    } else {
409                        todo_pos += 1;
410                        todo[todo_pos as usize] = (second_index, t_plane, t_max);
411                        todo_pos += 1;
412                        todo[todo_pos as usize] = (first_index, t_min, t_plane);
413                    }
414                }
415                KDAccelNode::Leaf {
416                    primitive_indices_offset,
417                    n_primitives,
418                } => {
419                    // Check for intersections inside leaf node
420                    let primitive_indices_offset = *primitive_indices_offset;
421                    let n_primitives = *n_primitives;
422                    for i in 0..n_primitives {
423                        let index = self.primitive_indices[primitive_indices_offset + i];
424                        let prim = &self.primitives[index];
425                        if let Some(mut isect_n) = prim.intersect(r) {
426                            if prim.is_geometric() {
427                                isect_n.primitive = Some(Arc::downgrade(prim));
428                            }
429                            isect = Some(isect_n);
430                        }
431                    }
432                }
433            }
434        }
435        return isect;
436    }
437
438    fn intersect_p(&self, r: &Ray) -> bool {
439        let _p = ProfilePhase::new(Prof::AccelIntersectP);
440
441        // Compute initial parametric range of ray inside kd-tree extent
442        let (t_min, t_max) = if let Some((t_min, t_max)) = self.bounds.intersect_p(r) {
443            (t_min, t_max)
444        } else {
445            return false;
446        };
447
448        // Prepare to traverse kd-tree for ray
449        let inv_dir = [safe_recip(r.d.x), safe_recip(r.d.y), safe_recip(r.d.z)];
450        const MAX_TODO: usize = 64;
451        let mut todo: [KDTodo; MAX_TODO] = [(0, 0.0, 0.0); MAX_TODO];
452        let mut todo_pos: i32 = 0;
453        todo[todo_pos as usize] = (0, t_min, t_max);
454        while todo_pos >= 0 {
455            let (node_num, t_min, t_max) = todo[todo_pos as usize];
456            todo_pos -= 1;
457            let node = &self.nodes[node_num];
458            // Bail out if we found a hit closer than the current node
459            if r.t_max.get() < t_min {
460                break;
461            }
462            match node {
463                KDAccelNode::Interior {
464                    split,
465                    axis,
466                    above_child,
467                } => {
468                    // Process kd-tree interior node
469
470                    // Compute parametric distance along ray to split plane
471                    let split = *split;
472                    let axis = *axis as usize;
473                    let above_child = *above_child as usize;
474                    let t_plane = (split - r.o[axis]) * inv_dir[axis];
475                    let below_first = r.o[axis] < split || (r.o[axis] == split && r.d[axis] <= 0.0);
476                    let (first_index, second_index) = if below_first {
477                        (node_num + 1, above_child)
478                    } else {
479                        (above_child, node_num + 1)
480                    };
481
482                    // Advance to next child node, possibly enqueue other child
483                    if t_plane > t_max || t_plane < 0.0 {
484                        todo_pos += 1;
485                        todo[todo_pos as usize] = (first_index, t_min, t_max);
486                    } else if t_plane < t_min {
487                        todo_pos += 1;
488                        todo[todo_pos as usize] = (second_index, t_min, t_max);
489                    } else {
490                        todo_pos += 1;
491                        todo[todo_pos as usize] = (second_index, t_plane, t_max);
492                        todo_pos += 1;
493                        todo[todo_pos as usize] = (first_index, t_min, t_plane);
494                    }
495                }
496                KDAccelNode::Leaf {
497                    primitive_indices_offset,
498                    n_primitives,
499                } => {
500                    // Check for intersections inside leaf node
501
502                    let primitive_indices_offset = *primitive_indices_offset;
503                    let n_primitives = *n_primitives;
504                    for i in 0..n_primitives {
505                        let index = self.primitive_indices[primitive_indices_offset + i];
506                        if self.primitives[index].intersect_p(r) {
507                            return true;
508                        }
509                    }
510                }
511            }
512        }
513        return false;
514    }
515}
516
517impl Aggregate for KDTreeAccel {}
518
519pub fn create_kdtree_accelerator(
520    prims: &[Arc<dyn Primitive>],
521    params: &ParamSet,
522) -> Result<Arc<dyn Primitive>, PbrtError> {
523    let isect_cost = params.find_one_int("intersectcost", 80);
524    let trav_cost = params.find_one_int("traversalcost", 1);
525    let empty_bonus = params.find_one_float("emptybonus", 0.5);
526    let max_primes = params.find_one_int("maxprims", 1);
527    let max_depth = params.find_one_int("maxdepth", -1);
528    return Ok(Arc::new(KDTreeAccel::new(
529        prims,
530        isect_cost,
531        trav_cost,
532        empty_bonus,
533        max_primes,
534        max_depth,
535    )));
536}