From eca59ad82d7fb367eadae3e9ee547f692753489f Mon Sep 17 00:00:00 2001 From: TheEdgeOfRage Date: Wed, 6 Mar 2024 14:13:28 +0100 Subject: [PATCH] Add octree generation --- .gitignore | 3 ++ CMakeLists.txt | 2 + include/nori/accel.h | 4 ++ include/nori/octree.h | 26 ++++++++++ src/accel.cpp | 37 ++++++-------- src/octree.cpp | 114 ++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 165 insertions(+), 21 deletions(-) create mode 100644 include/nori/octree.h create mode 100644 src/octree.cpp diff --git a/.gitignore b/.gitignore index 91a0a74..0de6241 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ x64 *.exr *.png +.ccls-cache/ + +scenes/pa2/ajax.obj diff --git a/CMakeLists.txt b/CMakeLists.txt index 52d71c1..0e1bb50 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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 diff --git a/include/nori/accel.h b/include/nori/accel.h index 6a98a2c..8c00d15 100644 --- a/include/nori/accel.h +++ b/include/nori/accel.h @@ -19,6 +19,7 @@ #pragma once #include +#include 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 diff --git a/include/nori/octree.h b/include/nori/octree.h new file mode 100644 index 0000000..67ed5fb --- /dev/null +++ b/include/nori/octree.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include + +NORI_NAMESPACE_BEGIN + +class Octree { +public: + // Members + BoundingBox3f *bbox; + Mesh *mesh; + std::vector children; + std::vector triangles; + + // Methods + explicit Octree(BoundingBox3f *bbox, Mesh *mesh, std::vector triangles) : bbox(bbox), mesh(mesh), triangles(triangles) { + children = std::vector(8, nullptr); + } + static Octree *build(BoundingBox3f *bbox, Mesh *mesh, std::vector triangles, int recursionDepth = 0); + + uint32_t getIntersectingTriangle(Ray3f &ray, Intersection &its, bool shadowRay); + BoundingBox3f getBoundingBox() const { return *bbox; } +}; + +NORI_NAMESPACE_END diff --git a/src/accel.cpp b/src/accel.cpp index b6664e6..d20ca80 100644 --- a/src/accel.cpp +++ b/src/accel.cpp @@ -18,6 +18,7 @@ #include #include +#include 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(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::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 diff --git a/src/octree.cpp b/src/octree.cpp new file mode 100644 index 0000000..11e2177 --- /dev/null +++ b/src/octree.cpp @@ -0,0 +1,114 @@ +#include +#include +#include + +NORI_NAMESPACE_BEGIN + + +Octree *Octree::build(BoundingBox3f *bbox, Mesh *mesh, std::vector 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()); + 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 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(0, 8), [&](const tbb::blocked_range &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