realtime preview: render triangle approximations of analytic shapes

metadata
Wenzel Jakob 2010-10-25 09:05:30 +02:00
parent 9689d85377
commit a3842d5e2c
11 changed files with 311 additions and 23 deletions

View File

@ -55,6 +55,9 @@ public:
/// Release bound resources /// Release bound resources
void unbind(); void unbind();
/// Return all bound triangle meshes
inline const std::vector<const TriMesh *> &getMeshes() const { return m_meshes; }
/// Return the shadow cube map for debugging purposes /// Return the shadow cube map for debugging purposes
inline GPUTexture *getShadowMap() { return m_shadowMap; } inline GPUTexture *getShadowMap() { return m_shadowMap; }
@ -268,6 +271,7 @@ private:
VPLProgramConfiguration m_targetConfig; VPLProgramConfiguration m_targetConfig;
ref<GPUProgram> m_backgroundProgram; ref<GPUProgram> m_backgroundProgram;
VPLDependencyNode m_backgroundDependencies; VPLDependencyNode m_backgroundDependencies;
std::vector<const TriMesh *> m_meshes;
}; };
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -251,10 +251,22 @@ public:
*/ */
virtual const AbstractKDTree *getKDTree() const; virtual const AbstractKDTree *getKDTree() const;
/**
* \brief Create a triangle mesh approximation of this shape
*
* This function is used by the realtime preview and
* certain integrators, which rely on hardware rasterization.
*
* The default implementation simply returns \a NULL.
*/
virtual ref<TriMesh> createTriMesh();
/// Return the shape's BSDF /// Return the shape's BSDF
inline const BSDF *getBSDF() const { return m_bsdf.get(); } inline const BSDF *getBSDF() const { return m_bsdf.get(); }
/// Return the shape's BSDF /// Return the shape's BSDF
inline BSDF *getBSDF() { return m_bsdf.get(); } inline BSDF *getBSDF() { return m_bsdf.get(); }
/// Set the BSDF of this shape
inline void setBSDF(BSDF *bsdf) { m_bsdf = bsdf; }
/// Return the name of this shape /// Return the name of this shape
virtual std::string getName() const; virtual std::string getName() const;
@ -272,6 +284,8 @@ public:
inline Luminaire *getLuminaire() { return m_luminaire; } inline Luminaire *getLuminaire() { return m_luminaire; }
/// Return the associated luminaire (if any) /// Return the associated luminaire (if any)
inline const Luminaire *getLuminaire() const { return m_luminaire.get(); } inline const Luminaire *getLuminaire() const { return m_luminaire.get(); }
/// Set the luminaire of this shape
inline void setLuminaire(Luminaire *luminaire) { m_luminaire = luminaire; }
/// Called once after parsing /// Called once after parsing
virtual void configure(); virtual void configure();

View File

@ -22,6 +22,9 @@
#include <mitsuba/core/triangle.h> #include <mitsuba/core/triangle.h>
#include <mitsuba/core/pdf.h> #include <mitsuba/core/pdf.h>
#include <mitsuba/render/shape.h> #include <mitsuba/render/shape.h>
#include <boost/filesystem/fstream.hpp>
namespace fs = boost::filesystem;
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -116,6 +119,20 @@ public:
/// Return the number of vertices /// Return the number of vertices
inline size_t getVertexCount() const { return m_vertexCount; } inline size_t getVertexCount() const { return m_vertexCount; }
/**
* \brief Create a triangle mesh approximation of this shape
*
* Since instances are already triangle meshes, the implementation
* just returns a pointer to \a this.
*/
ref<TriMesh> createTriMesh();
/// Export an Wavefront OBJ version of this file
void writeOBJ(const fs::path &path) const;
/// Return the shape's BSDF
inline const BSDF *getBSDF() const { return m_bsdf.get(); }
/// Sample a point on the mesh /// Sample a point on the mesh
Float sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const; Float sampleArea(ShapeSamplingRecord &sRec, const Point2 &sample) const;

View File

@ -56,7 +56,7 @@ public:
Transform projectionTransform = camera->getGLProjectionTransform(jitter); Transform projectionTransform = camera->getGLProjectionTransform(jitter);
m_renderer->setCamera(projectionTransform.getMatrix(), camera->getViewTransform().getMatrix()); m_renderer->setCamera(projectionTransform.getMatrix(), camera->getViewTransform().getMatrix());
Transform clipToWorld = camera->getViewTransform().inverse() * projectionTransform.inverse(); Transform clipToWorld = camera->getViewTransform().inverse() * projectionTransform.inverse();
const std::vector<TriMesh *> meshes = scene->getMeshes(); const std::vector<const TriMesh *> meshes = m_shaderManager->getMeshes();
Point camPos = scene->getCamera()->getPosition(); Point camPos = scene->getCamera()->getPosition();
m_renderer->beginDrawingMeshes(); m_renderer->beginDrawingMeshes();

View File

@ -115,14 +115,19 @@ void VPLShaderManager::init() {
m_altShadowProgramParam_depthVec = m_altShadowProgramParam_depthVec =
m_altShadowProgram->getParameterID("depthVec"); m_altShadowProgram->getParameterID("depthVec");
const std::vector<TriMesh *> meshes = m_scene->getMeshes(); const std::vector<Shape *> shapes = m_scene->getShapes();
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires(); const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
for (size_t i=0; i<meshes.size(); ++i) { for (size_t i=0; i<shapes.size(); ++i) {
m_renderer->registerGeometry(meshes[i]); ref<TriMesh> triMesh = shapes[i]->createTriMesh();
Shader *shader = m_renderer->registerShaderForResource(meshes[i]->getBSDF()); if (!triMesh)
continue;
m_renderer->registerGeometry(triMesh);
Shader *shader = m_renderer->registerShaderForResource(triMesh->getBSDF());
if (shader != NULL && !shader->isComplete()) if (shader != NULL && !shader->isComplete())
m_renderer->unregisterShaderForResource(meshes[i]->getBSDF()); m_renderer->unregisterShaderForResource(triMesh->getBSDF());
triMesh->incRef();
m_meshes.push_back(triMesh);
} }
for (size_t i=0; i<luminaires.size(); ++i) for (size_t i=0; i<luminaires.size(); ++i)
@ -581,13 +586,14 @@ void VPLShaderManager::cleanup() {
m_backgroundProgram = NULL; m_backgroundProgram = NULL;
} }
const std::vector<TriMesh *> meshes = m_scene->getMeshes();
const std::vector<Luminaire *> luminaires = m_scene->getLuminaires(); const std::vector<Luminaire *> luminaires = m_scene->getLuminaires();
for (size_t i=0; i<meshes.size(); ++i) { for (size_t i=0; i<m_meshes.size(); ++i) {
m_renderer->unregisterGeometry(meshes[i]); m_renderer->unregisterGeometry(m_meshes[i]);
m_renderer->unregisterShaderForResource(meshes[i]->getBSDF()); m_renderer->unregisterShaderForResource(m_meshes[i]->getBSDF());
m_meshes[i]->decRef();
} }
m_meshes.clear();
for (size_t i=0; i<luminaires.size(); ++i) for (size_t i=0; i<luminaires.size(); ++i)
m_renderer->unregisterShaderForResource(luminaires[i]); m_renderer->unregisterShaderForResource(luminaires[i]);
m_initialized = false; m_initialized = false;

View File

