Blender V4.3
bvh/split.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2009-2010 NVIDIA Corporation
2 * SPDX-FileCopyrightText: 2011-2022 Blender Foundation
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 *
6 * Adapted code from NVIDIA Corporation. */
7
8#include "bvh/split.h"
9
10#include "bvh/build.h"
11#include "bvh/sort.h"
12
13#include "scene/hair.h"
14#include "scene/mesh.h"
15#include "scene/object.h"
16#include "scene/pointcloud.h"
17
18#include "util/algorithm.h"
19
21
22/* Object Split */
23
25 BVHSpatialStorage *storage,
26 const BVHRange &range,
27 vector<BVHReference> &references,
28 float nodeSAH,
29 const BVHUnaligned *unaligned_heuristic,
30 const Transform *aligned_space)
31 : sah(FLT_MAX),
32 dim(0),
33 num_left(0),
34 left_bounds(BoundBox::empty),
35 right_bounds(BoundBox::empty),
36 storage_(storage),
37 references_(&references),
38 unaligned_heuristic_(unaligned_heuristic),
39 aligned_space_(aligned_space)
40{
41 const BVHReference *ref_ptr = &references_->at(range.start());
42 float min_sah = FLT_MAX;
43
44 storage_->right_bounds.resize(range.size());
45
46 for (int dim = 0; dim < 3; dim++) {
47 /* Sort references. */
48 bvh_reference_sort(range.start(),
49 range.end(),
50 &references_->at(0),
51 dim,
54
55 /* sweep right to left and determine bounds. */
57 for (int i = range.size() - 1; i > 0; i--) {
58 BoundBox prim_bounds = get_prim_bounds(ref_ptr[i]);
59 right_bounds.grow(prim_bounds);
61 }
62
63 /* sweep left to right and select lowest SAH. */
65
66 for (int i = 1; i < range.size(); i++) {
67 BoundBox prim_bounds = get_prim_bounds(ref_ptr[i - 1]);
68 left_bounds.grow(prim_bounds);
70
71 float sah = nodeSAH + left_bounds.safe_area() * builder->params.primitive_cost(i) +
72 right_bounds.safe_area() * builder->params.primitive_cost(range.size() - i);
73
74 if (sah < min_sah) {
75 min_sah = sah;
76
77 this->sah = sah;
78 this->dim = dim;
79 this->num_left = i;
80 this->left_bounds = left_bounds;
81 this->right_bounds = right_bounds;
82 }
83 }
84 }
85}
86
87void BVHObjectSplit::split(BVHRange &left, BVHRange &right, const BVHRange &range)
88{
89 assert(references_->size() > 0);
90 /* sort references according to split */
91 bvh_reference_sort(range.start(),
92 range.end(),
93 &references_->at(0),
94 this->dim,
97
98 BoundBox effective_left_bounds, effective_right_bounds;
99 const int num_right = range.size() - this->num_left;
100 if (aligned_space_ == NULL) {
101 effective_left_bounds = left_bounds;
102 effective_right_bounds = right_bounds;
103 }
104 else {
105 effective_left_bounds = BoundBox::empty;
106 effective_right_bounds = BoundBox::empty;
107 for (int i = 0; i < this->num_left; ++i) {
108 BoundBox prim_boundbox = references_->at(range.start() + i).bounds();
109 effective_left_bounds.grow(prim_boundbox);
110 }
111 for (int i = 0; i < num_right; ++i) {
112 BoundBox prim_boundbox = references_->at(range.start() + this->num_left + i).bounds();
113 effective_right_bounds.grow(prim_boundbox);
114 }
115 }
116
117 /* split node ranges */
118 left = BVHRange(effective_left_bounds, range.start(), this->num_left);
119 right = BVHRange(effective_right_bounds, left.end(), num_right);
120}
121
122/* Spatial Split */
123
125 BVHSpatialStorage *storage,
126 const BVHRange &range,
127 vector<BVHReference> &references,
128 float nodeSAH,
129 const BVHUnaligned *unaligned_heuristic,
130 const Transform *aligned_space)
131 : sah(FLT_MAX),
132 dim(0),
133 pos(0.0f),
134 storage_(storage),
135 references_(&references),
136 unaligned_heuristic_(unaligned_heuristic),
137 aligned_space_(aligned_space)
138{
139 /* initialize bins. */
140 BoundBox range_bounds;
141 if (aligned_space == NULL) {
142 range_bounds = range.bounds();
143 }
144 else {
145 range_bounds = unaligned_heuristic->compute_aligned_boundbox(
146 range, &references_->at(0), *aligned_space);
147 }
148
149 float3 origin = range_bounds.min;
150 float3 binSize = (range_bounds.max - origin) * (1.0f / (float)BVHParams::NUM_SPATIAL_BINS);
151 float3 invBinSize = 1.0f / binSize;
152
153 for (int dim = 0; dim < 3; dim++) {
154 for (int i = 0; i < BVHParams::NUM_SPATIAL_BINS; i++) {
155 BVHSpatialBin &bin = storage_->bins[dim][i];
156
158 bin.enter = 0;
159 bin.exit = 0;
160 }
161 }
162
163 /* chop references into bins. */
164 for (unsigned int refIdx = range.start(); refIdx < range.end(); refIdx++) {
165 const BVHReference &ref = references_->at(refIdx);
166 BoundBox prim_bounds = get_prim_bounds(ref);
167 float3 firstBinf = (prim_bounds.min - origin) * invBinSize;
168 float3 lastBinf = (prim_bounds.max - origin) * invBinSize;
169 int3 firstBin = make_int3((int)firstBinf.x, (int)firstBinf.y, (int)firstBinf.z);
170 int3 lastBin = make_int3((int)lastBinf.x, (int)lastBinf.y, (int)lastBinf.z);
171
172 firstBin = clamp(firstBin, 0, BVHParams::NUM_SPATIAL_BINS - 1);
173 lastBin = clamp(lastBin, firstBin, BVHParams::NUM_SPATIAL_BINS - 1);
174
175 for (int dim = 0; dim < 3; dim++) {
176 BVHReference currRef(
177 get_prim_bounds(ref), ref.prim_index(), ref.prim_object(), ref.prim_type());
178
179 for (int i = firstBin[dim]; i < lastBin[dim]; i++) {
180 BVHReference leftRef, rightRef;
181
183 builder, leftRef, rightRef, currRef, dim, origin[dim] + binSize[dim] * (float)(i + 1));
184 storage_->bins[dim][i].bounds.grow(leftRef.bounds());
185 currRef = rightRef;
186 }
187
188 storage_->bins[dim][lastBin[dim]].bounds.grow(currRef.bounds());
189 storage_->bins[dim][firstBin[dim]].enter++;
190 storage_->bins[dim][lastBin[dim]].exit++;
191 }
192 }
193
194 /* select best split plane. */
196 for (int dim = 0; dim < 3; dim++) {
197 /* sweep right to left and determine bounds. */
198 BoundBox right_bounds = BoundBox::empty;
199 for (int i = BVHParams::NUM_SPATIAL_BINS - 1; i > 0; i--) {
200 right_bounds.grow(storage_->bins[dim][i].bounds);
201 storage_->right_bounds[i - 1] = right_bounds;
202 }
203
204 /* sweep left to right and select lowest SAH. */
205 BoundBox left_bounds = BoundBox::empty;
206 int leftNum = 0;
207 int rightNum = range.size();
208
209 for (int i = 1; i < BVHParams::NUM_SPATIAL_BINS; i++) {
210 left_bounds.grow(storage_->bins[dim][i - 1].bounds);
211 leftNum += storage_->bins[dim][i - 1].enter;
212 rightNum -= storage_->bins[dim][i - 1].exit;
213
214 float sah = nodeSAH + left_bounds.safe_area() * builder.params.primitive_cost(leftNum) +
215 storage_->right_bounds[i - 1].safe_area() *
216 builder.params.primitive_cost(rightNum);
217
218 if (sah < this->sah) {
219 this->sah = sah;
220 this->dim = dim;
221 this->pos = origin[dim] + binSize[dim] * (float)i;
222 }
223 }
224 }
225}
226
228 BVHRange &left,
229 BVHRange &right,
230 const BVHRange &range)
231{
232 /* Categorize references and compute bounds.
233 *
234 * Left-hand side: [left_start, left_end[
235 * Uncategorized/split: [left_end, right_start[
236 * Right-hand side: [right_start, refs.size()[ */
237
239 int left_start = range.start();
240 int left_end = left_start;
241 int right_start = range.end();
242 int right_end = range.end();
243 BoundBox left_bounds = BoundBox::empty;
244 BoundBox right_bounds = BoundBox::empty;
245
246 for (int i = left_end; i < right_start; i++) {
247 BoundBox prim_bounds = get_prim_bounds(refs[i]);
248 if (prim_bounds.max[this->dim] <= this->pos) {
249 /* entirely on the left-hand side */
250 left_bounds.grow(prim_bounds);
251 swap(refs[i], refs[left_end++]);
252 }
253 else if (prim_bounds.min[this->dim] >= this->pos) {
254 /* entirely on the right-hand side */
255 right_bounds.grow(prim_bounds);
256 swap(refs[i--], refs[--right_start]);
257 }
258 }
259
260 /* Duplicate or unsplit references intersecting both sides.
261 *
262 * Duplication happens into a temporary pre-allocated vector in order to
263 * reduce number of memmove() calls happening in vector.insert().
264 */
266 new_refs.clear();
267 new_refs.reserve(right_start - left_end);
268 while (left_end < right_start) {
269 /* split reference. */
270 BVHReference curr_ref(get_prim_bounds(refs[left_end]),
271 refs[left_end].prim_index(),
272 refs[left_end].prim_object(),
273 refs[left_end].prim_type());
274 BVHReference lref, rref;
275 split_reference(*builder, lref, rref, curr_ref, this->dim, this->pos);
276
277 /* compute SAH for duplicate/unsplit candidates. */
278 BoundBox lub = left_bounds; // Unsplit to left: new left-hand bounds.
279 BoundBox rub = right_bounds; // Unsplit to right: new right-hand bounds.
280 BoundBox ldb = left_bounds; // Duplicate: new left-hand bounds.
281 BoundBox rdb = right_bounds; // Duplicate: new right-hand bounds.
282
283 lub.grow(curr_ref.bounds());
284 rub.grow(curr_ref.bounds());
285 ldb.grow(lref.bounds());
286 rdb.grow(rref.bounds());
287
288 float lac = builder->params.primitive_cost(left_end - left_start);
289 float rac = builder->params.primitive_cost(right_end - right_start);
290 float lbc = builder->params.primitive_cost(left_end - left_start + 1);
291 float rbc = builder->params.primitive_cost(right_end - right_start + 1);
292
293 float unsplitLeftSAH = lub.safe_area() * lbc + right_bounds.safe_area() * rac;
294 float unsplitRightSAH = left_bounds.safe_area() * lac + rub.safe_area() * rbc;
295 float duplicateSAH = ldb.safe_area() * lbc + rdb.safe_area() * rbc;
296 float minSAH = min(min(unsplitLeftSAH, unsplitRightSAH), duplicateSAH);
297
298 if (minSAH == unsplitLeftSAH) {
299 /* unsplit to left */
300 left_bounds = lub;
301 left_end++;
302 }
303 else if (minSAH == unsplitRightSAH) {
304 /* unsplit to right */
305 right_bounds = rub;
306 swap(refs[left_end], refs[--right_start]);
307 }
308 else {
309 /* duplicate */
310 left_bounds = ldb;
311 right_bounds = rdb;
312 refs[left_end++] = lref;
313 new_refs.push_back(rref);
314 right_end++;
315 }
316 }
317 /* Insert duplicated references into actual array in one go. */
318 if (new_refs.size() != 0) {
319 refs.insert(refs.begin() + (right_end - new_refs.size()), new_refs.begin(), new_refs.end());
320 }
321 if (aligned_space_ != NULL) {
322 left_bounds = right_bounds = BoundBox::empty;
323 for (int i = left_start; i < left_end - left_start; ++i) {
324 BoundBox prim_boundbox = references_->at(i).bounds();
325 left_bounds.grow(prim_boundbox);
326 }
327 for (int i = right_start; i < right_end - right_start; ++i) {
328 BoundBox prim_boundbox = references_->at(i).bounds();
329 right_bounds.grow(prim_boundbox);
330 }
331 }
332 left = BVHRange(left_bounds, left_start, left_end - left_start);
333 right = BVHRange(right_bounds, right_start, right_end - right_start);
334}
335
337 const Transform *tfm,
338 int prim_index,
339 int dim,
340 float pos,
341 BoundBox &left_bounds,
342 BoundBox &right_bounds)
343{
344 Mesh::Triangle t = mesh->get_triangle(prim_index);
345 const float3 *verts = &mesh->verts[0];
346 float3 v1 = tfm ? transform_point(tfm, verts[t.v[2]]) : verts[t.v[2]];
347 v1 = get_unaligned_point(v1);
348
349 for (int i = 0; i < 3; i++) {
350 float3 v0 = v1;
351 int vindex = t.v[i];
352 v1 = tfm ? transform_point(tfm, verts[vindex]) : verts[vindex];
353 v1 = get_unaligned_point(v1);
354 float v0p = v0[dim];
355 float v1p = v1[dim];
356
357 /* insert vertex to the boxes it belongs to. */
358 if (v0p <= pos) {
359 left_bounds.grow(v0);
360 }
361
362 if (v0p >= pos) {
363 right_bounds.grow(v0);
364 }
365
366 /* edge intersects the plane => insert intersection to both boxes. */
367 if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
368 float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
369 left_bounds.grow(t);
370 right_bounds.grow(t);
371 }
372 }
373}
374
376 const Transform *tfm,
377 int prim_index,
378 int segment_index,
379 int dim,
380 float pos,
381 BoundBox &left_bounds,
382 BoundBox &right_bounds)
383{
384 /* curve split: NOTE - Currently ignores curve width and needs to be fixed. */
385 Hair::Curve curve = hair->get_curve(prim_index);
386 const int k0 = curve.first_key + segment_index;
387 const int k1 = k0 + 1;
388 float3 v0 = hair->get_curve_keys()[k0];
389 float3 v1 = hair->get_curve_keys()[k1];
390
391 if (tfm != NULL) {
392 v0 = transform_point(tfm, v0);
393 v1 = transform_point(tfm, v1);
394 }
395 v0 = get_unaligned_point(v0);
396 v1 = get_unaligned_point(v1);
397
398 float v0p = v0[dim];
399 float v1p = v1[dim];
400
401 /* insert vertex to the boxes it belongs to. */
402 if (v0p <= pos) {
403 left_bounds.grow(v0);
404 }
405
406 if (v0p >= pos) {
407 right_bounds.grow(v0);
408 }
409
410 if (v1p <= pos) {
411 left_bounds.grow(v1);
412 }
413
414 if (v1p >= pos) {
415 right_bounds.grow(v1);
416 }
417
418 /* edge intersects the plane => insert intersection to both boxes. */
419 if ((v0p < pos && v1p > pos) || (v0p > pos && v1p < pos)) {
420 float3 t = mix(v0, v1, clamp((pos - v0p) / (v1p - v0p), 0.0f, 1.0f));
421 left_bounds.grow(t);
422 right_bounds.grow(t);
423 }
424}
425
427 const Transform *tfm,
428 int prim_index,
429 int dim,
430 float pos,
431 BoundBox &left_bounds,
432 BoundBox &right_bounds)
433{
434 /* No real splitting support for points, assume they are small enough for it
435 * not to matter. */
436 float3 point = pointcloud->get_points()[prim_index];
437 float radius = pointcloud->get_radius()[prim_index];
438
439 if (tfm != NULL) {
440 point = transform_point(tfm, point);
441 }
442 point = get_unaligned_point(point);
443
444 if (point[dim] <= pos) {
445 left_bounds.grow(point, radius);
446 }
447
448 if (point[dim] >= pos) {
449 right_bounds.grow(point, radius);
450 }
451}
452
454 const Mesh *mesh,
455 int dim,
456 float pos,
457 BoundBox &left_bounds,
458 BoundBox &right_bounds)
459{
460 split_triangle_primitive(mesh, NULL, ref.prim_index(), dim, pos, left_bounds, right_bounds);
461}
462
464 const Hair *hair,
465 int dim,
466 float pos,
467 BoundBox &left_bounds,
468 BoundBox &right_bounds)
469{
471 NULL,
472 ref.prim_index(),
474 dim,
475 pos,
476 left_bounds,
477 right_bounds);
478}
479
481 const PointCloud *pointcloud,
482 int dim,
483 float pos,
484 BoundBox &left_bounds,
485 BoundBox &right_bounds)
486{
487 split_point_primitive(pointcloud, NULL, ref.prim_index(), dim, pos, left_bounds, right_bounds);
488}
489
491 const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
492{
493 Geometry *geom = object->get_geometry();
494
496 Mesh *mesh = static_cast<Mesh *>(geom);
497 for (int tri_idx = 0; tri_idx < mesh->num_triangles(); ++tri_idx) {
499 mesh, &object->get_tfm(), tri_idx, dim, pos, left_bounds, right_bounds);
500 }
501 }
502 else if (geom->geometry_type == Geometry::HAIR) {
503 Hair *hair = static_cast<Hair *>(geom);
504 for (int curve_idx = 0; curve_idx < hair->num_curves(); ++curve_idx) {
505 Hair::Curve curve = hair->get_curve(curve_idx);
506 for (int segment_idx = 0; segment_idx < curve.num_keys - 1; ++segment_idx) {
508 hair, &object->get_tfm(), curve_idx, segment_idx, dim, pos, left_bounds, right_bounds);
509 }
510 }
511 }
512 else if (geom->geometry_type == Geometry::POINTCLOUD) {
513 PointCloud *pointcloud = static_cast<PointCloud *>(geom);
514 for (int point_idx = 0; point_idx < pointcloud->num_points(); ++point_idx) {
516 pointcloud, &object->get_tfm(), point_idx, dim, pos, left_bounds, right_bounds);
517 }
518 }
519}
520
522 BVHReference &left,
523 BVHReference &right,
524 const BVHReference &ref,
525 int dim,
526 float pos)
527{
528 /* Initialize bounding-boxes. */
529 BoundBox left_bounds = BoundBox::empty;
530 BoundBox right_bounds = BoundBox::empty;
531
532 /* loop over vertices/edges. */
533 const Object *ob = builder.objects[ref.prim_object()];
534
535 if (ref.prim_type() & PRIMITIVE_TRIANGLE) {
536 Mesh *mesh = static_cast<Mesh *>(ob->get_geometry());
537 split_triangle_reference(ref, mesh, dim, pos, left_bounds, right_bounds);
538 }
539 else if (ref.prim_type() & PRIMITIVE_CURVE) {
540 Hair *hair = static_cast<Hair *>(ob->get_geometry());
541 split_curve_reference(ref, hair, dim, pos, left_bounds, right_bounds);
542 }
543 else if (ref.prim_type() & PRIMITIVE_POINT) {
544 PointCloud *pointcloud = static_cast<PointCloud *>(ob->get_geometry());
545 split_point_reference(ref, pointcloud, dim, pos, left_bounds, right_bounds);
546 }
547 else {
548 split_object_reference(ob, dim, pos, left_bounds, right_bounds);
549 }
550
551 /* intersect with original bounds. */
552 left_bounds.max[dim] = pos;
553 right_bounds.min[dim] = pos;
554
555 left_bounds.intersect(ref.bounds());
556 right_bounds.intersect(ref.bounds());
557
558 /* set references */
559 left = BVHReference(left_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
560 right = BVHReference(right_bounds, ref.prim_index(), ref.prim_object(), ref.prim_type());
561}
562
vector< Object * > objects
Definition build.h:97
BVHParams params
Definition build.h:110
BVHSpatialStorage * storage_
Definition bvh/split.h:44
vector< BVHReference > * references_
Definition bvh/split.h:45
BoundBox left_bounds
Definition bvh/split.h:29
BoundBox right_bounds
Definition bvh/split.h:30
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
Definition bvh/split.h:49
void split(BVHRange &left, BVHRange &right, const BVHRange &range)
Definition bvh/split.cpp:87
const Transform * aligned_space_
Definition bvh/split.h:47
const BVHUnaligned * unaligned_heuristic_
Definition bvh/split.h:46
@ NUM_SPATIAL_BINS
Definition params.h:112
__forceinline float primitive_cost(int n) const
Definition params.h:154
__forceinline int prim_type() const
Definition params.h:217
__forceinline int prim_object() const
Definition params.h:213
__forceinline const BoundBox & bounds() const
Definition params.h:205
__forceinline int prim_index() const
Definition params.h:209
void split_point_primitive(const PointCloud *pointcloud, const Transform *tfm, int prim_index, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
void split_curve_primitive(const Hair *hair, const Transform *tfm, int prim_index, int segment_index, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
void split_curve_reference(const BVHReference &ref, const Hair *hair, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
void split_triangle_primitive(const Mesh *mesh, const Transform *tfm, int prim_index, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
__forceinline float3 get_unaligned_point(const float3 &point) const
Definition bvh/split.h:157
__forceinline BoundBox get_prim_bounds(const BVHReference &prim) const
Definition bvh/split.h:147
BVHSpatialStorage * storage_
Definition bvh/split.h:87
void split_object_reference(const Object *object, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
const Transform * aligned_space_
Definition bvh/split.h:90
vector< BVHReference > * references_
Definition bvh/split.h:88
void split_triangle_reference(const BVHReference &ref, const Mesh *mesh, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
void split_point_reference(const BVHReference &ref, const PointCloud *pointcloud, int dim, float pos, BoundBox &left_bounds, BoundBox &right_bounds)
void split_reference(const BVHBuild &builder, BVHReference &left, BVHReference &right, const BVHReference &ref, int dim, float pos)
void split(BVHBuild *builder, BVHRange &left, BVHRange &right, const BVHRange &range)
BoundBox compute_aligned_boundbox(const BVHObjectBinning &range, const BVHReference *references, const Transform &aligned_space, BoundBox *cent_bounds=NULL) const
Definition unaligned.cpp:98
Type geometry_type
Definition hair.h:14
#define CCL_NAMESPACE_END
#define NULL
ccl_device_forceinline int3 make_int3(const int x, const int y, const int z)
draw_view in_light_buf[] float
static float verts[][3]
#define mix(a, b, c)
Definition hash.h:36
@ PRIMITIVE_CURVE
@ PRIMITIVE_TRIANGLE
@ PRIMITIVE_POINT
#define PRIMITIVE_UNPACK_SEGMENT(type)
#define swap(a, b)
Definition sort.c:55
#define min(a, b)
Definition sort.c:32
void bvh_reference_sort(int start, int end, BVHReference *data, int dim, const BVHUnaligned *unaligned_heuristic, const Transform *aligned_space)
Definition sort.cpp:162
#define FLT_MAX
Definition stdcycles.h:14
BoundBox bounds
Definition params.h:308
vector< BVHReference > new_references
Definition params.h:332
vector< BoundBox > right_bounds
Definition params.h:324
BVHSpatialBin bins[3][BVHParams::NUM_SPATIAL_BINS]
Definition params.h:327
float3 max
Definition boundbox.h:21
__forceinline float3 size() const
Definition boundbox.h:123
__forceinline void intersect(const BoundBox &bbox)
Definition boundbox.h:86
__forceinline float safe_area() const
Definition boundbox.h:93
__forceinline void grow(const float3 &pt)
Definition boundbox.h:36
float3 min
Definition boundbox.h:21
int first_key
Definition hair.h:20
size_t num_points() const
float z
Definition sky_float3.h:27
float y
Definition sky_float3.h:27
float x
Definition sky_float3.h:27
CCL_NAMESPACE_END CCL_NAMESPACE_BEGIN ccl_device_inline float3 transform_point(ccl_private const Transform *t, const float3 a)
Definition transform.h:63
ccl_device_inline int clamp(int a, int mn, int mx)
Definition util/math.h:379