MobileRT  1.0
A multi platform C++ CPU progressive Ray Tracer.
BVH.hpp
Go to the documentation of this file.
1 #ifndef MOBILERT_ACCELERATORS_BVH_HPP
2 #define MOBILERT_ACCELERATORS_BVH_HPP
3 
6 #include "MobileRT/Scene.hpp"
7 #include <algorithm>
8 #include <array>
9 #include <boost/sort/spreadsort/spreadsort.hpp>
10 #include <glm/glm.hpp>
11 #include <random>
12 #include <vector>
13 
14 namespace MobileRT {
15 
21  template<typename T>
22  class BVH final {
23  private:
28  struct BuildNode {
29  AABB box_ {};
30  ::glm::vec3 centroid_ {};
31  ::std::int32_t oldIndex_ {};
32 
36  explicit BuildNode() = default;
37 
45  explicit BuildNode(AABB &&box, const ::std::int32_t oldIndex) :
46  box_ {box},
48  oldIndex_ {oldIndex} {
49 
50  }
51  };
52 
56  struct BVHNode {
57  AABB box_ {};
58  ::std::int32_t indexOffset_ {};
59  ::std::int32_t numPrimitives_ {};
60  };
61 
62  struct rightshift {
64  rightshift(const int longestAxis) noexcept : longestAxis_{longestAxis} { }
65 
66  int operator()(const BuildNode &node, const unsigned offset) const {
67  return ::boost::sort::spreadsort::float_mem_cast<float, int>(node.centroid_[this->longestAxis_]) >> offset;
68  }
69  };
70 
71  struct lessthan {
73  lessthan(const int longestAxis) noexcept : longestAxis_{longestAxis} { }
74 
75  bool operator()(const BuildNode &node1, const BuildNode &node2) const {
76  return node1.centroid_[this->longestAxis_] < node2.centroid_[this->longestAxis_];
77  }
78  };
79 
80  private:
81  ::std::vector<BVHNode> boxes_ {};
82  ::std::vector<T> primitives_ {};
83 
84  private:
85  void build(::std::vector<T> &&primitives);
86 
87  Intersection intersect(Intersection intersection);
88 
89  template<typename Iterator>
90  ::std::int32_t getSplitIndexSah(Iterator itBegin, Iterator itEnd);
91 
92  template<typename Iterator>
93  AABB getSurroundingBox(Iterator itBegin, Iterator itEnd);
94 
95  public:
96  explicit BVH() = default;
97 
98  explicit BVH(::std::vector<T> &&primitives);
99 
100  BVH(const BVH &bvh) = delete;
101 
102  BVH(BVH &&bvh) noexcept = default;
103 
104  ~BVH();
105 
106  BVH &operator=(const BVH &bvh) = delete;
107 
108  BVH &operator=(BVH &&bvh) noexcept = default;
109 
110  Intersection trace(Intersection intersection);
111 
112  Intersection shadowTrace(Intersection intersection);
113 
114  const ::std::vector<T>& getPrimitives() const;
115  };
116 
117 
118 
125  template<typename T>
126  BVH<T>::BVH(::std::vector<T> &&primitives) {
127  if (primitives.empty()) {
128  this->boxes_.emplace_back(BVHNode {});
129  LOG_WARN("Empty BVH for '", typeid(T).name(), "' without any primitives.");
130  return;
131  }
132  const typename ::std::vector<T>::size_type numPrimitives {primitives.size()};
133  const typename ::std::vector<T>::size_type maxNodes {numPrimitives * 2 - 1};
134  this->boxes_.resize(maxNodes);
135  LOG_INFO("Building BVH for '", typeid(T).name(), "' with '", numPrimitives, "' primitives.");
136  build(::std::move(primitives));
137  LOG_INFO("Built BVH for '", typeid(T).name(), "' with '", this->primitives_.size(), "' primitives in '", this->boxes_.size(), "' boxes.");
138  }
139 
145  template<typename T>
147  this->boxes_.clear();
148  this->primitives_.clear();
149 
150  ::std::vector<BVHNode> {}.swap(this->boxes_);
151  ::std::vector<T> {}.swap(this->primitives_);
152  }
153 
160  template<typename T>
161  void BVH<T>::build(::std::vector<T> &&primitives) {
162  ::std::int32_t currentBoxIndex {};
163  ::std::int32_t beginBoxIndex {};
164  const long long unsigned primitivesSize {primitives.size()};
165  ::std::int32_t endBoxIndex {static_cast<::std::int32_t> (primitivesSize)};
166  ::std::int32_t maxNodeIndex {};
167 
168  ::std::array<::std::int32_t, StackSize> stackBoxIndex {};
169  ::std::array<::std::int32_t, StackSize> stackBoxBegin {};
170  ::std::array<::std::int32_t, StackSize> stackBoxEnd {};
171 
172  ::std::array<::std::int32_t, StackSize>::iterator itStackBoxIndex {stackBoxIndex.begin()};
173  ::std::advance(itStackBoxIndex, 1);
174 
175  ::std::array<::std::int32_t, StackSize>::iterator itStackBoxBegin {stackBoxBegin.begin()};
176  ::std::advance(itStackBoxBegin, 1);
177 
178  ::std::array<::std::int32_t, StackSize>::iterator itStackBoxEnd {stackBoxEnd.begin()};
179  ::std::advance(itStackBoxEnd, 1);
180 
181  const ::std::array<::std::int32_t, StackSize>::const_iterator itStackBoxIndexBegin {stackBoxIndex.cbegin()};
182 
183  // Auxiliary structure used to sort all the AABBs by the position of the centroid.
184  ::std::vector<BuildNode> buildNodes {};
185  buildNodes.reserve(static_cast<long unsigned> (primitivesSize));
186  for (::std::uint32_t i {}; i < primitivesSize; ++i) {
187  const T &primitive {primitives [i]};
188  AABB &&box {primitive.getAABB()};
189  buildNodes.emplace_back(BuildNode {::std::move(box), static_cast<::std::int32_t> (i)});
190  }
191 
192  do {
193  const auto itCurrentBox {this->boxes_.begin() + currentBoxIndex};
194  const ::std::int32_t boxPrimitivesSize {endBoxIndex - beginBoxIndex};
195  const auto itBegin {buildNodes.begin() + beginBoxIndex};
196 
197  const auto itEnd {buildNodes.begin() + endBoxIndex};
198  const AABB surroundingBox {getSurroundingBox(itBegin, itEnd)};
199  const ::glm::vec3 maxDist {surroundingBox.getPointMax() - surroundingBox.getPointMin()};
200  const int longestAxis {
201  maxDist[0] >= maxDist[1] && maxDist[0] >= maxDist[2]
202  ? 0
203  : maxDist[1] >= maxDist[0] && maxDist[1] >= maxDist[2]
204  ? 1
205  : 2
206  };
207 
208  // Use C++ partition to sort primitives by buckets where each bucket don't have primitives sorted inside.
209  // It is faster than using C++ standard sort or C++ Boost Radix sort.
210  const int numBuckets {10};
211  const ::glm::vec3 step {maxDist / static_cast<float> (numBuckets)};
212  const float stepAxis {step[longestAxis]};
213  const float startBox {surroundingBox.getPointMin()[longestAxis]};
214  const float bucket1MaxLimit {startBox + stepAxis};
215  typename ::std::vector<BuildNode>::iterator itBucket {::std::partition(itBegin, itEnd,
216  [&](const BuildNode &node) {
217  return node.centroid_[longestAxis] < bucket1MaxLimit;
218  }
219  )};
220  for (::std::int32_t bucketIndex {2}; bucketIndex < numBuckets; ++bucketIndex) {
221  const float bucketMaxLimit {startBox + stepAxis * bucketIndex};
222  itBucket = ::std::partition(::std::move(itBucket), itEnd,
223  [&](const BuildNode &node) {
224  return node.centroid_[longestAxis] < bucketMaxLimit;
225  }
226  );
227  }
228 
229 
230  itCurrentBox->box_ = itBegin->box_;
231  ::std::vector<AABB> boxes {itCurrentBox->box_};
232  boxes.reserve(static_cast<::std::uint32_t> (boxPrimitivesSize));
233  for (::std::int32_t i {beginBoxIndex + 1}; i < endBoxIndex; ++i) {
234  const AABB newBox {buildNodes[static_cast<::std::uint32_t> (i)].box_};
235  itCurrentBox->box_ = ::MobileRT::surroundingBox(newBox, itCurrentBox->box_);
236  boxes.emplace_back(newBox);
237  }
238 
239  const int maxPrimitivesInBoxLeaf {4};
240  const bool isLeaf {boxPrimitivesSize <= maxPrimitivesInBoxLeaf};
241  if (isLeaf) {
242  itCurrentBox->indexOffset_ = beginBoxIndex;
243  itCurrentBox->numPrimitives_ = boxPrimitivesSize;
244 
245  ::std::advance(itStackBoxIndex, -1); // pop
246  currentBoxIndex = *itStackBoxIndex;
247  ::std::advance(itStackBoxBegin, -1); // pop
248  beginBoxIndex = *itStackBoxBegin;
249  ::std::advance(itStackBoxEnd, -1); // pop
250  endBoxIndex = *itStackBoxEnd;
251  } else {
252  const ::std::int32_t left {maxNodeIndex + 1};
253  const ::std::int32_t right {left + 1};
254  const ::std::int32_t splitIndex {getSplitIndexSah(boxes.begin(), boxes.end())};
255 
256  itCurrentBox->indexOffset_ = left;
257  maxNodeIndex = ::std::max(right, maxNodeIndex);
258 
259  *itStackBoxIndex = right;
260  ::std::advance(itStackBoxIndex, 1); // push
261  *itStackBoxBegin = beginBoxIndex + splitIndex;
262  ::std::advance(itStackBoxBegin, 1); // push
263  *itStackBoxEnd = endBoxIndex;
264  ::std::advance(itStackBoxEnd, 1); // push
265 
266  currentBoxIndex = left;
267  endBoxIndex = beginBoxIndex + splitIndex;
268  }
269  } while(itStackBoxIndex > itStackBoxIndexBegin);
270 
271  LOG_INFO("maxNodeIndex = ", maxNodeIndex);
272  this->boxes_.erase (this->boxes_.begin() + maxNodeIndex + 1, this->boxes_.end());
273  this->boxes_.shrink_to_fit();
274  ::std::vector<BVHNode> {this->boxes_}.swap(this->boxes_);
275 
276  // Insert primitives with the proper order.
277  this->primitives_.reserve(static_cast<long unsigned> (primitivesSize));
278  for (::std::uint32_t i {}; i < primitivesSize; ++i) {
279  const BuildNode &node {buildNodes[i]};
280  const ::std::uint32_t oldIndex {static_cast<::std::uint32_t> (node.oldIndex_)};
281  this->primitives_.emplace_back(::std::move(primitives[oldIndex]));
282  }
283  }
284 
293  template<typename T>
295  intersection = intersect(intersection);
296  return intersection;
297  }
298 
308  template<typename T>
310  intersection = intersect(intersection);
311  return intersection;
312  }
313 
326  template<typename T>
328  if (this->primitives_.empty()) {
329  return intersection;
330  }
331  ::std::int32_t boxIndex {};
332  ::std::array<::std::int32_t, StackSize> stackBoxIndex {};
333 
334  const ::std::array<::std::int32_t, StackSize>::const_iterator itBeginBoxIndex {stackBoxIndex.cbegin()};
335  ::std::array<::std::int32_t, StackSize>::iterator itStackBoxIndex {stackBoxIndex.begin()};
336  ::std::advance(itStackBoxIndex, 1);
337 
338  const typename ::std::vector<BVHNode>::iterator itBoxes {this->boxes_.begin()};
339  const typename ::std::vector<T>::iterator itPrimitives {this->primitives_.begin()};
340  do {
341  const BVHNode &node {*(itBoxes + boxIndex)};
342  if (node.box_.intersect(intersection.ray_)) {
343 
344  const ::std::int32_t numberPrimitives {node.numPrimitives_};
345  if (numberPrimitives > 0) {
346  for (::std::int32_t i {}; i < numberPrimitives; ++i) {
347  T &primitive {*(itPrimitives + node.indexOffset_ + i)};
348  const float lastDist {intersection.length_};
349  intersection = primitive.intersect(intersection);
350  if (intersection.ray_.shadowTrace_ && intersection.length_ < lastDist) {
351  return intersection;
352  }
353  }
354  ::std::advance(itStackBoxIndex, -1); // pop
355  boxIndex = *itStackBoxIndex;
356  } else {
357  const ::std::int32_t left {node.indexOffset_};
358  const ::std::int32_t right {node.indexOffset_ + 1};
359  const BVHNode &childLeft {*(itBoxes + left)};
360  const BVHNode &childRight {*(itBoxes + right)};
361 
362  const bool traverseLeft {childLeft.box_.intersect(intersection.ray_)};
363  const bool traverseRight {childRight.box_.intersect(intersection.ray_)};
364 
365  if (!traverseLeft && !traverseRight) {
366  ::std::advance(itStackBoxIndex, -1); // pop
367  boxIndex = *itStackBoxIndex;
368  } else {
369  boxIndex = (traverseLeft) ? left : right;
370  if (traverseLeft && traverseRight) {
371  *itStackBoxIndex = right;
372  ::std::advance(itStackBoxIndex, 1); // push
373  }
374  }
375  }
376 
377  } else {
378  ::std::advance(itStackBoxIndex, -1); // pop
379  boxIndex = *itStackBoxIndex;
380  }
381 
382  } while (itStackBoxIndex > itBeginBoxIndex);
383  return intersection;
384  }
385 
396  template<typename T>
397  template<typename Iterator>
398  ::std::int32_t BVH<T>::getSplitIndexSah(const Iterator itBegin, const Iterator itEnd) {
399  const long numberBoxes {static_cast<long>(itEnd - itBegin)};
400  const Iterator itBoxes {itBegin};
401  const long numBoxes {numberBoxes - 1};
402  const ::std::uint32_t sizeUnsigned {static_cast<::std::uint32_t> (numBoxes)};
403 
404  ::std::vector<float> leftArea (sizeUnsigned);
405  AABB leftBox {*itBoxes};
406  const ::std::vector<float>::iterator itLeftArea {leftArea.begin()};
407  *itLeftArea = leftBox.getSurfaceArea();
408  for (::std::int32_t i {1}; i < numBoxes; ++i) {
409  leftBox = surroundingBox(leftBox, *(itBoxes + i));
410  *(itLeftArea + i) = leftBox.getSurfaceArea();
411  }
412 
413  ::std::vector<float> rightArea (sizeUnsigned);
414  AABB rightBox {*(itBoxes + numBoxes)};
415  const ::std::vector<float>::iterator itRightArea {rightArea.begin()};
416  *(itRightArea + numBoxes - 1) = rightBox.getSurfaceArea();
417  for (long i {numBoxes - 2}; i >= 0; --i) {
418  rightBox = surroundingBox(rightBox, *(itBoxes + i + 1));
419  *(itRightArea + i) = rightBox.getSurfaceArea();
420  }
421 
422  ::std::int32_t splitIndex {1};
423  float minSah {*(itLeftArea) + numBoxes * *(itRightArea)};
424  for (::std::int32_t i {1}; i < numBoxes; ++i) {
425  const ::std::int32_t nextSplit {i + 1};
426  const long numBoxesLeft {nextSplit};
427  const long numBoxesRight {numberBoxes - numBoxesLeft};
428  const float areaLeft {*(itLeftArea + i)};
429  const float areaRight {*(itRightArea + i)};
430  const float leftSah {numBoxesLeft * areaLeft};
431  const float rightSah {numBoxesRight * areaRight};
432  const float sah {leftSah + rightSah};
433  if (sah < minSah) {
434  splitIndex = nextSplit;
435  minSah = sah;
436  }
437  }
438  return splitIndex;
439  }
440 
449  template<typename T>
450  template<typename Iterator>
451  AABB BVH<T>::getSurroundingBox(const Iterator itBegin, const Iterator itEnd) {
452  AABB maxBox {itBegin->box_.getPointMin(), itBegin->box_.getPointMax()};
453 
454  for (Iterator it {itBegin + 1}; it < itEnd; ::std::advance(it, 1)) {
455  const AABB &box {it->box_};
456  maxBox = surroundingBox(maxBox, box);
457  }
458 
459  return maxBox;
460  }
461 
468  template<typename T>
469  const ::std::vector<T>& BVH<T>::getPrimitives() const {
470  return this->primitives_;
471  }
472 
473 
474 }//namespace MobileRT
475 
476 #endif //MOBILERT_ACCELERATORS_BVH_HPP
bool operator()(const BuildNode &node1, const BuildNode &node2) const
Definition: BVH.hpp:75
Definition: BVH.hpp:62
::std::vector< T > primitives_
Definition: BVH.hpp:82
#define LOG_WARN(...)
Definition: Utils.hpp:48
Ray ray_
Definition: Intersection.hpp:27
Intersection intersect(Intersection intersection)
Definition: BVH.hpp:327
void build(::std::vector< T > &&primitives)
Definition: BVH.hpp:161
int operator()(const BuildNode &node, const unsigned offset) const
Definition: BVH.hpp:66
BVH()=default
#define LOG_INFO(...)
Definition: Utils.hpp:43
::std::int32_t getSplitIndexSah(Iterator itBegin, Iterator itEnd)
Intersection shadowTrace(Intersection intersection)
Definition: BVH.hpp:309
const ::std::vector< T > & getPrimitives() const
Definition: BVH.hpp:469
lessthan(const int longestAxis) noexcept
Definition: BVH.hpp:73
Definition: BVH.hpp:28
float length_
Definition: Intersection.hpp:19
Definition: Intersection.hpp:14
::glm::vec3 centroid_
Definition: BVH.hpp:30
AABB getSurroundingBox(Iterator itBegin, Iterator itEnd)
Definition: BVH.hpp:451
::std::vector< BVHNode > boxes_
Definition: BVH.hpp:81
::std::int32_t oldIndex_
Definition: BVH.hpp:31
::glm::vec3 getPointMax() const
Definition: AABB.cpp:101
::glm::vec3 getCentroid() const
Definition: AABB.cpp:81
AABB surroundingBox(const AABB &box1, const AABB &box2)
Definition: AABB.cpp:113
AABB box_
Definition: BVH.hpp:29
int longestAxis_
Definition: BVH.hpp:72
Definition: AABB.hpp:18
~BVH()
Definition: BVH.hpp:146
Definition: BVH.hpp:22
BuildNode(AABB &&box, const ::std::int32_t oldIndex)
Definition: BVH.hpp:45
bool shadowTrace_
Definition: Ray.hpp:45
BVH & operator=(const BVH &bvh)=delete
int longestAxis_
Definition: BVH.hpp:63
Intersection trace(Intersection intersection)
Definition: BVH.hpp:294
::glm::vec3 getPointMin() const
Definition: AABB.cpp:92
rightshift(const int longestAxis) noexcept
Definition: BVH.hpp:64
const AABB box
Definition: TestPlane.cpp:26
Definition: BVH.hpp:71
Definition: BVH.hpp:56
Definition: AABB.cpp:105