VPL pruning

metadata
Wenzel Jakob 2011-04-24 15:24:04 +02:00
parent 2d193b8b4f
commit 3e8e0bba53
11 changed files with 95 additions and 35 deletions

View File

@ -50,7 +50,7 @@ public:
const Luminaire *luminaire, const Point &camPos, bool faceNormals);
/// Draw the background if there is an environment luminaire
void drawBackground(const Transform &clipToWorld, const Point &camPos);
void drawBackground(const Transform &clipToWorld, const Point &camPos, Float scaleFactor);
/// Release bound resources
void unbind();

View File

@ -32,11 +32,11 @@ class MTS_EXPORT_RENDER PreviewWorker : public WorkProcessor {
public:
inline PreviewWorker(int blockSize, Point cameraO, Vector cameraTL,
Vector cameraDx, Vector cameraDy, const VPL &vpl, Float minDist, bool coherent,
bool diffuseSources, bool diffuseReceivers)
bool diffuseSources, bool diffuseReceivers, Float backgroundScale)
: m_blockSize(blockSize), m_cameraO(cameraO), m_cameraTL(cameraTL),
m_cameraDx(cameraDx), m_cameraDy(cameraDy), m_vpl(vpl),
m_minDist(minDist), m_coherent(coherent), m_diffuseSources(diffuseSources),
m_diffuseReceivers(diffuseReceivers) {
m_diffuseReceivers(diffuseReceivers), m_backgroundScale(backgroundScale) {
}
void processIncoherent(const WorkUnit *workUnit, WorkResult *workResult,
@ -71,6 +71,7 @@ private:
const std::vector<const Shape *> *m_shapes;
bool m_coherent;
bool m_diffuseSources, m_diffuseReceivers;
Float m_backgroundScale;
};
MTS_NAMESPACE_END

View File

@ -46,16 +46,20 @@ struct VPL {
/**
* Generate a series of point light sources by sampling from the Halton
* sequence (as is done in Instant Radiosity). The parameter <code>offset</code>
* sequence (as is done in Instant Radiosity). The parameter \c offset
* allows setting the initial QMC sample index (should be set to 0 if no offset is
* desired), and the last index is returned after the function finishes. This can
* be used to generate an arbitrary number of VPLs incrementally. Note that the
* parameter <code>count</code> is only a suggestion. Generally, the implementation
* will produce a few more VPLs. After VPL generation is done, their power must be scaled
* by the inverse of the returned index.
* value supplied with the parameter \c count is only a suggestion to the implementation.
* Generally, it will produce a few more VPLs than the requsted amount. After VPL
* generation is done, their power must be scaled by the inverse of the returned index.
* The implementation here also needs an pseudorandom number generator, which
* is used to prune VPLs in an unbiased manner.
*/
extern MTS_EXPORT_RENDER size_t generateVPLs(const Scene *scene, size_t offset,
size_t count, int maxDepth, std::deque<VPL> &vpls);
extern MTS_EXPORT_RENDER size_t generateVPLs(const Scene *scene,
Random *random, size_t offset,
size_t count, int maxDepth, bool prune,
std::deque<VPL> &vpls);
MTS_NAMESPACE_END

View File

@ -17,6 +17,7 @@
*/
#include <mitsuba/core/statistics.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/hw/vpl.h>
#include <mitsuba/hw/session.h>
#include <mitsuba/hw/device.h>
@ -46,6 +47,7 @@ public:
m_session = Session::create();
m_device = Device::create(m_session);
m_renderer = Renderer::create(m_session);
m_random = new Random();
}
@ -73,7 +75,7 @@ public:
m_shaderManager->unbind();
}
m_renderer->endDrawingMeshes();
m_shaderManager->drawBackground(clipToWorld, camPos);
m_shaderManager->drawBackground(clipToWorld, camPos, 1.0f);
}
bool preprocess(const Scene *scene, RenderQueue *queue, const RenderJob *job,
@ -81,7 +83,8 @@ public:
Integrator::preprocess(scene, queue, job, sceneResID, cameraResID, samplerResID);
if (m_vpls.size() == 0) {
Float normalization = (Float) 1 / generateVPLs(scene, 0, m_vplCount, m_maxDepth, m_vpls);
Float normalization = (Float) 1 / generateVPLs(scene, m_random,
0, m_vplCount, m_maxDepth, true, m_vpls);
for (size_t i=0; i<m_vpls.size(); ++i)
m_vpls[i].P *= normalization;
Log(EInfo, "Generated %i virtual point lights", m_vpls.size());

View File

@ -177,8 +177,9 @@ void VPLShaderManager::init() {
oss << "varying vec3 d;" << endl
<< "uniform vec3 camPos;" << endl
<< "uniform float scale;" << endl
<< "void main() {" << endl
<< " gl_FragColor.rgb = " << evalName << "_background(normalize(d - camPos));" << endl
<< " gl_FragColor.rgb = scale * " << evalName << "_background(normalize(d - camPos));" << endl
<< " gl_FragColor.a = 1.0;" << endl
<< "}" << endl;
@ -606,7 +607,7 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf,
m_targetConfig.bind(program, config, textureUnitOffset);
}
void VPLShaderManager::drawBackground(const Transform &clipToWorld, const Point &camPos) {
void VPLShaderManager::drawBackground(const Transform &clipToWorld, const Point &camPos, Float scaleFactor) {
if (m_backgroundProgram == NULL)
return;
int textureUnitOffset = 0;
@ -615,6 +616,7 @@ void VPLShaderManager::drawBackground(const Transform &clipToWorld, const Point
m_backgroundDependencies, textureUnitOffset);
m_backgroundProgram->setParameter("clipToWorld", clipToWorld, false);
m_backgroundProgram->setParameter("camPos", camPos, false);
m_backgroundProgram->setParameter("scale", scaleFactor);
m_renderer->blitQuad(false);
m_backgroundProgram->unbind();
m_backgroundDependencies.recursiveUnbind();

View File

@ -79,7 +79,7 @@ void PreviewWorker::processIncoherent(const WorkUnit *workUnit, WorkResult *work
++numRays;
if (!m_kdtree->rayIntersect(primary, its)) {
block->setPixel(pos++, m_scene->LeBackground(primary));
block->setPixel(pos++, m_scene->LeBackground(primary)*m_backgroundScale);
continue;
}
@ -283,7 +283,7 @@ void PreviewWorker::processCoherent(const WorkUnit *workUnit, WorkResult *workRe
Point(primRay4.o[0].f[idx], primRay4.o[1].f[idx], primRay4.o[2].f[idx]),
Vector(primRay4.d[0].f[idx], primRay4.d[1].f[idx], primRay4.d[2].f[idx]),
0.0f
));
)) * m_backgroundScale;
memset(&direct[idx], 0, sizeof(Spectrum));
continue;
}
@ -457,7 +457,7 @@ void PreviewWorker::processCoherent(const WorkUnit *workUnit, WorkResult *workRe
ref<WorkProcessor> PreviewWorker::clone() const {
return new PreviewWorker(m_blockSize, m_cameraO, m_cameraTL,
m_cameraDx, m_cameraDy, m_vpl, m_minDist, m_coherent,
m_diffuseSources, m_diffuseReceivers);
m_diffuseSources, m_diffuseReceivers, m_backgroundScale);
}
MTS_IMPLEMENT_CLASS(PreviewWorker, false, WorkProcessor)

View File

@ -18,11 +18,48 @@
#include <mitsuba/render/vpl.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/core/statistics.h>
MTS_NAMESPACE_BEGIN
size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDepth,
std::deque<VPL> &vpls) {
static StatsCounter prunedVPLs("VPL generator", "Pruned VPLs", EPercentage);
static void appendVPL(const Scene *scene, Random *random,
VPL &vpl, bool prune, std::deque<VPL> &vpls) {
prunedVPLs.incrementBase();
if (prune) {
/* Possibly reject VPLs if they are unlikely to be
visible from the camera */
int nSuccesses = 0, nSamples = 50;
const Shape *shape;
Normal n;
Ray ray;
Float t;
for (int i=0; i<nSamples; ++i) {
scene->getCamera()->generateRay(Point2(random->nextFloat(),
random->nextFloat()), Point2(0.0f), 0, ray);
if (scene->rayIntersect(ray, t, shape, n)) {
if (!scene->isOccluded(ray(t), vpl.its.p, 0))
++nSuccesses;
} else {
++nSuccesses; // be conservative
}
}
/// Have a small chance of acceptance in any case
Float acceptanceProb = (nSuccesses+1) / (Float) (nSamples+1);
if (random->nextFloat() < acceptanceProb) {
vpl.P /= acceptanceProb;
vpls.push_back(vpl);
} else {
++prunedVPLs;
}
} else {
vpls.push_back(vpl);
}
}
size_t generateVPLs(const Scene *scene, Random *random,
size_t offset, size_t count, int maxDepth, bool prune, std::deque<VPL> &vpls) {
ref<Sampler> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Sampler), Properties("halton")));
EmissionRecord eRec;
@ -50,7 +87,7 @@ size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDept
lumVPL.its.shFrame = (eRec.luminaire->getType() & Luminaire::EOnSurface)
? Frame(eRec.sRec.n) : stdFrame;
lumVPL.luminaire = eRec.luminaire;
vpls.push_back(lumVPL);
appendVPL(scene, random, lumVPL, prune, vpls);
weight *= eRec.luminaire->sampleEmissionDirection(eRec, dirSample);
Float cosTheta = (eRec.luminaire->getType() & Luminaire::EOnSurface)
@ -87,7 +124,7 @@ size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDept
VPL vpl(ESurfaceVPL, weight);
vpl.its = its;
vpls.push_back(vpl);
appendVPL(scene, random, vpl, prune, vpls);
weight *= bsdfVal;

