Add octree generation

This commit is contained in:
Pavle Portic 2024-03-06 14:13:28 +01:00
parent c5732d64ad
commit eca59ad82d
Signed by: TheEdgeOfRage
GPG Key ID: 66AD4BA646FBC0D2
6 changed files with 165 additions and 21 deletions

3
.gitignore vendored
View File

@ -28,3 +28,6 @@ x64
*.exr
*.png
.ccls-cache/
scenes/pa2/ajax.obj

View File

@ -54,6 +54,7 @@ add_executable(nori
include/nori/emitter.h
include/nori/mesh.h
include/nori/object.h
include/nori/octree.h
include/nori/parser.h
include/nori/proplist.h
include/nori/ray.h
@ -78,6 +79,7 @@ add_executable(nori
src/mesh.cpp
src/obj.cpp
src/object.cpp
src/octree.cpp
src/parser.cpp
src/perspective.cpp
src/proplist.cpp

View File

@ -19,6 +19,7 @@
#pragma once
#include <nori/mesh.h>
#include <nori/octree.h>
NORI_NAMESPACE_BEGIN
@ -65,9 +66,12 @@ public:
*/
bool rayIntersect(const Ray3f &ray, Intersection &its, bool shadowRay) const;
uint32_t getIntersectingTriangle(Octree *node, Ray3f &ray, Intersection &its, bool shadowRay = false) const;
private:
Mesh *m_mesh = nullptr; ///< Mesh (only a single one for now)
BoundingBox3f m_bbox; ///< Bounding box of the entire scene
Octree *m_octree = nullptr; ///< Octree for the mesh
};
NORI_NAMESPACE_END

26
include/nori/octree.h Normal file
View File

@ -0,0 +1,26 @@
#pragma once
#include <nori/mesh.h>
#include <vector>
NORI_NAMESPACE_BEGIN
class Octree {
public:
// Members
BoundingBox3f *bbox;
Mesh *mesh;
std::vector<Octree*> children;
std::vector<uint32_t> triangles;
// Methods
explicit Octree(BoundingBox3f *bbox, Mesh *mesh, std::vector<uint32_t> triangles) : bbox(bbox), mesh(mesh), triangles(triangles) {
children = std::vector<Octree*>(8, nullptr);
}
static Octree *build(BoundingBox3f *bbox, Mesh *mesh, std::vector<uint32_t> triangles, int recursionDepth = 0);
uint32_t getIntersectingTriangle(Ray3f &ray, Intersection &its, bool shadowRay);
BoundingBox3f getBoundingBox() const { return *bbox; }
};
NORI_NAMESPACE_END

View File

@ -18,6 +18,7 @@
#include <nori/accel.h>
#include <Eigen/Geometry>
#include <chrono>
NORI_NAMESPACE_BEGIN
@ -29,32 +30,26 @@ void Accel::addMesh(Mesh *mesh) {
}
void Accel::build() {
/* Nothing to do here for now */
auto triangles = std::vector<uint32_t>(m_mesh->getTriangleCount());
for (uint32_t idx = 0; idx < m_mesh->getTriangleCount(); ++idx) {
triangles[idx] = idx;
}
cout << "Building octree" << endl;
auto start = std::chrono::high_resolution_clock::now();
m_octree = Octree::build(&m_bbox, m_mesh, triangles);
auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::high_resolution_clock::now() - start);
std::cout << "Octree build took " << duration.count()/1000.0f << " s" << std::endl;
}
bool Accel::rayIntersect(const Ray3f &ray_, Intersection &its, bool shadowRay) const {
bool foundIntersection = false; // Was an intersection found so far?
uint32_t f = (uint32_t) -1; // Triangle index of the closest intersection
Ray3f ray(ray_); /// Make a copy of the ray (we will need to update its '.maxt' value)
/* Brute force search through all triangles */
for (uint32_t idx = 0; idx < m_mesh->getTriangleCount(); ++idx) {
float u, v, t;
if (m_mesh->rayIntersect(idx, ray, u, v, t)) {
/* An intersection was found! Can terminate
immediately if this is a shadow ray query */
if (shadowRay)
return true;
ray.maxt = its.t = t;
its.uv = Point2f(u, v);
its.mesh = m_mesh;
f = idx;
foundIntersection = true;
}
}
auto f = m_octree->getIntersectingTriangle(ray, its, shadowRay);
bool found = f != (uint32_t) -1;
if (foundIntersection) {
if (found && !shadowRay) {
/* At this point, we now know that there is an intersection,
and we know the triangle index of the closest such intersection.
@ -107,7 +102,7 @@ bool Accel::rayIntersect(const Ray3f &ray_, Intersection &its, bool shadowRay) c
}
}
return foundIntersection;
return found;
}
NORI_NAMESPACE_END

114
src/octree.cpp Normal file
View File

@ -0,0 +1,114 @@
#include <future>
#include <nori/octree.h>
#include <tbb/tbb.h>
NORI_NAMESPACE_BEGIN
Octree *Octree::build(BoundingBox3f *bbox, Mesh *mesh, std::vector<uint32_t> triangles, int recursionDepth) {
if (triangles.empty()) {
return nullptr;
}
if (triangles.size() <= 20 || recursionDepth > 10 || bbox->getVolume() < 0.001f) {
return new Octree(bbox, mesh, triangles);
}
auto m_F = mesh->getIndices();
auto m_V = mesh->getVertexPositions();
Octree *node = new Octree(bbox, mesh, std::vector<uint32_t>());
auto bMin = bbox->min;
auto w = bbox->max.x() - bMin.x();
auto h = bbox->max.y() - bMin.y();
auto d = bbox->max.z() - bMin.z();
auto w2 = w / 2.0f, h2 = h / 2.0f, d2 = d / 2.0f;
BoundingBox3f *newBboxes[8] = {
new BoundingBox3f(bMin + Vector3f(0, 0, 0), bMin + Vector3f(w2, h2, d2)),
new BoundingBox3f(bMin + Vector3f(w2, 0, 0), bMin + Vector3f(w, h2, d2)),
new BoundingBox3f(bMin + Vector3f(0, h2, 0), bMin + Vector3f(w2, h, d2)),
new BoundingBox3f(bMin + Vector3f(w2, h2, 0), bMin + Vector3f(w, h, d2)),
new BoundingBox3f(bMin + Vector3f(0, 0, d2), bMin + Vector3f(w2, h2, d)),
new BoundingBox3f(bMin + Vector3f(w2, 0, d2), bMin + Vector3f(w, h2, d)),
new BoundingBox3f(bMin + Vector3f(0, h2, d2), bMin + Vector3f(w2, h, d)),
new BoundingBox3f(bMin + Vector3f(w2, h2, d2), bMin + Vector3f(w, h, d))
};
std::vector<uint32_t> newTriangles[8];
BoundingBox3f triangleBbox;
for(int i = 0; i < 8; i++) {
newTriangles[i].reserve(triangles.size() / 8);
for (uint32_t idx : triangles) {
auto a = m_V.col(m_F(0, idx));
auto b = m_V.col(m_F(1, idx));
auto c = m_V.col(m_F(2, idx));
triangleBbox.min = a.cwiseMin(b).cwiseMin(c);
triangleBbox.max = a.cwiseMax(b).cwiseMax(c);
if (newBboxes[i]->overlaps(triangleBbox)) {
newTriangles[i].push_back(idx);
}
}
newTriangles[i].shrink_to_fit();
}
if (recursionDepth < 2) {
tbb::parallel_for(tbb::blocked_range<size_t>(0, 8), [&](const tbb::blocked_range<size_t> &range) {
for (size_t i = range.begin(); i != range.end(); ++i) {
node->children[i] = build(newBboxes[i], mesh, newTriangles[i], recursionDepth + 1);
}
});
} else {
for (int i = 0; i < 8; i++) {
node->children[i] = build(newBboxes[i], mesh, newTriangles[i], recursionDepth + 1);
}
}
return node;
}
uint32_t Octree::getIntersectingTriangle(Ray3f &ray, Intersection &its, bool shadowRay) {
float u, v, t;
uint32_t closestTriangle = -1;
uint32_t triangle;
if (triangles.empty()) {
std::sort(children.begin(), children.end(), [ray](Octree *a, Octree *b) {
if (a == nullptr) {
return false;
}
if (b == nullptr) {
return true;
}
return a->bbox->distanceTo(ray.o) < b->bbox->distanceTo(ray.o);
});
for (auto child : children) {
if (child == nullptr || !child->bbox->rayIntersect(ray)) {
continue;
}
triangle = child->getIntersectingTriangle(ray, its, shadowRay);
if (triangle != (uint32_t) -1) {
return triangle;
}
}
return closestTriangle;
}
for (auto triangle : triangles) {
if (mesh->rayIntersect(triangle, ray, u, v, t)) {
if (shadowRay) {
return triangle;
}
if (t < ray.maxt) {
ray.maxt = its.t = t;
its.uv = Point2f(u, v);
its.mesh = mesh;
closestTriangle = triangle;
}
}
}
return closestTriangle;
}
NORI_NAMESPACE_END