Skip to content

Commit

Permalink
bounding volume hierarchies
Browse files Browse the repository at this point in the history
  • Loading branch information
alysssssa committed May 1, 2023
1 parent 5965f6b commit b73386f
Show file tree
Hide file tree
Showing 8 changed files with 182 additions and 2 deletions.
2 changes: 1 addition & 1 deletion raytracer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@ project(raytracer)

set(CMAKE_CXX_STANDARD 14)

add_executable(raytracer main.cpp vec3.h colour.h ray.h hittable.h sphere.h hittable_list.h raytracer.h camera.h material.h moving_sphere.h aabb.h)
add_executable(raytracer main.cpp vec3.h colour.h ray.h hittable.h sphere.h hittable_list.h raytracer.h camera.h material.h moving_sphere.h aabb.h bvh.h)
12 changes: 12 additions & 0 deletions raytracer/aabb.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,5 +32,17 @@ class aabb {
point3 maximum;
};

aabb surrounding_box(aabb box0, aabb box1) {
point3 small(fmin(box0.min().x(), box1.min().x()),
fmin(box0.min().y(), box1.min().y()),
fmin(box0.min().z(), box1.min().z()));

point3 big(fmax(box0.max().x(), box1.max().x()),
fmax(box0.max().y(), box1.max().y()),
fmax(box0.max().z(), box1.max().z()));

return aabb(small,big);
}


#endif // AABB_H
118 changes: 118 additions & 0 deletions raytracer/bvh.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@

#ifndef BVH_H
#define BVH_H

#include "raytracer.h"
#include "hittable.h"
#include "hittable_list.h"

// bounding volume hierarchy

class bvh_node : public hittable {
public:
bvh_node();

bvh_node(const hittable_list& list, double time0, double time1)
: bvh_node(list.objects, 0, list.objects.size(), time0, time1)
{}

// the children of a bvh_node are to generic hittables (not necessarily bvh_nodes)
bvh_node(
const std::vector<shared_ptr<hittable>>& src_objects,
size_t start, size_t end, double time0, double time1
);

virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const override;

virtual bool bounding_box(double time0, double time1, aabb& output_box) const override;

public:
shared_ptr<hittable> left;
shared_ptr<hittable> right;
aabb box;
};

inline bool box_compare(const shared_ptr<hittable> a, const shared_ptr<hittable> b, int axis) {
aabb box_a;
aabb box_b;

if (!a->bounding_box(0, 0, box_a) || !b->bounding_box(0, 0, box_b))
std::cerr << "No bounding box in bvh_node constructor.\n";

return box_a.min().e[axis] < box_b.min().e[axis];
}

bool box_x_compare(const shared_ptr<hittable> a, const shared_ptr<hittable> b) {
return box_compare(a, b, 0);
}

bool box_y_compare(const shared_ptr<hittable> a, const shared_ptr<hittable> b) {
return box_compare(a, b, 1);
}

bool box_z_compare(const shared_ptr<hittable> a, const shared_ptr<hittable> b) {
return box_compare(a, b, 2);
}

// splitting BVH volumes:
// 1. randomly choose an axis
// 2. sort the primitives
// 3. put half in each subtree

bvh_node::bvh_node(
const std::vector<shared_ptr<hittable>>& src_objects,
size_t start, size_t end, double time0, double time1
) {
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;

size_t object_span = end - start;

if (object_span == 1) {
left = right = objects[start];
} else if (object_span == 2) {
if (comparator(objects[start], objects[start + 1])) {
left = objects[start];
right = objects[start + 1];
} else {
left = objects[start + 1];
right = objects[start];
}
} else {
std::sort(objects.begin() + start, objects.begin() + end, comparator);

auto mid = start + object_span / 2;
left = make_shared<bvh_node>(objects, start, mid, time0, time1);
right = make_shared<bvh_node>(objects, mid, end, time0, time1);
}

aabb box_left, box_right;

if (!left->bounding_box(time0, time1, box_left)
|| !right->bounding_box(time0, time1, box_right)
) {
std::cerr << "No bounding box in bvh_node constructor.\n";
}

box = surrounding_box(box_left, box_right);
}

bool bvh_node::bounding_box(double time0, double time1, aabb& output_box) const {
output_box = box;
return true;
}

