diff --git a/books/RayTracingTheNextWeek.html b/books/RayTracingTheNextWeek.html
index 0bbc9704..6da3fdf4 100644
--- a/books/RayTracingTheNextWeek.html
+++ b/books/RayTracingTheNextWeek.html
@@ -372,8 +372,10 @@
This gives the following result:
+

+
@@ -923,13 +925,14 @@
public:
...
bvh_node(const std::vector>& src_objects, size_t start, size_t end) {
- auto objects = src_objects; // Create a modifiable array of the source scene objects
-
int axis = random_int(0,2);
+
auto comparator = (axis == 0) ? box_x_compare
: (axis == 1) ? box_y_compare
: box_z_compare;
+ auto objects = src_objects; // A modifiable array of the source scene objects
+
size_t object_span = end - start;
if (object_span == 1) {
@@ -1035,6 +1038,111 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [random-spheres-bvh]: [main.cc] Random spheres, using BVH]
+The rendered image should be identical to the non-BVH version shown in
+[image 1](#image-bouncing-spheres). However, if you time the two versions, the BVH version should be
+faster. I see a speedup of almost _six and a half times_ the prior version.
+
+
+Another BVH Optimization
+-------------------------
+We can speed up the BVH optimization a bit more. Instead of choosing a random splitting axis, let's
+split the longest axis of the enclosing bounding box to get the most subdivision. The change is
+straight-forward, but we'll add a few things to the `aabb` class in the process.
+
+The first task is to construct an axis-aligned bounding box of the span of objects in the BVH
+constructor. Basically, we'll construct the `bvh_node`s bounding box from this span by initializing
+the bounding box to empty, and then augmenting it with each bounding box in the span of objects.
+
+We don't have a way yet to express an empty bounding box, so we'll imagine one for now and
+implementing it shortly.
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ class bvh_node : public hittable {
+ public:
+ ...
+ bvh_node(const std::vector>& src_objects, size_t start, size_t end) {
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ // Build the bounding box of the span of source objects.
+ bbox = aabb::empty;
+ for (int object_index=start; object_index < end; ++object_index)
+ bbox = aabb(bbox, src_objects[object_index]->bounding_box());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+
+ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ [Listing [object-span-bbox]: [bvh.h] Building the bbox for the span of BVH objects]
+
+Now that we have the bounding box, set the splitting axis to the one with the longest side. Again,
+we'll imagine a function that does that for us: `aabb::longest_axis()`. Finally, since we're
+computing the bounding box of the object span up front, we can delete the original line that
+computed it as the union of the left and right sides.
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ class bvh_node : public hittable {
+ public:
+ ...
+ bvh_node(const std::vector>& src_objects, size_t start, size_t end) {
+ // Build the bounding box of the span of source objects.
+ bbox = aabb::empty;
+ for (int object_index=start; object_index < end; ++object_index)
+ bbox = aabb(bbox, src_objects[object_index]->bounding_box());
+
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ int axis = bbox.longest_axis();
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+
+ auto comparator = (axis == 0) ? box_x_compare
+ : (axis == 1) ? box_y_compare
+ : box_z_compare;
+
+ ...
+
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ delete
+ bbox = aabb(left->bounding_box(), right->bounding_box());
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ }
+
+ ...
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ [Listing [object-span-bbox]: [bvh.h] Building the bbox for the span of BVH objects]
+
+Now to implement the empty `aabb` code and the new `aabb::longest_axis()` function:
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+ class aabb {
+ public:
+ ...
+
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++ highlight
+ int longest_axis() const {
+ // Returns the index of the longest axis of the bounding box.
+
+ if (x.size() > y.size())
+ return x.size() > z.size() ? 0 : 2;
+ else
+ return y.size() > z.size() ? 1 : 2;
+ }
+
+ static const aabb empty, universe;
+ };
+
+ const aabb aabb::empty = aabb(interval::empty, interval::empty, interval::empty);
+ const aabb aabb::universe = aabb(interval::universe, interval::universe, interval::universe);
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ C++
+
+ ...
+
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ [Listing [aabb-empty-and-axis]: [aabb.h]
+ New aabb constants and longest_axis() function]
+
+As before, you should see identical results to [image 1](#image-bouncing-spheres), but rendering a
+little bit faster. On my system, this yields something like an additional 18% render speedup. Not
+bad for a little extra work.
+
Texture Mapping
diff --git a/src/TheNextWeek/aabb.h b/src/TheNextWeek/aabb.h
index 462d97f2..5ffa38c7 100644
--- a/src/TheNextWeek/aabb.h
+++ b/src/TheNextWeek/aabb.h
@@ -72,8 +72,22 @@ class aabb {
}
return true;
}
+
+ int longest_axis() const {
+ // Returns the index of the longest axis of the bounding box.
+
+ if (x.size() > y.size())
+ return x.size() > z.size() ? 0 : 2;
+ else
+ return y.size() > z.size() ? 1 : 2;
+ }
+
+ static const aabb empty, universe;
};
+const aabb aabb::empty = aabb(interval::empty, interval::empty, interval::empty);
+const aabb aabb::universe = aabb(interval::universe, interval::universe, interval::universe);
+
aabb operator+(const aabb& bbox, const vec3& offset) {
return aabb(bbox.x + offset.x(), bbox.y + offset.y(), bbox.z + offset.z());
}
diff --git a/src/TheNextWeek/bvh.h b/src/TheNextWeek/bvh.h
index 9d66cda9..2740f881 100644
--- a/src/TheNextWeek/bvh.h
+++ b/src/TheNextWeek/bvh.h
@@ -13,6 +13,7 @@
#include "rtweekend.h"
+#include "aabb.h"
#include "hittable.h"
#include "hittable_list.h"
@@ -24,13 +25,19 @@ class bvh_node : public hittable {
bvh_node(const hittable_list& list) : bvh_node(list.objects, 0, list.objects.size()) {}
bvh_node(const std::vector>& src_objects, size_t start, size_t end) {
- auto objects = src_objects; // Create a modifiable array of the source scene objects
+ // Build the bounding box of the span of source objects.
+ bbox = aabb::empty;
+ for (int object_index=start; object_index < end; ++object_index)
+ bbox = aabb(bbox, src_objects[object_index]->bounding_box());
+
+ int axis = bbox.longest_axis();
- int axis = random_int(0,2);
auto comparator = (axis == 0) ? box_x_compare
: (axis == 1) ? box_y_compare
: box_z_compare;
+ auto objects = src_objects; // A modifiable array of the source scene objects
+
size_t object_span = end - start;
if (object_span == 1) {
@@ -50,8 +57,6 @@ class bvh_node : public hittable {
left = make_shared(objects, start, mid);
right = make_shared(objects, mid, end);
}
-
- bbox = aabb(left->bounding_box(), right->bounding_box());
}
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
diff --git a/src/TheRestOfYourLife/aabb.h b/src/TheRestOfYourLife/aabb.h
index 462d97f2..5ffa38c7 100644
--- a/src/TheRestOfYourLife/aabb.h
+++ b/src/TheRestOfYourLife/aabb.h
@@ -72,8 +72,22 @@ class aabb {
}
return true;
}
+
+ int longest_axis() const {
+ // Returns the index of the longest axis of the bounding box.
+
+ if (x.size() > y.size())
+ return x.size() > z.size() ? 0 : 2;
+ else
+ return y.size() > z.size() ? 1 : 2;
+ }
+
+ static const aabb empty, universe;
};
+const aabb aabb::empty = aabb(interval::empty, interval::empty, interval::empty);
+const aabb aabb::universe = aabb(interval::universe, interval::universe, interval::universe);
+
aabb operator+(const aabb& bbox, const vec3& offset) {
return aabb(bbox.x + offset.x(), bbox.y + offset.y(), bbox.z + offset.z());
}
diff --git a/src/TheRestOfYourLife/bvh.h b/src/TheRestOfYourLife/bvh.h
index 9d66cda9..2740f881 100644
--- a/src/TheRestOfYourLife/bvh.h
+++ b/src/TheRestOfYourLife/bvh.h
@@ -13,6 +13,7 @@
#include "rtweekend.h"
+#include "aabb.h"
#include "hittable.h"
#include "hittable_list.h"
@@ -24,13 +25,19 @@ class bvh_node : public hittable {
bvh_node(const hittable_list& list) : bvh_node(list.objects, 0, list.objects.size()) {}
bvh_node(const std::vector>& src_objects, size_t start, size_t end) {
- auto objects = src_objects; // Create a modifiable array of the source scene objects
+ // Build the bounding box of the span of source objects.
+ bbox = aabb::empty;
+ for (int object_index=start; object_index < end; ++object_index)
+ bbox = aabb(bbox, src_objects[object_index]->bounding_box());
+
+ int axis = bbox.longest_axis();
- int axis = random_int(0,2);
auto comparator = (axis == 0) ? box_x_compare
: (axis == 1) ? box_y_compare
: box_z_compare;
+ auto objects = src_objects; // A modifiable array of the source scene objects
+
size_t object_span = end - start;
if (object_span == 1) {
@@ -50,8 +57,6 @@ class bvh_node : public hittable {
left = make_shared(objects, start, mid);
right = make_shared(objects, mid, end);
}
-
- bbox = aabb(left->bounding_box(), right->bounding_box());
}
bool hit(const ray& r, interval ray_t, hit_record& rec) const override {