@ -146,6 +146,9 @@ Float Shape::pdfArea(const ShapeSamplingRecord &sRec) const {
return 0.0f; return 0.0f;
} }
ref<TriMesh> Shape::createTriMesh() {
return NULL;
}
std::string ShapeSamplingRecord::toString() const { std::string ShapeSamplingRecord::toString() const {
std::ostringstream oss; std::ostringstream oss;

View File

@ -465,6 +465,10 @@ void TriMesh::computeTangentSpaceBasis() {
m_name.c_str(), zeroArea, zeroNormals); m_name.c_str(), zeroArea, zeroNormals);
} }
ref<TriMesh> TriMesh::createTriMesh() {
return this;
}
void TriMesh::serialize(Stream *stream, InstanceManager *manager) const { void TriMesh::serialize(Stream *stream, InstanceManager *manager) const {
Shape::serialize(stream, manager); Shape::serialize(stream, manager);
uint32_t flags = 0; uint32_t flags = 0;
@ -497,6 +501,46 @@ void TriMesh::serialize(Stream *stream, InstanceManager *manager) const {
m_triangleCount * sizeof(Triangle)/sizeof(uint32_t)); m_triangleCount * sizeof(Triangle)/sizeof(uint32_t));
} }
void TriMesh::writeOBJ(const fs::path &path) const {
fs::ofstream os(path);
os << "o " << m_name << endl;
for (size_t i=0; i<m_vertexCount; ++i) {
os << "v "
<< m_positions[i].x << " "
<< m_positions[i].y << " "
<< m_positions[i].z << endl;
}
if (m_normals) {
for (size_t i=0; i<m_vertexCount; ++i) {
os << "vn "
<< m_normals[i].x << " "
<< m_normals[i].y << " "
<< m_normals[i].z << endl;
}
}
if (m_normals) {
for (size_t i=0; i<m_triangleCount; ++i) {
os << "f "
<< m_triangles[i].idx[0] + 1 << "//"
<< m_triangles[i].idx[0] + 1 << " "
<< m_triangles[i].idx[1] + 1 << "//"
<< m_triangles[i].idx[1] + 1 << " "
<< m_triangles[i].idx[2] + 1 << "//"
<< m_triangles[i].idx[2] + 1 << endl;
}
} else {
for (size_t i=0; i<m_triangleCount; ++i) {
os << "f "
<< m_triangles[i].idx[0] + 1 << " "
<< m_triangles[i].idx[1] + 1 << " "
<< m_triangles[i].idx[2] + 1 << endl;
}
}
os.close();
}
void TriMesh::serialize(Stream *_stream) const { void TriMesh::serialize(Stream *_stream) const {
ref<Stream> stream = _stream; ref<Stream> stream = _stream;

View File

@ -463,7 +463,7 @@ void PreviewThread::run() {
} }
void PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) { void PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) {
const std::vector<TriMesh *> meshes = m_context->scene->getMeshes(); const std::vector<const TriMesh *> meshes = m_shaderManager->getMeshes();
m_shaderManager->setVPL(vpl); m_shaderManager->setVPL(vpl);

View File

@ -20,6 +20,7 @@
#include <mitsuba/render/bsdf.h> #include <mitsuba/render/bsdf.h>
#include <mitsuba/render/subsurface.h> #include <mitsuba/render/subsurface.h>
#include <mitsuba/render/luminaire.h> #include <mitsuba/render/luminaire.h>
#include <mitsuba/render/trimesh.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -373,24 +374,59 @@ public:
return clippedAABB; return clippedAABB;
} }
#if 0 ref<TriMesh> createTriMesh() {
inline AABB getAABB(Float start, Float end) const { /// Choice of discretization
AABB result; const size_t phiSteps = 20;
const Float r = m_radius; const Float dPhi = (2*M_PI) / phiSteps;
const Point a = m_objectToWorld(Point(0, 0, start));
const Point b = m_objectToWorld(Point(0, 0, end));
ref<TriMesh> mesh = new TriMesh("Cylinder approximation",
phiSteps*2, phiSteps*2, true, false, false);
Point *vertices = mesh->getVertexPositions();
Normal *normals = mesh->getVertexNormals();
Triangle *triangles = mesh->getTriangles();
size_t triangleIdx = 0, vertexIdx = 0;
for (size_t phi=0; phi<phiSteps; ++phi) {
Float sinPhi = std::sin(phi * dPhi);
Float cosPhi = std::cos(phi * dPhi);
int idx0 = vertexIdx, idx1 = idx0+1;
int idx2 = (vertexIdx+2) % (2*phiSteps), idx3 = idx2+1;
normals[vertexIdx] = m_objectToWorld(Normal(cosPhi, sinPhi, 0));
vertices[vertexIdx++] = m_objectToWorld(Point(cosPhi*m_radius, sinPhi*m_radius, 0));
normals[vertexIdx] = m_objectToWorld(Normal(cosPhi, sinPhi, 0));
vertices[vertexIdx++] = m_objectToWorld(Point(cosPhi*m_radius, sinPhi*m_radius, m_length));
triangles[triangleIdx].idx[0] = idx0;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx1;
triangleIdx++;
triangles[triangleIdx].idx[0] = idx1;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx3;
triangleIdx++;
}
mesh->setBSDF(m_bsdf);
mesh->setLuminaire(m_luminaire);
mesh->configure();
return mesh.get();
}
#if 0
AABB getAABB() const {
const Point a = m_objectToWorld(Point(0, 0, 0));
const Point b = m_objectToWorld(Point(0, 0, m_length));
const Float r = m_radius;
AABB result;
result.expandBy(a - Vector(r, r, r)); result.expandBy(a - Vector(r, r, r));
result.expandBy(a + Vector(r, r, r)); result.expandBy(a + Vector(r, r, r));
result.expandBy(b - Vector(r, r, r)); result.expandBy(b - Vector(r, r, r));
result.expandBy(b + Vector(r, r, r)); result.expandBy(b + Vector(r, r, r));
return result; return result;
} }
AABB getAABB() const {
/* Very approximate .. */
return getAABB(0, m_length);
}
#endif #endif
Float getSurfaceArea() const { Float getSurfaceArea() const {

View File

@ -21,6 +21,7 @@
#include <mitsuba/render/subsurface.h> #include <mitsuba/render/subsurface.h>
#include <mitsuba/render/luminaire.h> #include <mitsuba/render/luminaire.h>
#include <mitsuba/render/gkdtree.h> #include <mitsuba/render/gkdtree.h>
#include <mitsuba/render/trimesh.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/fresolver.h> #include <mitsuba/core/fresolver.h>
@ -46,9 +47,10 @@ public:
for (size_t i=0; i<m_vertices.size()-1; i++) for (size_t i=0; i<m_vertices.size()-1; i++)
if (!m_vertexStartsFiber[i+1]) if (!m_vertexStartsFiber[i+1])
m_segIndex.push_back(i); m_segIndex.push_back(i);
m_segmentCount = m_segIndex.size();
Log(EDebug, "Building a kd-tree for " SIZE_T_FMT " hair vertices, " Log(EDebug, "Building a kd-tree for " SIZE_T_FMT " hair vertices, "
SIZE_T_FMT " segments,", m_vertices.size(), m_segIndex.size()); SIZE_T_FMT " segments,", m_vertices.size(), m_segmentCount);
/* Ray-cylinder intersections are expensive. Use only the /* Ray-cylinder intersections are expensive. Use only the
SAH cost as the tree subdivision stopping criterion, SAH cost as the tree subdivision stopping criterion,
@ -91,6 +93,11 @@ public:
return m_radius; return m_radius;
} }
/// Return the total number of segments
inline size_t getSegmentCount() const {
return m_segmentCount;
}
/// Intersect a ray with all segments stored in the kd-tree /// Intersect a ray with all segments stored in the kd-tree
inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt, inline bool rayIntersect(const Ray &ray, Float _mint, Float _maxt,
Float &t, void *temp) const { Float &t, void *temp) const {
@ -247,6 +254,7 @@ protected:
std::vector<Point> m_vertices; std::vector<Point> m_vertices;
std::vector<bool> m_vertexStartsFiber; std::vector<bool> m_vertexStartsFiber;
std::vector<index_type> m_segIndex; std::vector<index_type> m_segIndex;
size_t m_segmentCount;
Float m_radius; Float m_radius;
}; };
@ -366,6 +374,74 @@ public:
its.shape = this; its.shape = this;
} }
ref<TriMesh> createTriMesh() {
/// Choice of discretization
const size_t phiSteps = 6;
const Float dPhi = (2*M_PI) / phiSteps;
size_t nSegments = m_kdtree->getSegmentCount();
ref<TriMesh> mesh = new TriMesh("Hair mesh approximation",
phiSteps*2*nSegments, phiSteps*2*nSegments, true, false, false);
Point *vertices = mesh->getVertexPositions();
Normal *normals = mesh->getVertexNormals();
Triangle *triangles = mesh->getTriangles();
size_t triangleIdx = 0, vertexIdx = 0;
const std::vector<Point> &hairVertices = m_kdtree->getVertices();
const std::vector<bool> &vertexStartsFiber = m_kdtree->getStartFiber();
const Float radius = m_kdtree->getRadius();
Float *cosPhi = new Float[phiSteps];
Float *sinPhi = new Float[phiSteps];
for (size_t i=0; i<phiSteps; ++i) {
sinPhi[i] = std::sin(i*dPhi);
cosPhi[i] = std::cos(i*dPhi);
}
size_t hairIdx = 0;
for (size_t iv=0; iv<hairVertices.size()-1; iv++) {
if (!vertexStartsFiber[iv+1]) {
for (size_t phi=0; phi<phiSteps; ++phi) {
Vector tangent = m_kdtree->tangent(iv);
Vector dir = Normal(Frame(tangent).toWorld(
Vector(cosPhi[phi], sinPhi[phi], 0)));
Normal miterNormal1 = m_kdtree->firstMiterNormal(iv);
Normal miterNormal2 = m_kdtree->secondMiterNormal(iv);
Float t1 = dot(miterNormal1, radius*dir) / dot(miterNormal1, tangent);
Float t2 = dot(miterNormal2, radius*dir) / dot(miterNormal2, tangent);
normals[vertexIdx] = Normal(dir);
vertices[vertexIdx++] = m_kdtree->firstVertex(iv) + radius*dir - tangent*t1;
normals[vertexIdx] = Normal(dir);
vertices[vertexIdx++] = m_kdtree->secondVertex(iv) + radius*dir - tangent*t2;
int idx0 = 2*(phi + hairIdx*phiSteps), idx1 = idx0+1;
int idx2 = (2*phi+2) % (2*phiSteps) + 2*hairIdx*phiSteps, idx3 = idx2+1;
triangles[triangleIdx].idx[0] = idx0;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx1;
triangleIdx++;
triangles[triangleIdx].idx[0] = idx1;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx3;
triangleIdx++;
}
hairIdx++;
}
}
Assert(triangleIdx == phiSteps*2*nSegments);
Assert(vertexIdx == phiSteps*2*nSegments);
delete[] cosPhi;
delete[] sinPhi;
mesh->setBSDF(m_bsdf);
mesh->setLuminaire(m_luminaire);
mesh->configure();
return mesh.get();
}
const AbstractKDTree *getKDTree() const { const AbstractKDTree *getKDTree() const {
return m_kdtree.get(); return m_kdtree.get();
} }

View File

@ -20,6 +20,7 @@
#include <mitsuba/render/bsdf.h> #include <mitsuba/render/bsdf.h>
#include <mitsuba/render/luminaire.h> #include <mitsuba/render/luminaire.h>
#include <mitsuba/render/subsurface.h> #include <mitsuba/render/subsurface.h>
#include <mitsuba/render/trimesh.h>
#include <mitsuba/core/properties.h> #include <mitsuba/core/properties.h>
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -258,6 +259,93 @@ public:
return squareToConePdf(cosThetaMax); return squareToConePdf(cosThetaMax);
} }
ref<TriMesh> createTriMesh() {
/// Choice of discretization
const size_t thetaSteps = 20;
const size_t phiSteps = thetaSteps * 2;
const Float dTheta = M_PI / (thetaSteps-1);
const Float dPhi = (2*M_PI) / phiSteps;
size_t topIdx = (thetaSteps-2) * phiSteps, botIdx = topIdx+1;
/// Precompute cosine and sine tables
Float *cosPhi = new Float[phiSteps];
Float *sinPhi = new Float[phiSteps];
for (size_t i=0; i<phiSteps; ++i) {
sinPhi[i] = std::sin(i*dPhi);
cosPhi[i] = std::cos(i*dPhi);
}
size_t numTris = 2 * phiSteps * (thetaSteps-2);
ref<TriMesh> mesh = new TriMesh("Sphere approximation",
numTris, botIdx+1, true, false, false);
Point *vertices = mesh->getVertexPositions();
Normal *normals = mesh->getVertexNormals();
Triangle *triangles = mesh->getTriangles();
size_t vertexIdx = 0;
for (size_t theta=1; theta<thetaSteps-1; ++theta) {
Float sinTheta = std::sin(theta * dTheta);
Float cosTheta = std::cos(theta * dTheta);
for (size_t phi=0; phi<phiSteps; ++phi) {
Vector v(
sinTheta * cosPhi[phi],
sinTheta * sinPhi[phi],
cosTheta
);
vertices[vertexIdx] = m_objectToWorld(Point(v*m_radius));
normals[vertexIdx++] = m_objectToWorld(Normal(v));
}
}
vertices[vertexIdx] = m_objectToWorld(Point(0, 0, m_radius));
normals[vertexIdx++] = m_objectToWorld(Normal(0, 0, 1));
vertices[vertexIdx] = m_objectToWorld(Point(0, 0, -m_radius));
normals[vertexIdx++] = m_objectToWorld(Normal(0, 0, -1));
Assert(vertexIdx == botIdx+1);
size_t triangleIdx = 0;
for (size_t theta=1; theta<thetaSteps; ++theta) {
for (size_t phi=0; phi<phiSteps; ++phi) {
size_t nextPhi = (phi + 1) % phiSteps;
size_t idx0, idx1, idx2, idx3;
if (theta == 1) {
idx0 = idx1 = topIdx;
} else {
idx0 = phiSteps*(theta-2) + phi;
idx1 = phiSteps*(theta-2) + nextPhi;
}
if (theta == thetaSteps-1) {
idx2 = idx3 = botIdx;
} else {
idx2 = phiSteps*(theta-1) + phi;
idx3 = phiSteps*(theta-1) + nextPhi;
}
if (idx0 != idx1) {
triangles[triangleIdx].idx[0] = idx0;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx1;
triangleIdx++;
}
if (idx2 != idx3) {
triangles[triangleIdx].idx[0] = idx1;
triangles[triangleIdx].idx[1] = idx2;
triangles[triangleIdx].idx[2] = idx3;
triangleIdx++;
}
}
}
Assert(triangleIdx == numTris);
delete[] cosPhi;
delete[] sinPhi;
mesh->setBSDF(m_bsdf);
mesh->setLuminaire(m_luminaire);
mesh->configure();
return mesh.get();
}
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "Sphere[" << endl oss << "Sphere[" << endl