diff --git a/include/mitsuba/hw/vpl.h b/include/mitsuba/hw/vpl.h index 1f3e348e..2bab4119 100644 --- a/include/mitsuba/hw/vpl.h +++ b/include/mitsuba/hw/vpl.h @@ -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(); diff --git a/include/mitsuba/render/preview.h b/include/mitsuba/render/preview.h index 9518efdf..579cb9eb 100644 --- a/include/mitsuba/render/preview.h +++ b/include/mitsuba/render/preview.h @@ -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 *m_shapes; bool m_coherent; bool m_diffuseSources, m_diffuseReceivers; + Float m_backgroundScale; }; MTS_NAMESPACE_END diff --git a/include/mitsuba/render/vpl.h b/include/mitsuba/render/vpl.h index da282a16..d9f58017 100644 --- a/include/mitsuba/render/vpl.h +++ b/include/mitsuba/render/vpl.h @@ -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 offset + * 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 count 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 &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 &vpls); MTS_NAMESPACE_END diff --git a/src/integrators/vpl/vpl.cpp b/src/integrators/vpl/vpl.cpp index 568f1b77..69051dfd 100644 --- a/src/integrators/vpl/vpl.cpp +++ b/src/integrators/vpl/vpl.cpp @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -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; isetParameter("clipToWorld", clipToWorld, false); m_backgroundProgram->setParameter("camPos", camPos, false); + m_backgroundProgram->setParameter("scale", scaleFactor); m_renderer->blitQuad(false); m_backgroundProgram->unbind(); m_backgroundDependencies.recursiveUnbind(); diff --git a/src/librender/preview.cpp b/src/librender/preview.cpp index c47076c7..99ac72f3 100644 --- a/src/librender/preview.cpp +++ b/src/librender/preview.cpp @@ -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 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) diff --git a/src/librender/vpl.cpp b/src/librender/vpl.cpp index a2e90b8e..8513dbc9 100644 --- a/src/librender/vpl.cpp +++ b/src/librender/vpl.cpp @@ -18,11 +18,48 @@ #include #include +#include MTS_NAMESPACE_BEGIN -size_t generateVPLs(const Scene *scene, size_t offset, size_t count, int maxDepth, - std::deque &vpls) { +static StatsCounter prunedVPLs("VPL generator", "Pruned VPLs", EPercentage); + +static void appendVPL(const Scene *scene, Random *random, + VPL &vpl, bool prune, std::deque &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; igetCamera()->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 &vpls) { ref sampler = static_cast (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; diff --git a/src/qtgui/preview.cpp b/src/qtgui/preview.cpp index 024e4ae1..4a9e0507 100644 --- a/src/qtgui/preview.cpp +++ b/src/qtgui/preview.cpp @@ -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(); @@ -57,6 +57,8 @@ PreviewThread::PreviewThread(Device *parentDevice, Renderer *parentRenderer) m_framebuffer = m_renderer->createGPUTexture("Framebuffer"); for (int i=0; ireset(); } - 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 sched = Scheduler::getInstance(); diff --git a/src/qtgui/preview.h b/src/qtgui/preview.h index 57b08e3e..5ac19808 100644 --- a/src/qtgui/preview.h +++ b/src/qtgui/preview.h @@ -84,11 +84,11 @@ private: ref m_framebuffer; ref m_accumProgram; ref m_previewProc; + ref m_random; int m_accumProgramParam_source1; int m_accumProgramParam_source2; const GPUTexture *m_accumBuffer; ref m_mutex; - ref m_random; ref m_queueCV; ref m_timer; ref m_started; @@ -101,6 +101,7 @@ private: std::vector m_releaseList; Point m_camPos; Transform m_camViewTransform; + Float m_backgroundScaleFactor; bool m_quit, m_sleep, m_motion, m_useSync; bool m_refreshScene; }; diff --git a/src/qtgui/preview_proc.cpp b/src/qtgui/preview_proc.cpp index 92aaca5b..502fe29d 100644 --- a/src/qtgui/preview_proc.cpp +++ b/src/qtgui/preview_proc.cpp @@ -19,8 +19,8 @@ #include #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 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); } diff --git a/src/qtgui/preview_proc.h b/src/qtgui/preview_proc.h index 818fcf38..dd05d4ff 100644 --- a/src/qtgui/preview_proc.h +++ b/src/qtgui/preview_proc.h @@ -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 */