View File

@ -29,8 +29,8 @@ PreviewThread::PreviewThread(Device *parentDevice, Renderer *parentRenderer)
m_renderer = Renderer::create(m_session);
m_mutex = new Mutex();
m_queueCV = new ConditionVariable(m_mutex);
m_random = new Random();
m_bufferCount = 3;
m_backgroundScaleFactor = 1.0f;
m_queueEntryIndex = 0;
m_session->init();
m_timer = new Timer();
@ -58,6 +58,8 @@ PreviewThread::PreviewThread(Device *parentDevice, Renderer *parentRenderer)
for (int i=0; i<m_bufferCount; ++i)
m_recycleQueue.push_back(PreviewQueueEntry(m_queueEntryIndex++));
m_random = new Random();
MTS_AUTORELEASE_END()
}
@ -350,9 +352,12 @@ void PreviewThread::run() {
m_timer->reset();
}
if (m_vpls.empty())
m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset,
1, m_context->pathLength, m_vpls);
if (m_vpls.empty()) {
size_t oldOffset = m_vplSampleOffset;
m_vplSampleOffset = generateVPLs(m_context->scene, m_random,
m_vplSampleOffset, 1, m_context->pathLength, !m_motion, m_vpls);
m_backgroundScaleFactor = m_vplSampleOffset - oldOffset;
}
VPL vpl = m_vpls.front();
m_vpls.pop_front();
@ -388,9 +393,12 @@ void PreviewThread::run() {
m_timer->reset();
}
if (m_vpls.empty())
m_vplSampleOffset = generateVPLs(m_context->scene, m_vplSampleOffset,
1, m_context->pathLength, m_vpls);
if (m_vpls.empty()) {
size_t oldOffset = m_vplSampleOffset;
m_vplSampleOffset = generateVPLs(m_context->scene, m_random,
m_vplSampleOffset, 1, m_context->pathLength, !m_motion, m_vpls);
m_backgroundScaleFactor = m_vplSampleOffset - oldOffset;
}
VPL vpl = m_vpls.front();
m_vpls.pop_front();
@ -500,7 +508,8 @@ void PreviewThread::oglRenderVPL(PreviewQueueEntry &target, const VPL &vpl) {
m_shaderManager->unbind();
}
m_renderer->endDrawingMeshes();
m_shaderManager->drawBackground(clipToWorld, camPos);
m_shaderManager->drawBackground(clipToWorld, camPos,
m_backgroundScaleFactor);
m_framebuffer->releaseTarget();
target.buffer->activateTarget();
@ -592,7 +601,8 @@ void PreviewThread::rtrtRenderVPL(PreviewQueueEntry &target, const VPL &vpl) {
target.buffer->getBitmap(),
m_context->previewMethod == ERayTraceCoherent,
m_context->diffuseSources,
m_context->diffuseReceivers);
m_context->diffuseReceivers,
m_backgroundScaleFactor);
m_mutex->unlock();
ref<Scheduler> sched = Scheduler::getInstance();

