If we take a look at the hit function in HittableList class:
1 2 3 4 5 6 7 8 9 10 11 12 13
boolHittableList::hit(const Ray &r, Interval interval, HitRecord &record)const{ HitRecord temp_rec; bool if_hit = false; autoclosest_t = interval.max; for (constauto& i : objects) { if (i->hit(r, Interval(interval.min, closest_t), temp_rec)) { if_hit = true; closest_t = temp_rec.t; record = temp_rec; } } return if_hit; }
We can see it got linear time complexity. It’s not hard to imagine that if we have a lot of objects in the scene, the performance will be terrible. So we need to find a way to improve it. One way to do it is to use a bounding volume hierarchy (BVH).
Bounding Volume Hierarchy
Imagine we have many objects in a scene, we can use a box to wrap all the objects up, and two sub-boxes to wrap half of the objects up. Then we can do the same in the sub-box, and so on. In this case, we have a tree structure. And finding a node in the tree will be O(nlog(n)) time complexity, which is much better than O(n).
One question is how can we warp the objects up? The bounding should be fast and compact. One way to do it is aligning the bounding box with the axis, or make an axis-aligned bounding box(AABB). In R2 we know if we want to have a bound on something, we take intersection of two intervals. In R3 we can take intersection of two boxes. So we can use this to warp the objects up.
To detect whether the ray hits the box, we can use the ray’s equation:
R=A+tb
If we only consider the x axis, we can have:
Rx=Ax+tbx
and planes that is orthogonal to yz plane has this equation:
x=x0x=x1
We can then simply plugin the ray’s equation into the plane’s equation and solve for t:
Similar thing goes for t1. Since one could be either greater or small than each other, we make t0 the max, and t1 the min. Then we can have the interval of t that the ray hits the box. We can do the same for y and z axis. We now define a AABB class to represent the box:
icon-padding
MathUtil.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
classAABB { public: Interval x, y, z;
AABB() = default;
AABB(const Point3 &a, const Point3 &b);
AABB(const Interval& x, const Interval& y, const Interval& z);
Interval axis(int i)const;
[[nodiscard]] boolhit(const Ray &r, Interval ray_int)const;
For moving sphere, we want to wrap the sphere up in the whole time interval. To make our life easier, we define other constructors for Interval and AABB:
BVHs are also hittables. They are containers, but they can also respond to hits(that’s what we want). Since we will build a binary tree structure, we will define a BVHNode class:
icon-padding
MathUtil.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
classBVHNode : public IHittable { public: BVHNode() = default;
Notice we are converting the HittableList to a BVHNode before rendering, which the latter uses a tree structure to store the objects and searches node in logarithmic time complexity rather than linear time complexity, but the result is not so different: