beginning work on a new KD-tree implementation
parent
fcd083ed8f
commit
88c0caad3d
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
This file is part of Mitsuba, a physically based rendering system.
|
||||
|
||||
Copyright (c) 2007-2010 by Wenzel Jakob and others.
|
||||
|
||||
Mitsuba is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License Version 3
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
Mitsuba is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#if !defined(__KDTREE_GENERIC_H)
|
||||
#define __KDTREE_GENERIC_H
|
||||
|
||||
#include <mitsuba/mitsuba.h>
|
||||
#include <boost/static_assert.hpp>
|
||||
|
||||
#define MTS_KD_MAX_DEPTH 48 ///< Compile-time KD-tree depth limit
|
||||
#define MTS_KD_STATISTICS 1 ///< Collect statistics during building/traversal
|
||||
#define MTS_KD_MINMAX_BINS 30
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
/**
|
||||
* \brief Generic kd-tree for data structure used to accelerate
|
||||
* ray intersections against large amounts of three-dimensional
|
||||
* shapes.
|
||||
*
|
||||
*/
|
||||
template <typename ShapeType> class GenericKDTree : public Object {
|
||||
typedef uint32_t index_type; ///< Index number format (max 2^32 prims)
|
||||
typedef uint32_t size_type; ///< Size number format
|
||||
|
||||
GenericKDTree() {
|
||||
m_traversalCost = 15;
|
||||
m_intersectionCost = 20;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Build a KD-tree over the supplied axis-aligned bounding
|
||||
* boxes.
|
||||
*/
|
||||
template <typename AABBFunctor>
|
||||
void build(const AABBFunctor &aabbFunctor, size_type primCount) {
|
||||
/* Establish an ad-hoc depth cutoff value (Formula from PBRT) */
|
||||
m_maxDepth = std::min((int) (8 + 1.3f * log2i(primCount)),
|
||||
MTS_KD_MAX_DEPTH);
|
||||
|
||||
Log(EDebug, "kd-tree configuration:");
|
||||
Log(EDebug, " Traversal cost : %.2f", m_traversalCost);
|
||||
Log(EDebug, " Intersection cost : %.2f", m_intersectionCost);
|
||||
Log(EDebug, " Max. tree depth : %i", m_maxDepth);
|
||||
|
||||
}
|
||||
protected:
|
||||
/**
|
||||
* \brief Describes the beginning or end of a primitive
|
||||
* when projected onto a certain dimension.
|
||||
*/
|
||||
struct EdgeEvent {
|
||||
/// Possible event types
|
||||
enum EEventType {
|
||||
EEdgeEnd = 0,
|
||||
EEdgePlanar = 1,
|
||||
EEdgeStart = 2
|
||||
};
|
||||
|
||||
/// Dummy constructor
|
||||
inline EdgeEvent() { }
|
||||
|
||||
/// Create a new edge event
|
||||
inline EdgeEvent(uint8_t type, Float t, index_type index)
|
||||
: t(t), index(index), type(type) {
|
||||
}
|
||||
|
||||
/* Plane position */
|
||||
Float t;
|
||||
/* Primitive index */
|
||||
index_type index;
|
||||
/* Event type: end/planar/start */
|
||||
uint8_t type;
|
||||
};
|
||||
|
||||
typedef std::vector<EdgeEvent> edge_event_vector;
|
||||
typedef edge_event_vector edge_event_vector3[3];
|
||||
|
||||
/// Edge event comparison functor
|
||||
struct EdgeEventSorter : public std::binary_function<EdgeEvent, EdgeEvent, bool> {
|
||||
inline bool operator()(const EdgeEvent &a, const EdgeEvent &b) const {
|
||||
if (a.t != b.t)
|
||||
return a.t < b.t;
|
||||
return a.type < b.type;
|
||||
}
|
||||
};
|
||||
|
||||
/// KD-tree node in 64bit
|
||||
struct KDNode {
|
||||
union {
|
||||
/* Inner node */
|
||||
struct {
|
||||
/* Bit layout:
|
||||
31 : False (inner node)
|
||||
30-3 : Offset to the right child
|
||||
3-0 : Split axis
|
||||
*/
|
||||
uint32_t combined;
|
||||
|
||||
/// Split plane coordinate
|
||||
float split;
|
||||
} inner;
|
||||
|
||||
/* Leaf node */
|
||||
struct {
|
||||
/* Bit layout:
|
||||
31 : True (leaf node)
|
||||
30-0 : Offset to the node's primitive list
|
||||
*/
|
||||
uint32_t combined;
|
||||
|
||||
/// End offset of the primitive list
|
||||
uint32_t end;
|
||||
} leaf;
|
||||
};
|
||||
|
||||
enum EMask {
|
||||
ETypeMask = 1 << 31,
|
||||
ELeafOffsetMask = ~ETypeMask,
|
||||
EInnerAxisMask = 0x3,
|
||||
EInnerOffsetMask = ~EInnerAxisMask
|
||||
};
|
||||
|
||||
/// Initialize a leaf kd-Tree node
|
||||
inline void setLeaf(unsigned int offset, unsigned int numPrims) {
|
||||
leaf.combined = ETypeMask | offset;
|
||||
leaf.end = offset + numPrims;
|
||||
}
|
||||
|
||||
/// Initialize an interior kd-Tree node
|
||||
inline void setInner(int axis, unsigned int offset, Float split) {
|
||||
inner.combined = axis | (offset << 2);
|
||||
inner.split = (float) split;
|
||||
}
|
||||
|
||||
/// Is this a leaf node?
|
||||
FINLINE bool isLeaf() const {
|
||||
return leaf.combined & ETypeMask;
|
||||
}
|
||||
|
||||
/// Assuming this is a leaf node, return the first primitive index
|
||||
FINLINE index_type getPrimStart() const {
|
||||
return leaf.combined & ELeafOffsetMask;
|
||||
}
|
||||
|
||||
/// Assuming this is a leaf node, return the last primitive index
|
||||
FINLINE index_type getPrimEnd() const {
|
||||
return leaf.end;
|
||||
}
|
||||
|
||||
/// Return the sibling of this node
|
||||
FINLINE const KDNode * __restrict getSibling() const {
|
||||
return (const KDNode *) ((ptrdiff_t) this ^ (ptrdiff_t) 8);
|
||||
}
|
||||
|
||||
/// Return the left child (assuming that this is an interior node)
|
||||
FINLINE const KDNode * __restrict getLeft() const {
|
||||
return this +
|
||||
((inner.combined & EInnerOffsetMask) >> 2);
|
||||
}
|
||||
|
||||
/// Return the right child (assuming that this is an interior node)
|
||||
FINLINE const KDNode * __restrict getRight() const {
|
||||
return getLeft() + 1;
|
||||
}
|
||||
|
||||
/// Return the split plane location (assuming that this is an interior node)
|
||||
inline float getSplit() const {
|
||||
return inner.split;
|
||||
}
|
||||
|
||||
/// Return the split axis (assuming that this is an interior node)
|
||||
inline int getAxis() const {
|
||||
return inner.combined & EInnerAxisMask;
|
||||
}
|
||||
};
|
||||
|
||||
BOOST_STATIC_ASSERT(sizeof(KDNode) == 8);
|
||||
|
||||
/**
|
||||
* \brief Min-max binning as described in
|
||||
* "Highly Parallel Fast KD-tree Construction for Interactive
|
||||
* Ray Tracing of Dynamic Scenes"
|
||||
* by M. Shevtsov, A. Soupikov and A. Kapustin
|
||||
*
|
||||
* @tparam BinCount Number of bins to be allocated
|
||||
*/
|
||||
template <int BinCount = 32> struct MinMaxBins {
|
||||
MinMaxBins(AABB &aabb) : aabb(aabb) {
|
||||
for (int dim=0; dim<3; ++dim)
|
||||
invBinSize[dim] = BinCount / (aabb.max[dim]-aabb.min[dim]);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Run min-max binning
|
||||
*
|
||||
* \param func Functor to be used to determine the AABB for
|
||||
* a given list of primitives
|
||||
* \param primCount Number of primitives
|
||||
*/
|
||||
template <typename AABBFunctor> void bin(
|
||||
const AABBFunctor &func,
|
||||
size_type count) {
|
||||
primCount = count;
|
||||
memset(minBins, 0, sizeof(size_type) * 3 * BinCount);
|
||||
memset(maxBins, 0, sizeof(size_type) * 3 * BinCount);
|
||||
for (size_type i=0; i<primCount; ++i) {
|
||||
AABB aabb = func(i);
|
||||
for (int dim=0; dim<3; ++dim) {
|
||||
int minIdx = (aabb.min[dim] - aabb.min[dim]) * invBinSize[dim];
|
||||
int maxIdx = (aabb.max[dim] - aabb.min[dim]) * invBinSize[dim];
|
||||
maxBins[dim * BinCount + std::min(std::max(maxIdx, 0), BinCount-1)]++;
|
||||
minBins[dim * BinCount + std::min(std::max(minIdx, 0), BinCount-1)]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add the bin counts of another \ref MinMaxBins instance.
|
||||
* This is used when distributing min-max-binning over multiple
|
||||
* processors in a SMP machine.
|
||||
*/
|
||||
MinMaxBins& operator+=(const MinMaxBins &otherBins) {
|
||||
for (int i=0; i<3*BinCount; ++i) {
|
||||
minBins[i] += otherBins.minBins[i];
|
||||
maxBins[i] += otherBins.maxBins[i];
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Turn the bin counts into proper primitive numbers
|
||||
*/
|
||||
void computePrimitiveCounts() {
|
||||
int minIdx = 0, maxIdx = 3*BinCount-1;
|
||||
for (int j=0; j<3; ++j) {
|
||||
size_type minAccum = 0, maxAccum = 0;
|
||||
for (int i=0; i<BinCount; ++i) {
|
||||
minBins[minIdx++] += minAccum;
|
||||
maxBins[maxIdx--] += maxAccum;
|
||||
minAccum = minBins[minIdx];
|
||||
maxAccum = maxBins[maxIdx];
|
||||
}
|
||||
Assert(maxBins[maxIdx] == primCount);
|
||||
Assert(minBins[minIdx] == primCount);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Evaluate the surface area heuristic at each bin boundary
|
||||
* and return the maximizer for the given cost constants.
|
||||
*/
|
||||
void maximizeSAH(Float traversalCost, Float intersectionCost) {
|
||||
int binIdx = 0;
|
||||
for (int dim=0; dim<3; ++dim) {
|
||||
AABB leftAABB, rightAABB;
|
||||
for (int i=0; i<BinCount-1; ++i) {
|
||||
size_type leftCount = minBins[binIdx];
|
||||
size_type rightCount = maxBins[binIdx+1];
|
||||
|
||||
float cost = traversalCost + intersectionCost
|
||||
* (pA * leftCount + pB * rightCount);
|
||||
|
||||
binIdx++;
|
||||
}
|
||||
binIdx++;
|
||||
}
|
||||
}
|
||||
private:
|
||||
size_type minBins[3*BinCount], maxBins[3*BinCount];
|
||||
AABB aabb;
|
||||
Vector invBinSize;
|
||||
size_type primCount;
|
||||
};
|
||||
private:
|
||||
Float m_traversalCost, m_intersectionCost;
|
||||
int m_maxDepth;
|
||||
};
|
||||
|
||||
MTS_NAMESPACE_END
|
||||
|
||||
#endif /* __KDTREE_GENERIC_H */
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
This file is part of Mitsuba, a physically based rendering system.
|
||||
|
||||
Copyright (c) 2007-2010 by Wenzel Jakob and others.
|
||||
|
||||
Mitsuba is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License Version 3
|
||||
as published by the Free Software Foundation.
|
||||
|
||||
Mitsuba is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include <mitsuba/render/testcase.h>
|
||||
#include <mitsuba/render/gkdtree.h>
|
||||
|
||||
MTS_NAMESPACE_BEGIN
|
||||
|
||||
class TestKDTree : public TestCase {
|
||||
public:
|
||||
MTS_BEGIN_TESTCASE()
|
||||
MTS_DECLARE_TEST(test01_sutherlandHodgman)
|
||||
MTS_DECLARE_TEST(test02_buildSimple)
|
||||
MTS_END_TESTCASE()
|
||||
|
||||
void test01_sutherlandHodgman() {
|
||||
/* Test the triangle clipping algorithm */
|
||||
Vertex vertices[3];
|
||||
vertices[0].p = Point(0, 0, 0);
|
||||
vertices[1].p = Point(1, 0, 0);
|
||||
vertices[2].p = Point(1, 1, 0);
|
||||
|
||||
AABB clipAABB(
|
||||
Point(0, .5, -1),
|
||||
Point(1, 1, 1)
|
||||
);
|
||||
|
||||
AABB expectedResult(
|
||||
Point(.5, .5, 0),
|
||||
Point(1, 1, 0)
|
||||
);
|
||||
|
||||
Triangle t;
|
||||
t.idx[0] = 0; t.idx[1] = 1; t.idx[2] = 2;
|
||||
|
||||
AABB clippedAABB = t.getClippedAABB(vertices, clipAABB);
|
||||
|
||||
assertEquals(clippedAABB.min, expectedResult.min);
|
||||
assertEquals(clippedAABB.max, expectedResult.max);
|
||||
}
|
||||
|
||||
class TriKDTree : GenericKDTree<Triangle> {
|
||||
};
|
||||
|
||||
void test02_buildSimple() {
|
||||
TriKDTree tree;
|
||||
std::vector<Triangle> tris;
|
||||
}
|
||||
};
|
||||
|
||||
MTS_EXPORT_TESTCASE(TestKDTree, "Testcase for kd-tree related code")
|
||||
MTS_NAMESPACE_END
|
Loading…
Reference in New Issue