View File

@ -84,11 +84,11 @@ private:
ref<GPUTexture> m_framebuffer;
ref<GPUProgram> m_accumProgram;
ref<PreviewProcess> m_previewProc;
ref<Random> m_random;
int m_accumProgramParam_source1;
int m_accumProgramParam_source2;
const GPUTexture *m_accumBuffer;
ref<Mutex> m_mutex;
ref<Random> m_random;
ref<ConditionVariable> m_queueCV;
ref<Timer> m_timer;
ref<WaitFlag> m_started;
@ -101,6 +101,7 @@ private:
std::vector<GPUTexture *> m_releaseList;
Point m_camPos;
Transform m_camViewTransform;
Float m_backgroundScaleFactor;
bool m_quit, m_sleep, m_motion, m_useSync;
bool m_refreshScene;
};

View File

@ -19,8 +19,8 @@
#include <mitsuba/render/rectwu.h>
#include "preview_proc.h"
PreviewProcess::PreviewProcess(const Scene *scene, int sceneResID, int blockSize)
: m_vpl(NULL) {
PreviewProcess::PreviewProcess(const Scene *scene, int sceneResID,
int blockSize) : m_vpl(NULL) {
m_blockSize = blockSize;
m_logLevel = ETrace;
m_mutex = new Mutex();
@ -73,7 +73,7 @@ void PreviewProcess::processResult(const WorkResult *result, bool cancelled) {
void PreviewProcess::configure(const VPL &vpl, Float minDist, const Point2 &jitter,
const Bitmap *source, Bitmap *target, bool coherent, bool diffuseSources,
bool diffuseReceivers) {
bool diffuseReceivers, Float backgroundScale) {
BlockedImageProcess::init(m_film->getCropOffset(), m_film->getCropSize(), m_blockSize);
m_source = source;
m_target = target;
@ -83,6 +83,7 @@ void PreviewProcess::configure(const VPL &vpl, Float minDist, const Point2 &jitt
m_coherent = coherent;
m_diffuseSources = diffuseSources;
m_diffuseReceivers = diffuseReceivers;
m_backgroundScale = backgroundScale;
/* It is not necessary to shoot normalized rays. Instead, interpolate:
here, we generate the upper left corner ray as well as the
@ -109,7 +110,7 @@ void PreviewProcess::configure(const VPL &vpl, Float minDist, const Point2 &jitt
ref<WorkProcessor> PreviewProcess::createWorkProcessor() const {
return new PreviewWorker(m_blockSize, m_cameraO, m_cameraTL,
m_cameraDx, m_cameraDy, *m_vpl, m_minDist, m_coherent,
m_diffuseSources, m_diffuseReceivers);
m_diffuseSources, m_diffuseReceivers, m_backgroundScale);
}

View File

@ -36,7 +36,7 @@ public:
void configure(const VPL &vpl, Float minDist, const Point2 &jitter,
const Bitmap *source, Bitmap *target, bool coherent,
bool diffuseSources, bool diffuseReceivers);
bool diffuseSources, bool diffuseReceivers, Float backgroundScale);
inline int getRayCount() const { return m_numRays; }
inline const Scene *getScene() const { return m_scene; }
@ -63,6 +63,7 @@ private:
bool m_coherent;
bool m_diffuseSources;
bool m_diffuseReceivers;
Float m_backgroundScale;
};
#endif /* __PREVIEW_PROC_H */