480 lines
16 KiB
C++
480 lines
16 KiB
C++
/*
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
Copyright (c) 2007-2011 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(__SHAPE_KDTREE_H)
|
|
#define __SHAPE_KDTREE_H
|
|
|
|
#include <mitsuba/render/shape.h>
|
|
#include <mitsuba/render/sahkdtree3.h>
|
|
#include <mitsuba/render/triaccel.h>
|
|
|
|
#if defined(MTS_KD_CONSERVE_MEMORY)
|
|
#if defined(MTS_HAS_COHERENT_RT)
|
|
#error MTS_KD_CONSERVE_MEMORY & MTS_HAS_COHERENT_RT are incompatible
|
|
#endif
|
|
#endif
|
|
|
|
#if defined(SINGLE_PRECISION)
|
|
/// 32 byte temporary storage for intersection computations
|
|
#define MTS_KD_INTERSECTION_TEMP 32
|
|
#else
|
|
#define MTS_KD_INTERSECTION_TEMP 64
|
|
#endif
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
typedef const Shape * ConstShapePtr;
|
|
|
|
/**
|
|
* \brief SAH KD-tree acceleration data structure for fast ray-triangle
|
|
* intersections.
|
|
*
|
|
* Implements the construction algorithm for 'perfect split' trees as outlined
|
|
* in the paper "On Bulding fast kd-Trees for Ray Tracing, and on doing that in
|
|
* O(N log N)" by Ingo Wald and Vlastimil Havran. Non-triangle shapes are
|
|
* supported, but most optimizations here target large triangle meshes.
|
|
* For more details regarding the construction algorithm, please refer to
|
|
* the class \ref GenericKDTree.
|
|
*
|
|
* This class offers a choice of two different triangle intersection algorithms:
|
|
* By default, intersections are computed using the "TriAccel" projection with
|
|
* pre-computation method from Ingo Wald's PhD thesis "Realtime Ray Tracing
|
|
* and Interactive Global Illumination". This adds an overhead of 48 bytes per
|
|
* triangle.
|
|
*
|
|
* When compiled with \c MTS_KD_CONSERVE_MEMORY, the Moeller-Trumbore intersection
|
|
* test is used instead, which doesn't need any extra storage. However, it also
|
|
* tends to be quite a bit slower.
|
|
*
|
|
* \sa GenericKDTree
|
|
* \ingroup librender
|
|
*/
|
|
|
|
class MTS_EXPORT_RENDER ShapeKDTree : public SAHKDTree3D<ShapeKDTree> {
|
|
friend class GenericKDTree<AABB, SurfaceAreaHeuristic, ShapeKDTree>;
|
|
friend class SAHKDTree3D<ShapeKDTree>;
|
|
friend class Instance;
|
|
friend class AnimatedInstance;
|
|
public:
|
|
// =============================================================
|
|
//! @{ \name Initialization and tree construction
|
|
// =============================================================
|
|
/// Create an empty kd-tree
|
|
ShapeKDTree();
|
|
|
|
/// Add a shape to the kd-tree
|
|
void addShape(const Shape *shape);
|
|
|
|
/// Return the list of stored shapes
|
|
inline const std::vector<const Shape *> &getShapes() const { return m_shapes; }
|
|
|
|
/**
|
|
* \brief Return the total number of low-level primitives (triangles
|
|
* and other low-level primitives)
|
|
*/
|
|
inline size_type getPrimitiveCount() const {
|
|
return m_shapeMap[m_shapeMap.size()-1];
|
|
}
|
|
|
|
/// Return an axis-aligned bounding box containing all primitives
|
|
inline const AABB &getAABB() const { return m_aabb; }
|
|
|
|
/// Return an bounding sphere containing all primitives
|
|
inline const BSphere &getBSphere() const { return m_bsphere; }
|
|
|
|
/// Build the kd-tree (needs to be called before tracing any rays)
|
|
void build();
|
|
|
|
//! @}
|
|
// =============================================================
|
|
|
|
// =============================================================
|
|
//! @{ \name Ray tracing routines
|
|
// =============================================================
|
|
|
|
/**
|
|
* \brief Intersect a ray against all primitives stored in the kd-tree
|
|
* and return detailed intersection information
|
|
*
|
|
* \param ray
|
|
* A 3-dimensional ray data structure with minimum/maximum
|
|
* extent information, as well as a time (which applies when
|
|
* the shapes are animated)
|
|
*
|
|
* \param its
|
|
* A detailed intersection record, which will be filled by the
|
|
* intersection query
|
|
*
|
|
* \return \c true if an intersection was found
|
|
*/
|
|
bool rayIntersect(const Ray &ray, Intersection &its) const;
|
|
|
|
/**
|
|
* \brief Intersect a ray against all primitives stored in the kd-tree
|
|
* and return the traveled distance and intersected shape
|
|
*
|
|
* This function represents a performance compromise when the
|
|
* intersected shape must be known, but there is no need for
|
|
* a detailed intersection record.
|
|
*
|
|
* \param ray
|
|
* A 3-dimensional ray data structure with minimum/maximum
|
|
* extent information, as well as a time (which applies when
|
|
* the shapes are animated)
|
|
*
|
|
* \param t
|
|
* The traveled ray distance will be stored in this parameter
|
|
|
|
* \param shape
|
|
* A pointer to the intersected shape will be stored in this
|
|
* parameter
|
|
*
|
|
* \param n
|
|
* The geometric surface normal will be stored in this parameter
|
|
*
|
|
* \return \c true if an intersection was found
|
|
*/
|
|
bool rayIntersect(const Ray &ray, Float &t, ConstShapePtr &shape,
|
|
Normal &n) const;
|
|
|
|
/**
|
|
* \brief Test a ray for occlusion with respect to all primitives
|
|
* stored in the kd-tree.
|
|
*
|
|
* This function does not compute a detailed intersection record,
|
|
* and it never determines the closest intersection, which makes
|
|
* it quite a bit faster than the other two \c rayIntersect() methods.
|
|
* However, for this reason, it can only be used to check whether
|
|
* there is \a any occlusion along a ray or ray segment.
|
|
*
|
|
* \param ray
|
|
* A 3-dimensional ray data structure with minimum/maximum
|
|
* extent information, as well as a time (which applies when
|
|
* the shapes are animated)
|
|
*
|
|
* \return \c true if there is occlusion
|
|
*/
|
|
bool rayIntersect(const Ray &ray) const;
|
|
|
|
#if defined(MTS_HAS_COHERENT_RT)
|
|
/**
|
|
* \brief Intersect four rays with the stored triangle meshes while making
|
|
* use of ray coherence to do this very efficiently. Requires SSE.
|
|
*/
|
|
void rayIntersectPacket(const RayPacket4 &packet,
|
|
const RayInterval4 &interval, Intersection4 &its, void *temp) const;
|
|
|
|
/**
|
|
* \brief Fallback for incoherent rays
|
|
* \sa rayIntesectPacket
|
|
*/
|
|
void rayIntersectPacketIncoherent(const RayPacket4 &packet,
|
|
const RayInterval4 &interval, Intersection4 &its, void *temp) const;
|
|
#endif
|
|
//! @}
|
|
// =============================================================
|
|
|
|
MTS_DECLARE_CLASS()
|
|
protected:
|
|
/**
|
|
* \brief Return the shape index corresponding to a primitive index
|
|
* seen by the generic kd-tree implementation.
|
|
*
|
|
* When this is a triangle mesh, the \a idx parameter is updated to the
|
|
* triangle index within the mesh.
|
|
*/
|
|
FINLINE index_type findShape(index_type &idx) const {
|
|
std::vector<index_type>::const_iterator it = std::lower_bound(
|
|
m_shapeMap.begin(), m_shapeMap.end(), idx + 1) - 1;
|
|
idx -= *it;
|
|
return (index_type) (it - m_shapeMap.begin());
|
|
}
|
|
|
|
/// Return the axis-aligned bounding box of a certain primitive
|
|
FINLINE AABB getAABB(index_type idx) const {
|
|
index_type shapeIdx = findShape(idx);
|
|
const Shape *shape = m_shapes[shapeIdx];
|
|
if (m_triangleFlag[shapeIdx]) {
|
|
const TriMesh *mesh = static_cast<const TriMesh *>(shape);
|
|
return mesh->getTriangles()[idx].getAABB(mesh->getVertexPositions());
|
|
} else {
|
|
return shape->getAABB();
|
|
}
|
|
}
|
|
|
|
/// Return the AABB of a primitive when clipped to another AABB
|
|
FINLINE AABB getClippedAABB(index_type idx, const AABB &aabb) const {
|
|
index_type shapeIdx = findShape(idx);
|
|
const Shape *shape = m_shapes[shapeIdx];
|
|
if (m_triangleFlag[shapeIdx]) {
|
|
const TriMesh *mesh = static_cast<const TriMesh *>(shape);
|
|
return mesh->getTriangles()[idx].getClippedAABB(mesh->getVertexPositions(), aabb);
|
|
} else {
|
|
return shape->getClippedAABB(aabb);
|
|
}
|
|
}
|
|
|
|
/// Temporarily holds some intersection information
|
|
struct IntersectionCache {
|
|
size_type shapeIndex;
|
|
size_type primIndex;
|
|
Float u, v;
|
|
};
|
|
|
|
/**
|
|
* Check whether a primitive is intersected by the given ray. Some
|
|
* temporary space is supplied to store data that can later
|
|
* be used to create a detailed intersection record.
|
|
*/
|
|
FINLINE bool intersect(const Ray &ray, index_type idx, Float mint,
|
|
Float maxt, Float &t, void *temp) const {
|
|
IntersectionCache *cache =
|
|
static_cast<IntersectionCache *>(temp);
|
|
|
|
#if defined(MTS_KD_CONSERVE_MEMORY)
|
|
index_type shapeIdx = findShape(idx);
|
|
if (EXPECT_TAKEN(m_triangleFlag[shapeIdx])) {
|
|
const TriMesh *mesh =
|
|
static_cast<const TriMesh *>(m_shapes[shapeIdx]);
|
|
const Triangle &tri = mesh->getTriangles()[idx];
|
|
Float tempU, tempV, tempT;
|
|
if (tri.rayIntersect(mesh->getVertexPositions(), ray,
|
|
tempU, tempV, tempT)) {
|
|
if (tempT < mint || tempT > maxt)
|
|
return false;
|
|
t = tempT;
|
|
cache->shapeIndex = shapeIdx;
|
|
cache->primIndex = idx;
|
|
cache->u = tempU;
|
|
cache->v = tempV;
|
|
return true;
|
|
}
|
|
} else {
|
|
const Shape *shape = m_shapes[shapeIdx];
|
|
if (shape->rayIntersect(ray, mint, maxt, t,
|
|
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
|
cache->shapeIndex = shapeIdx;
|
|
cache->primIndex = KNoTriangleFlag;
|
|
return true;
|
|
}
|
|
}
|
|
#else
|
|
const TriAccel &ta = m_triAccel[idx];
|
|
if (EXPECT_TAKEN(m_triAccel[idx].k != KNoTriangleFlag)) {
|
|
Float tempU, tempV, tempT;
|
|
if (ta.rayIntersect(ray, mint, maxt, tempU, tempV, tempT)) {
|
|
t = tempT;
|
|
cache->shapeIndex = ta.shapeIndex;
|
|
cache->primIndex = ta.primIndex;
|
|
cache->u = tempU;
|
|
cache->v = tempV;
|
|
return true;
|
|
}
|
|
} else {
|
|
uint32_t shapeIndex = ta.shapeIndex;
|
|
const Shape *shape = m_shapes[shapeIndex];
|
|
if (shape->rayIntersect(ray, mint, maxt, t,
|
|
reinterpret_cast<uint8_t*>(temp) + 8)) {
|
|
cache->shapeIndex = shapeIndex;
|
|
cache->primIndex = KNoTriangleFlag;
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Check whether a primitive is intersected by the given ray. This
|
|
* version is used for shadow rays, hence no temporary space is supplied.
|
|
*/
|
|
FINLINE bool intersect(const Ray &ray, index_type idx,
|
|
Float mint, Float maxt) const {
|
|
#if defined(MTS_KD_CONSERVE_MEMORY)
|
|
index_type shapeIdx = findShape(idx);
|
|
if (EXPECT_TAKEN(m_triangleFlag[shapeIdx])) {
|
|
const TriMesh *mesh =
|
|
static_cast<const TriMesh *>(m_shapes[shapeIdx]);
|
|
const Triangle &tri = mesh->getTriangles()[idx];
|
|
Float tempU, tempV, tempT;
|
|
if (mesh->isOccluder() &&
|
|
tri.rayIntersect(mesh->getVertexPositions(), ray, tempU, tempV, tempT))
|
|
return tempT >= mint && tempT <= maxt;
|
|
return false;
|
|
} else {
|
|
const Shape *shape = m_shapes[shapeIdx];
|
|
return shape->isOccluder() &&
|
|
shape->rayIntersect(ray, mint, maxt);
|
|
}
|
|
#else
|
|
const TriAccel &ta = m_triAccel[idx];
|
|
uint32_t shapeIndex = ta.shapeIndex;
|
|
const Shape *shape = m_shapes[shapeIndex];
|
|
if (EXPECT_TAKEN(m_triAccel[idx].k != KNoTriangleFlag)) {
|
|
Float tempU, tempV, tempT;
|
|
return shape->isOccluder() &&
|
|
ta.rayIntersect(ray, mint, maxt, tempU, tempV, tempT);
|
|
} else {
|
|
return shape->isOccluder() &&
|
|
shape->rayIntersect(ray, mint, maxt);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
#if defined(MTS_HAS_COHERENT_RT)
|
|
/// Ray traversal stack entry for uncoherent ray tracing
|
|
struct CoherentKDStackEntry {
|
|
/* Current ray interval */
|
|
RayInterval4 MM_ALIGN16 interval;
|
|
/* Pointer to the far child */
|
|
const KDNode * __restrict node;
|
|
};
|
|
#endif
|
|
|
|
/**
|
|
* \brief After having found a unique intersection, fill a proper record
|
|
* using the temporary information collected in \ref intersect()
|
|
*/
|
|
template<bool BarycentricPos> FINLINE void fillIntersectionRecord(const Ray &ray,
|
|
const void *temp, Intersection &its) const {
|
|
const IntersectionCache *cache = reinterpret_cast<const IntersectionCache *>(temp);
|
|
const Shape *shape = m_shapes[cache->shapeIndex];
|
|
if (m_triangleFlag[cache->shapeIndex]) {
|
|
const TriMesh *trimesh = static_cast<const TriMesh *>(shape);
|
|
const Triangle &tri = trimesh->getTriangles()[cache->primIndex];
|
|
const Point *vertexPositions = trimesh->getVertexPositions();
|
|
const Normal *vertexNormals = trimesh->getVertexNormals();
|
|
const Point2 *vertexTexcoords = trimesh->getVertexTexcoords();
|
|
const Spectrum *vertexColors = trimesh->getVertexColors();
|
|
const TangentSpace *vertexTangents = trimesh->getVertexTangents();
|
|
const Vector b(1 - cache->u - cache->v, cache->u, cache->v);
|
|
|
|
const uint32_t idx0 = tri.idx[0], idx1 = tri.idx[1], idx2 = tri.idx[2];
|
|
const Point &p0 = vertexPositions[idx0];
|
|
const Point &p1 = vertexPositions[idx1];
|
|
const Point &p2 = vertexPositions[idx2];
|
|
|
|
if (BarycentricPos)
|
|
its.p = p0 * b.x + p1 * b.y + p2 * b.z;
|
|
else
|
|
its.p = ray(its.t);
|
|
|
|
Normal faceNormal(cross(p1-p0, p2-p0));
|
|
Float length = faceNormal.length();
|
|
if (!faceNormal.isZero())
|
|
faceNormal /= length;
|
|
|
|
its.geoFrame = Frame(faceNormal);
|
|
|
|
if (EXPECT_TAKEN(vertexNormals)) {
|
|
const Normal &n0 = vertexNormals[idx0];
|
|
const Normal &n1 = vertexNormals[idx1];
|
|
const Normal &n2 = vertexNormals[idx2];
|
|
|
|
if (EXPECT_TAKEN(!vertexTangents)) {
|
|
its.shFrame = Frame(normalize(n0 * b.x + n1 * b.y + n2 * b.z));
|
|
its.dpdu = its.dpdv = Vector(0.0f);
|
|
} else {
|
|
const TangentSpace &t0 = vertexTangents[idx0];
|
|
const TangentSpace &t1 = vertexTangents[idx1];
|
|
const TangentSpace &t2 = vertexTangents[idx2];
|
|
const Vector dpdu = t0.dpdu * b.x + t1.dpdu * b.y + t2.dpdu * b.z;
|
|
its.shFrame.n = normalize(n0 * b.x + n1 * b.y + n2 * b.z);
|
|
its.shFrame.s = normalize(dpdu - its.shFrame.n
|
|
* dot(its.shFrame.n, dpdu));
|
|
its.shFrame.t = cross(its.shFrame.n, its.shFrame.s);
|
|
its.dpdu = dpdu;
|
|
its.dpdv = t0.dpdv * b.x + t1.dpdv * b.y + t2.dpdv * b.z;
|
|
}
|
|
} else {
|
|
its.shFrame = its.geoFrame;
|
|
its.dpdu = its.dpdv = Vector(0.0f);
|
|
}
|
|
|
|
if (EXPECT_TAKEN(vertexTexcoords)) {
|
|
const Point2 &t0 = vertexTexcoords[idx0];
|
|
const Point2 &t1 = vertexTexcoords[idx1];
|
|
const Point2 &t2 = vertexTexcoords[idx2];
|
|
its.uv = t0 * b.x + t1 * b.y + t2 * b.z;
|
|
} else {
|
|
its.uv = Point2(0.0f);
|
|
}
|
|
|
|
if (EXPECT_NOT_TAKEN(vertexColors)) {
|
|
const Spectrum &c0 = vertexColors[idx0],
|
|
&c1 = vertexColors[idx1],
|
|
&c2 = vertexColors[idx2];
|
|
its.color = c0 * b.x + c1 * b.y + c2 * b.z;
|
|
}
|
|
|
|
its.wi = its.toLocal(-ray.d);
|
|
its.shape = trimesh;
|
|
its.hasUVPartials = false;
|
|
} else {
|
|
shape->fillIntersectionRecord(ray,
|
|
reinterpret_cast<const uint8_t*>(temp) + 8, its);
|
|
}
|
|
its.time = ray.time;
|
|
}
|
|
|
|
/// Plain shadow ray query (used by the 'instance' plugin)
|
|
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt) const {
|
|
Float mint, maxt, tempT = std::numeric_limits<Float>::infinity();
|
|
if (m_aabb.rayIntersect(ray, mint, maxt)) {
|
|
if (_mint > mint) mint = _mint;
|
|
if (_maxt < maxt) maxt = _maxt;
|
|
|
|
if (EXPECT_TAKEN(maxt > mint))
|
|
return rayIntersectHavran<true>(ray, mint, maxt, tempT, NULL);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Plain intersection query (used by the 'instance' plugin)
|
|
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt, Float &t, void *temp) const {
|
|
Float mint, maxt, tempT = std::numeric_limits<Float>::infinity();
|
|
if (m_aabb.rayIntersect(ray, mint, maxt)) {
|
|
if (_mint > mint) mint = _mint;
|
|
if (_maxt < maxt) maxt = _maxt;
|
|
|
|
if (EXPECT_TAKEN(maxt > mint)) {
|
|
if (rayIntersectHavran<false>(ray, mint, maxt, tempT, temp)) {
|
|
t = tempT;
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// Virtual destructor
|
|
virtual ~ShapeKDTree();
|
|
private:
|
|
std::vector<const Shape *> m_shapes;
|
|
std::vector<bool> m_triangleFlag;
|
|
std::vector<index_type> m_shapeMap;
|
|
#if !defined(MTS_KD_CONSERVE_MEMORY)
|
|
TriAccel *m_triAccel;
|
|
#endif
|
|
BSphere m_bsphere;
|
|
};
|
|
|
|
MTS_NAMESPACE_END
|
|
|
|
#endif /* __SHAPE_KDTREE_H */
|