initial implementation, WIP

This commit is contained in:
arvid schröder 2022-12-06 18:00:40 +01:00
parent 728b324c5b
commit 3f19627975
2 changed files with 213 additions and 42 deletions

View file

@ -4,15 +4,38 @@
#include <iostream> #include <iostream>
Texture KDTreeRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) { Texture KDTreeRenderer::renderImage(Scene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height); Texture image(width, height);
std::cout << "KD Tree renderer will only output KD Tree visualization" << std::endl; std::cout << "KD Tree renderer will only output KD Tree visualization" << std::endl;
return image; return image;
} }
Texture KDTreeRenderer::renderKDTree(FastScene const &scene, Camera const &camera, int width, int height) { Texture KDTreeRenderer::renderKDTree(FastScene const &scene, Camera const &camera, int width, int height) {
Texture image(width, height); const Color RED = {1, 0, 0};
const Color GREEN = {0, 1, 0};
Texture image(width, height);
// IMPLEMENT ME // IMPLEMENT ME
std::vector<std::vector<int>> intersections;
intersections.reserve(height);
return image; int max = 0;
float const aspectRatio = static_cast<float>(height) / static_cast<float>(width);
for (int x = 0; x < image.width(); ++x) {
std::vector<int> ints;
ints.reserve(width);
for (int y = 0; y < image.height(); ++y) {
Ray ray = camera.createRay((static_cast<float>(x) / image.width() * 2.0f - 1),
-(static_cast<float>(y) / image.height() * 2 - 1) * aspectRatio);
ints.push_back(scene.countNodeIntersections(ray));
max = std::max(ints[x], max);
}
intersections.push_back(std::move(ints));
}
std::cout << "done\n";
for (int x = 0; x < image.width(); ++x) {
for (int y = 0; y < image.height(); ++y) {
image.setPixelAt(x, y, clamped((max - intersections[y][x]) * GREEN + intersections[y][x] * RED));
}
}
return image;
} }

View file

@ -3,71 +3,219 @@
#include "shader/shader.h" #include "shader/shader.h"
#include <algorithm> #include <algorithm>
#include <iostream> #include <iostream>
#include <numeric>
int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const { int Node::countNodeIntersections(const Ray &ray, float t0, float t1) const {
// IMPLEMENT ME // IMPLEMENT ME
return 0; 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 { bool Node::findIntersection(Ray &ray, float t0, float t1) const {
// IMPLEMENT ME
// If this is a leaf node, we intersect with all the primitives... // IMPLEMENT ME
// ... otherwise we continue through the branches // If this is a leaf node, we intersect with all the primitives...
// Determine the order in which we intersect the child nodes if (isLeaf()) {
// Traverse the necessary children bool found = false;
return 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 { bool Node::findOcclusion(Ray &ray, float t0, float t1) const {
// IMPLEMENT ME if (isLeaf()) {
return false; 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 { int FastScene::countNodeIntersections(const Ray &ray) const {
// IMPLEMENT ME return root->countNodeIntersections(ray, 0, INFINITY);
return false;
} }
bool FastScene::findIntersection(Ray &ray) const { bool FastScene::findIntersection(Ray &ray) const {
// IMPLEMENT ME return root->findIntersection(ray, 0, ray.length);
return false;
} }
bool FastScene::findOcclusion(Ray &ray) const { bool FastScene::findOcclusion(Ray &ray) const {
// IMPLEMENT ME return root->findOcclusion(ray, 0, ray.length);
return false;
} }
void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) { void FastScene::buildTree(int maximumDepth, int minimumNumberOfPrimitives) {
// IMPLEMENT ME // IMPLEMENT ME
// Set the new depth and number of primitives // Set the new depth and number of primitives
this->maximumDepth = maximumDepth;
this->minimumNumberOfPrimitives = minimumNumberOfPrimitives;
// Determine the bounding box of the kD-Tree // Determine the bounding box of the kD-Tree
// Recursively build 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> FastScene::build(Vector3d const &minimumBounds, Vector3d const &maximumBounds,
// IMPLEMENT ME const std::vector<std::shared_ptr<Primitive>> &primitives, int depth) {
// Determine the diameter of the bounding box
// Test whether we have reached a leaf node... 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;
// ... otherwise create a new inner node by splitting through the widest // Test whether we have reached a leaf node...
// dimension if (++depth == maximumDepth) {
node->primitives = primitives;
return node;
}
// Determine the split position // ... otherwise create a new inner node by splitting through the widest
// Note: Use the median of the minimum bounds of the primitives // 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;
}
}
// Divide primitives into the left and right lists // Determine the split position
// Remember: A primitive can be in both lists! // Note: Use the median of the minimum bounds of the primitives
// Also remember: You split exactly at the minimum of a primitive, node->split = std::accumulate(primitives.begin(), primitives.end(), 0.0f,
// make sure *that* primitive does *not* appear in both lists! [&node](float &a, const auto &prim) {
return a + prim->minimumBounds(node->dimension);
}) /
static_cast<float>(primitives.size());
// Print out the number of primitives in the left and right child node // 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!
// Set the left and right split vectors 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);
}
}
}
// Recursively build the tree // Print out the number of primitives in the left and right child node
return nullptr; 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;
} }