games101-hw/07/Assignment7/BVH.cpp

151 lines
4.6 KiB
C++

#include <algorithm>
#include <cassert>
#include "BVH.hpp"
BVHAccel::BVHAccel(std::vector<Object*> p, int maxPrimsInNode,
SplitMethod splitMethod)
: maxPrimsInNode(std::min(255, maxPrimsInNode)), splitMethod(splitMethod),
primitives(std::move(p))
{
time_t start, stop;
time(&start);
if (primitives.empty())
return;
root = recursiveBuild(primitives);
time(&stop);
double diff = difftime(stop, start);
int hrs = (int)diff / 3600;
int mins = ((int)diff / 60) - (hrs * 60);
int secs = (int)diff - (hrs * 3600) - (mins * 60);
printf(
"\rBVH Generation complete: \nTime Taken: %i hrs, %i mins, %i secs\n\n",
hrs, mins, secs);
}
BVHBuildNode* BVHAccel::recursiveBuild(std::vector<Object*> objects)
{
BVHBuildNode* node = new BVHBuildNode();
// Compute bounds of all primitives in BVH node
Bounds3 bounds;
for (int i = 0; i < objects.size(); ++i)
bounds = Union(bounds, objects[i]->getBounds());
if (objects.size() == 1) {
// Create leaf _BVHBuildNode_
node->bounds = objects[0]->getBounds();
node->object = objects[0];
node->left = nullptr;
node->right = nullptr;
node->area = objects[0]->getArea();
return node;
}
else if (objects.size() == 2) {
node->left = recursiveBuild(std::vector{objects[0]});
node->right = recursiveBuild(std::vector{objects[1]});
node->bounds = Union(node->left->bounds, node->right->bounds);
node->area = node->left->area + node->right->area;
return node;
}
else {
Bounds3 centroidBounds;
for (int i = 0; i < objects.size(); ++i)
centroidBounds =
Union(centroidBounds, objects[i]->getBounds().Centroid());
int dim = centroidBounds.maxExtent();
switch (dim) {
case 0:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().x <
f2->getBounds().Centroid().x;
});
break;
case 1:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().y <
f2->getBounds().Centroid().y;
});
break;
case 2:
std::sort(objects.begin(), objects.end(), [](auto f1, auto f2) {
return f1->getBounds().Centroid().z <
f2->getBounds().Centroid().z;
});
break;
}
auto beginning = objects.begin();
auto middling = objects.begin() + (objects.size() / 2);
auto ending = objects.end();
auto leftshapes = std::vector<Object*>(beginning, middling);
auto rightshapes = std::vector<Object*>(middling, ending);
assert(objects.size() == (leftshapes.size() + rightshapes.size()));
node->left = recursiveBuild(leftshapes);
node->right = recursiveBuild(rightshapes);
node->bounds = Union(node->left->bounds, node->right->bounds);
node->area = node->left->area + node->right->area;
}
return node;
}
Intersection BVHAccel::Intersect(const Ray& ray) const
{
Intersection isect;
if (!root)
return isect;
isect = BVHAccel::getIntersection(root, ray);
return isect;
}
Intersection BVHAccel::getIntersection(BVHBuildNode* node, const Ray& ray) const
{
// TODO Traverse the BVH to find intersection
Intersection intr;
if(!node)
return intr;
Vector3f invDir(1.0/ray.direction.x,1.0/ray.direction.y,1.0/ray.direction.z);
const std::array<int,3> & dirIsNeg ={ray.direction.x<0,ray.direction.y<0,ray.direction.z<0};
if(!node->bounds.IntersectP(ray,invDir,dirIsNeg))
return intr;
if(node->object != nullptr)
{
return node->object->getIntersection(ray);
}
Intersection hit1 = getIntersection(node->left,ray);
Intersection hit2 = getIntersection(node->right,ray);
if(hit1.happened||hit2.happened)
{
return hit1.distance<=hit2.distance ? hit1:hit2;
}
return intr;
}
void BVHAccel::getSample(BVHBuildNode* node, float p, Intersection &pos, float &pdf){
if(node->left == nullptr || node->right == nullptr){
node->object->Sample(pos, pdf);
pdf *= node->area;
return;
}
if(p < node->left->area) getSample(node->left, p, pos, pdf);
else getSample(node->right, p - node->left->area, pos, pdf);
}
void BVHAccel::Sample(Intersection &pos, float &pdf){
float p = std::sqrt(get_random_float()) * root->area;
getSample(root, p, pos, pdf);
pdf /= root->area;
}