cloudy-raytracer/scene/fastscene.cpp
2022-12-06 18:00:40 +01:00

221 lines
8.9 KiB
C++

#include "primitive/primitive.h"
#include "scene/fastscene.h"
#include "shader/shader.h"
#include <algorithm>
#include <iostream>
#include <numeric>
int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
if (isLeaf()) {
return 0;
}
float t_split;
if (ray.direction[this->dimension] == 0) {
t_split = INFINITY;
} else {
t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
}
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
int farChild = nearChild == 0 ? 1 : 0;
if (t_split < 0 || t_split > t1) {
return 0 + this->child[nearChild]->countNodeIntersections(ray, t0, t1);
} else if (t_split < t0) {
return 0 + this->child[farChild]->countNodeIntersections(ray, t0, t1);
} else {
return 1 + this->child[nearChild]->countNodeIntersections(ray, t0, t_split) +
this->child[farChild]->countNodeIntersections(ray, t_split, t1);
}
}
bool Node::findIntersection(Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
// If this is a leaf node, we intersect with all the primitives...
if (isLeaf()) {
bool found = false;
for (const auto &prim: this->primitives) {
found |= prim->intersect(ray);
}
return found;
}
// ... otherwise we continue through the branches
// Determine the order in which we intersect the child nodes
// Traverse the necessary children
float t_split;
if (ray.direction[this->dimension] == 0) {
t_split = INFINITY;
} else {
t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
}
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
int farChild = nearChild == 0 ? 1 : 0;
if (t_split < 0 || t_split > t1) {
return this->child[nearChild]->findIntersection(ray, t0, t1);
} else if (t_split < t0) {
return this->child[farChild]->findIntersection(ray, t0, t1);
} else {
this->child[nearChild]->findIntersection(ray, t0, t_split);
if (ray.length < t_split) {
return true;
}
return this->child[farChild]->findIntersection(ray, t_split, t1);
}
}
bool Node::findOcclusion(Ray &ray, float t0, float t1) const {
if (isLeaf()) {
for (const auto &prim: this->primitives) {
if (prim->intersect(ray)) {
return true;
}
}
return false;
}
float t_split = (this->split - ray.origin[this->dimension]) / ray.direction[this->dimension];
int nearChild = ray.origin[this->dimension] > this->split ? 1 : 0;
int farChild = nearChild == 0 ? 1 : 0;
if (t_split < 0 || t_split > t1) {
return this->child[nearChild]->findIntersection(ray, t0, t1);
} else if (t_split < t0) {
return this->child[farChild]->findIntersection(ray, t0, t1);
} else {
this->child[nearChild]->findIntersection(ray, t0, t_split);
if (ray.length < t_split) {
return true;
}
return this->child[farChild]->findIntersection(ray, t_split, t1);
}
}
int FastScene::countNodeIntersections(const Ray &ray) const {
return root->countNodeIntersections(ray, 0, INFINITY);
}
bool FastScene::findIntersection(Ray &ray) const {
return root->findIntersection(ray, 0, ray.length);
}
bool FastScene::findOcclusion(Ray &ray) const {
return root->findOcclusion(ray, 0, ray.length);
}
void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) {
// IMPLEMENT ME
// Set the new depth and number of primitives
this->maximumDepth = maximumDepth;
this->minimumNumberOfPrimitives = minimumNumberOfPrimitives;
// Determine the bounding box of the kD-Tree
Vector3d maximumBounds = {
std::max_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->maximumBounds(0) < prim2->maximumBounds(0);
})->get()->maximumBounds(0),
std::max_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->maximumBounds(1) < prim2->maximumBounds(1);
})->get()->maximumBounds(1),
std::max_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->maximumBounds(2) < prim2->maximumBounds(2);
})->get()->maximumBounds(2)
};
Vector3d minimumBounds = {
std::min_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->minimumBounds(0) < prim2->minimumBounds(0);
})->get()->minimumBounds(0),
std::min_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->minimumBounds(1) < prim2->minimumBounds(1);
})->get()->minimumBounds(1),
std::min_element(this->primitives().begin(),
this->primitives().end(),
[](const auto &prim1, const auto &prim2) {
return prim1->minimumBounds(2) < prim2->minimumBounds(2);
})->get()->minimumBounds(2)
};
this->root = build(minimumBounds, maximumBounds, this->primitives(), 0);
// Recursively build the kD-Tree
}
std::unique_ptr<Node> FastScene::build(Vector3d const &minimumBounds, Vector3d const &maximumBounds,
const std::vector<std::shared_ptr<Primitive>> &primitives, int depth) {
std::unique_ptr<Node> node = std::make_unique<Node>();
// IMPLEMENT ME
// Determine the diameter of the bounding box
float diameter_x = maximumBounds.x - minimumBounds.x;
float diameter_y = maximumBounds.y - minimumBounds.y;
float diameter_z = maximumBounds.z - minimumBounds.z;
// Test whether we have reached a leaf node...
if (++depth == maximumDepth) {
node->primitives = primitives;
return node;
}
// ... otherwise create a new inner node by splitting through the widest
// dimension
if (diameter_x >= diameter_y) {
if (diameter_x >= diameter_z) {
node->dimension = 0;
} else {
node->dimension = 2;
}
} else {
if (diameter_y > diameter_z) {
node->dimension = 1;
} else {
node->dimension = 2;
}
}
// Determine the split position
// Note: Use the median of the minimum bounds of the primitives
node->split = std::accumulate(primitives.begin(), primitives.end(), 0.0f,
[&node](float &a, const auto &prim) {
return a + prim->minimumBounds(node->dimension);
}) /
static_cast<float>(primitives.size());
// Divide primitives into the left and right lists
// Remember: A primitive can be in both lists!
// Also remember: You split exactly at the minimum of a primitive,
// make sure *that* primitive does *not* appear in both lists!
std::vector<std::shared_ptr<Primitive>> left{};
std::vector<std::shared_ptr<Primitive>> right{};
for (auto &prim: primitives) {
if (prim->minimumBounds(node->dimension) > node->split) {
right.push_back(prim);
} else {
left.push_back(prim);
if (prim->maximumBounds(node->dimension) > node->split) {
right.push_back(prim);
}
}
}
// Print out the number of primitives in the left and right child node
std::cout << "Nodes: " << left.size() << " / " << right.size() << "\n";
// Set the left and right split vectors
Vector3d newMinBounds = {node->dimension == 0 ? node->split : minimumBounds[0],
node->dimension == 1 ? node->split : minimumBounds[1],
node->dimension == 2 ? node->split : minimumBounds[2]};
Vector3d newMaxBounds = {node->dimension == 0 ? node->split : maximumBounds[0],
node->dimension == 1 ? node->split : maximumBounds[1],
node->dimension == 2 ? node->split : maximumBounds[2]};
node->child[0] = build(minimumBounds, newMaxBounds, left, depth);
node->child[1] = build(newMinBounds, maximumBounds, right, depth);
// Recursively build the tree
return node;
}