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 *.exr
*.png *.png
.ccls-cache/
scenes/pa2/ajax.obj

View File

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

View File

@ -19,6 +19,7 @@
#pragma once #pragma once
#include <nori/mesh.h> #include <nori/mesh.h>
#include <nori/octree.h>
NORI_NAMESPACE_BEGIN NORI_NAMESPACE_BEGIN
@ -65,9 +66,12 @@ public:
*/ */
bool rayIntersect(const Ray3f &ray, Intersection &its, bool shadowRay) const; bool rayIntersect(const Ray3f &ray, Intersection &its, bool shadowRay) const;
uint32_t getIntersectingTriangle(Octree *node, Ray3f &ray, Intersection &its, bool shadowRay = false) const;
private: private:
Mesh *m_mesh = nullptr; ///< Mesh (only a single one for now) Mesh *m_mesh = nullptr; ///< Mesh (only a single one for now)
BoundingBox3f m_bbox; ///< Bounding box of the entire scene BoundingBox3f m_bbox; ///< Bounding box of the entire scene
Octree *m_octree = nullptr; ///< Octree for the mesh
}; };
NORI_NAMESPACE_END 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 <nori/accel.h>
#include <Eigen/Geometry> #include <Eigen/Geometry>
#include <chrono>
NORI_NAMESPACE_BEGIN NORI_NAMESPACE_BEGIN
@ -29,32 +30,26 @@ void Accel::addMesh(Mesh *mesh) {
} }
void Accel::build() { 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 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) Ray3f ray(ray_); /// Make a copy of the ray (we will need to update its '.maxt' value)
/* Brute force search through all triangles */ auto f = m_octree->getIntersectingTriangle(ray, its, shadowRay);
for (uint32_t idx = 0; idx < m_mesh->getTriangleCount(); ++idx) { bool found = f != (uint32_t) -1;
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;
}
}
if (foundIntersection) { if (found && !shadowRay) {
/* At this point, we now know that there is an intersection, /* At this point, we now know that there is an intersection,
and we know the triangle index of the closest such 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 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