2022-12-06 13:49:37 +01:00
|
|
|
#include "primitive/primitive.h"
|
|
|
|
#include "scene/fastscene.h"
|
|
|
|
#include "shader/shader.h"
|
|
|
|
#include <algorithm>
|
|
|
|
#include <iostream>
|
2022-12-06 18:00:40 +01:00
|
|
|
#include <numeric>
|
2022-12-06 13:49:37 +01:00
|
|
|
|
|
|
|
int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
// 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);
|
|
|
|
}
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Node::findIntersection(Ray &ray, float t0, float t1) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
|
|
|
|
// 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);
|
|
|
|
}
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool Node::findOcclusion(Ray &ray, float t0, float t1) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
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);
|
|
|
|
}
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
int FastScene::countNodeIntersections(const Ray &ray) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
return root->countNodeIntersections(ray, 0, INFINITY);
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FastScene::findIntersection(Ray &ray) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
return root->findIntersection(ray, 0, ray.length);
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
bool FastScene::findOcclusion(Ray &ray) const {
|
2022-12-06 18:00:40 +01:00
|
|
|
return root->findOcclusion(ray, 0, ray.length);
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) {
|
2022-12-06 18:00:40 +01:00
|
|
|
// 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
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|
|
|
|
|
2022-12-06 18:00:40 +01:00
|
|
|
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;
|
2022-12-06 13:49:37 +01:00
|
|
|
}
|