bool bvh_node::hit(const ray& r, double t_min, double t_max, hit_record& rec) const {
if (!box.hit(r, t_min, t_max)) return false;

bool hit_left = left->hit(r, t_min, t_max, rec);
bool hit_right = right->hit(r, t_min, hit_left ? rec.t : t_max, rec);

return hit_left || hit_right;
}

#endif // BVH_H
4 changes: 4 additions & 0 deletions raytracer/hittable.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "ray.h"
#include "raytracer.h"
#include "aabb.h"

class material;

Expand All @@ -28,6 +29,9 @@ struct hit_record {
class hittable {
public:
virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const = 0;
// not all primitives have bounding boxes (e.g. infinite plane)
// moving objects have bounding box enclosing the object for the entire time interval
virtual bool bounding_box(double time0, double time1, aabb& output_box) const = 0;
};

#endif // HITTABLE_H
17 changes: 17 additions & 0 deletions raytracer/hittable_list.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#define HITTABLE_LIST_H

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

#include <memory>
#include <vector>
Expand All @@ -19,6 +20,7 @@ class hittable_list : public hittable {
void add(shared_ptr<hittable> object) { objects.push_back(object); }

virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const override;
virtual bool bounding_box(double time0, double time1, aabb& output_box) const override;

public:
std::vector<shared_ptr<hittable>> objects;
Expand All @@ -40,4 +42,19 @@ bool hittable_list::hit(const ray& r, double t_min, double t_max, hit_record& re
return hit_anything;
}

bool hittable_list::bounding_box(double time0, double time1, aabb& output_box) const {
if (objects.empty()) return false;

aabb temp_box;
bool first_box = true;

for (const auto& object : objects) {
if (!object->bounding_box(time0, time1, temp_box)) return false;
output_box = first_box ? temp_box : surrounding_box(output_box, temp_box);
first_box = false;
}

return true;
}

#endif // HITTABLE_LIST_H
18 changes: 17 additions & 1 deletion raytracer/moving_sphere.h
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "hittable.h"
#include "raytracer.h"
#include "aabb.h"

class moving_sphere: public hittable {
public:
Expand All @@ -12,7 +13,8 @@ class moving_sphere: public hittable {
point3 cen0, point3 cen1, double _time0, double _time1, double r, shared_ptr<material> m
) : center0(cen0), center1(cen1), time0(_time0), time1(_time1), radius(r), mat_ptr(m) {};

bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const;
virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const override;
virtual bool bounding_box(double time0, double time1, aabb& output_box) const override;

point3 center(double time) const;

Expand Down Expand Up @@ -55,4 +57,18 @@ bool moving_sphere::hit(const ray& r, double t_min, double t_max, hit_record& re
return true;
}

// box of ((box at time0) and (box at time1))
bool moving_sphere::bounding_box(double time0, double time1, aabb& output_box) const {
aabb box0(
center(time0) - vec3(radius, radius, radius),
center(time0) + vec3(radius, radius, radius)
);
aabb box1(
center(time1) - vec3(radius, radius, radius),
center(time1) + vec3(radius, radius, radius)
);
output_box = surrounding_box(box0, box1);
return true;
}

#endif // MOVING_SPHERE_H
5 changes: 5 additions & 0 deletions raytracer/raytracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ inline double random_double(double min, double max) {
// return distribution(generator);
//}

inline int random_int(int min, int max) {
// random integer in [min,max]
return static_cast<int>(random_double(min, max+1));
}

inline double clamp(double x, double min, double max) {
if (x < min) return min;
if (x > max) return max;
Expand Down
8 changes: 8 additions & 0 deletions raytracer/sphere.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ class sphere : public hittable {
sphere(point3 cen, double r, shared_ptr<material> m) : center(cen), radius(r), mat_ptr(m) {};

virtual bool hit(const ray& r, double t_min, double t_max, hit_record& rec) const override;
virtual bool bounding_box(double time0, double time1, aabb& output_box) const override;

public:
point3 center;
Expand Down Expand Up @@ -53,4 +54,11 @@ bool sphere::hit(const ray& r, double t_min, double t_max, hit_record& rec) cons

}

bool sphere::bounding_box(double time0, double time1, aabb& output_box) const {
output_box = aabb(
center - vec3(radius, radius, radius),
center + vec3(radius, radius, radius));
return true;
}

#endif // SPHERE_H

0 comments on commit b73386f

Please sign in to comment.