Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 110 additions & 2 deletions books/RayTracingTheNextWeek.html
Original file line number Diff line number Diff line change
Expand Up @@ -372,8 +372,10 @@
<div class='together'>
This gives the following result:

<div id="image-bouncing-spheres">
![<span class='num'>Image 1:</span> Bouncing spheres
](../images/img-2.01-bouncing-spheres.png class='pixel')
</div>

</div>

Expand Down Expand Up @@ -923,13 +925,14 @@
public:
...
bvh_node(const std::vector<shared_ptr<hittable>>& 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) {
Expand Down Expand Up @@ -1035,6 +1038,111 @@
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[Listing [random-spheres-bvh]: <kbd>[main.cc]</kbd> 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<shared_ptr<hittable>>& 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]: <kbd>[bvh.h]</kbd> 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<shared_ptr<hittable>>& 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]: <kbd>[bvh.h]</kbd> 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]: <kbd>[aabb.h]</kbd>
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
Expand Down
14 changes: 14 additions & 0 deletions src/TheNextWeek/aabb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
13 changes: 9 additions & 4 deletions src/TheNextWeek/bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "rtweekend.h"

#include "aabb.h"
#include "hittable.h"
#include "hittable_list.h"

Expand All @@ -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<shared_ptr<hittable>>& 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) {
Expand All @@ -50,8 +57,6 @@ class bvh_node : public hittable {
left = make_shared<bvh_node>(objects, start, mid);
right = make_shared<bvh_node>(objects, mid, end);
}

bbox = aabb(left->bounding_box(), right->bounding_box());
}

bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
Expand Down
14 changes: 14 additions & 0 deletions src/TheRestOfYourLife/aabb.h
Original file line number Diff line number Diff line change
Expand Up @@ -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());
}
Expand Down
13 changes: 9 additions & 4 deletions src/TheRestOfYourLife/bvh.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

#include "rtweekend.h"

#include "aabb.h"
#include "hittable.h"
#include "hittable_list.h"

Expand All @@ -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<shared_ptr<hittable>>& 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) {
Expand All @@ -50,8 +57,6 @@ class bvh_node : public hittable {
left = make_shared<bvh_node>(objects, start, mid);
right = make_shared<bvh_node>(objects, mid, end);
}

bbox = aabb(left->bounding_box(), right->bounding_box());
}

bool hit(const ray& r, interval ray_t, hit_record& rec) const override {
Expand Down