From 18cade3daec71417c00c7185b9300b0fd42db51e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 28 Sep 2012 22:50:11 -0400 Subject: [PATCH 1/8] added Lipo script for OSX --- data/darwin/lipo.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100755 data/darwin/lipo.sh diff --git a/data/darwin/lipo.sh b/data/darwin/lipo.sh new file mode 100755 index 00000000..41954c48 --- /dev/null +++ b/data/darwin/lipo.sh @@ -0,0 +1,19 @@ +rm -Rf Mitsuba.app +cp -R Mitsuba64.app/ Mitsuba.app +for i in Mitsuba32.app/plugins/*; do + base=$(basename $i) + echo "Running lipo on $base" + lipo -create Mitsuba32.app/plugins/$base Mitsuba64.app/plugins/$base -output Mitsuba.app/plugins/$base +done +for i in Mitsuba32.app/Contents/MacOS/*; do + base=$(basename $i) + echo "Running lipo on $base" + lipo -create Mitsuba32.app/Contents/MacOS/$base Mitsuba64.app/Contents/MacOS/$base -output Mitsuba.app/Contents/MacOS/$base +done +for i in Mitsuba32.app/Contents/Frameworks/libmitsuba*; do + base=$(basename $i) + echo "Running lipo on $base" + lipo -create Mitsuba32.app/Contents/Frameworks/$base Mitsuba64.app/Contents/Frameworks/$base -output Mitsuba.app/Contents/Frameworks/$base +done +lipo -create Mitsuba32.app/python/2.6/mitsuba.so Mitsuba64.app/python/2.6/mitsuba.so -output Mitsuba.app/python/2.6/mitsuba.so +lipo -create Mitsuba32.app/python/2.7/mitsuba.so Mitsuba64.app/python/2.7/mitsuba.so -output Mitsuba.app/python/2.7/mitsuba.so From 741aee57b21fc39d2966603bfc3a98b652d646a4 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Fri, 28 Sep 2012 23:14:11 -0400 Subject: [PATCH 2/8] cleaned up some integrator GUI options, fixed sppm so that it works with maxDepth==-1 --- src/integrators/photonmapper/sppm.cpp | 17 ++++++----- src/integrators/pssmlt/pssmlt.cpp | 2 +- src/mtsgui/resources/docs.xml | 42 --------------------------- 3 files changed, 11 insertions(+), 50 deletions(-) diff --git a/src/integrators/photonmapper/sppm.cpp b/src/integrators/photonmapper/sppm.cpp index 2048f312..b292524f 100644 --- a/src/integrators/photonmapper/sppm.cpp +++ b/src/integrators/photonmapper/sppm.cpp @@ -102,7 +102,7 @@ public: /* Indicates if the gathering steps should be canceled if not enough photons are generated. */ m_autoCancelGathering = props.getBoolean("autoCancelGathering", true); m_mutex = new Mutex(); - if (m_maxDepth <= 1) + if (m_maxDepth <= 1 && m_maxDepth != -1) Log(EError, "Maximum depth must be set to \"2\" or higher!"); } @@ -150,6 +150,7 @@ public: m_gatherBlocks.clear(); m_running = true; m_totalEmitted = 0; + m_totalPhotons = 0; ref sampler = static_cast (PluginManager::getInstance()-> createObject(MTS_CLASS(Sampler), Properties("independent"))); @@ -258,7 +259,7 @@ public: if (gatherPoint.its.isEmitter()) gatherPoint.emission += weight * gatherPoint.its.Le(-ray.d); - if (depth >= m_maxDepth) { + if (depth >= m_maxDepth && m_maxDepth != -1) { gatherPoint.depth = -1; break; } @@ -270,7 +271,7 @@ public: a glossy material */ if ((bsdf->getType() & BSDF::EAll) == BSDF::EDiffuseReflection || (bsdf->getType() & BSDF::EAll) == BSDF::EDiffuseTransmission || - depth + 1 > m_maxDepth) { + (depth + 1 > m_maxDepth && m_maxDepth != -1)) { gatherPoint.weight = weight; gatherPoint.depth = depth; break; @@ -302,13 +303,14 @@ public: void photonMapPass(int it, RenderQueue *queue, const RenderJob *job, Film *film, int sceneResID, int sensorResID, int samplerResID) { - Log(EInfo, "Performing a photon mapping pass %i", it); + Log(EInfo, "Performing a photon mapping pass %i (" SIZE_T_FMT " photons so far)", + it, m_totalPhotons); ref sched = Scheduler::getInstance(); /* Generate the global photon map */ ref proc = new GatherPhotonProcess( GatherPhotonProcess::EAllSurfacePhotons, m_photonCount, - m_granularity, m_maxDepth-1, m_rrDepth, true, + m_granularity, m_maxDepth == -1 ? -1 : m_maxDepth-1, m_rrDepth, true, m_autoCancelGathering, job); proc->bindResource("scene", sceneResID); @@ -325,6 +327,7 @@ public: Log(EInfo, "Gathering .."); m_totalEmitted += proc->getShotParticles(); + m_totalPhotons += photonMap->size(); film->clear(); #if defined(MTS_OPENMP) #pragma omp parallel for schedule(dynamic) @@ -340,7 +343,7 @@ public: if (gp.depth != -1) { M = (Float) photonMap->estimateRadianceRaw( - gp.its, gp.radius, flux, m_maxDepth-gp.depth); + gp.its, gp.radius, flux, m_maxDepth == -1 ? INT_MAX : m_maxDepth-gp.depth); } else { M = 0; flux = Spectrum(0.0f); @@ -391,7 +394,7 @@ private: Float m_initialRadius, m_alpha; int m_photonCount, m_granularity; int m_maxDepth, m_rrDepth; - size_t m_totalEmitted; + size_t m_totalEmitted, m_totalPhotons; bool m_running; bool m_autoCancelGathering; }; diff --git a/src/integrators/pssmlt/pssmlt.cpp b/src/integrators/pssmlt/pssmlt.cpp index c65ecfc6..2df150e7 100644 --- a/src/integrators/pssmlt/pssmlt.cpp +++ b/src/integrators/pssmlt/pssmlt.cpp @@ -209,7 +209,7 @@ public: direct illumination component will be hidden, which is useful for analyzing the component rendered by MLT. */ m_config.directSamples = props.getInteger("directSamples", 16); - m_config.separateDirect = m_config.directSamples < 0; + m_config.separateDirect = m_config.directSamples >= 0; /* Should the multiple importance sampling-based weight computation by Kelemen et al. be used? Otherwise, the implementation falls back diff --git a/src/mtsgui/resources/docs.xml b/src/mtsgui/resources/docs.xml index 14fec234..499007c9 100644 --- a/src/mtsgui/resources/docs.xml +++ b/src/mtsgui/resources/docs.xml @@ -489,45 +489,6 @@ more uniform over time image -- specifically, since MLT tends to get stuck in very bright regions at the cost of the remainder of the image. - - When running two-stage MLT, this parameter influences the size - of the downsampled image created in the first pass (i.e. setting this - to 16 means that the horizontal/vertical resolution will be 16 times - lower). When the two-stage process introduces noisy halos around - very bright image regions, it might might be good to reduce this - parameter to 4 or even 1. Generally though, it should be safe to leave - it unchanged. - - - Should the implementation try to achieve a better spread of the - image plane pixel positions associated with paths? This is done by passing - stratified positions to large mutations. The default is - true. - - - Should an optimized direct illumination sampling strategy be used - for s=1 paths? (as opposed to plain emission sampling). Usually - a good idea. Note that this setting only applies when the - bidirectional path tracer is used internally. The optimization - affects all paths, not just the ones contributing direct illumination, - hence it is completely independent of the separateDirect - parameter. - - - Specifies the number of parallel work units required for - multithreaded and network rendering. When set to -1, the - amount will default to four times the number of cores. Note that - every additional work unit entails a significant amount of - communication overhead (a full-sized floating put image must be - transmitted), hence it is important to set this value as low as - possible, while ensuring that there are enough units to keep all - workers busy. - - - Should the multiple importance sampling-based weight computation by - Kelemen et al. be used? Otherwise, the implementation falls back - to the 'use of expectations' technique from Veach-style MLT. - Number of samples used to estimate the total luminance received by the camera's sensor. @@ -540,9 +501,6 @@ Specifies the minimum path depth, after which the implementation will start to use the "russian roulette" path termination criterion (set to -1 to disable). - - If set to a nonzero value, the rendering process will automatically be stopped after this many seconds. - Date: Sat, 29 Sep 2012 00:37:11 -0400 Subject: [PATCH 3/8] fixed a ray epsilon issue in the libbidir-specific intersection code --- include/mitsuba/render/mipmap.h | 11 +++++++---- src/integrators/pssmlt/pssmlt.cpp | 6 ------ src/integrators/pssmlt/pssmlt_proc.cpp | 18 ++++++++++++------ src/libbidir/vertex.cpp | 1 + src/librender/scene.cpp | 21 ++++++++++++++++++--- 5 files changed, 38 insertions(+), 19 deletions(-) diff --git a/include/mitsuba/render/mipmap.h b/include/mitsuba/render/mipmap.h index 2246acc8..0820cc69 100644 --- a/include/mitsuba/render/mipmap.h +++ b/include/mitsuba/render/mipmap.h @@ -755,10 +755,13 @@ protected: for (int ut = u0; ut <= u1; ++ut) { if (q < (Float) MTS_MIPMAP_LUT_SIZE) { - const Float weight = m_weightLut[(int) q]; - result += evalTexel(level, ut, vt) * weight; - denominator += weight; - ++nSamples; + uint32_t qi = (uint32_t) q; + if (qi < MTS_MIPMAP_LUT_SIZE) { + const Float weight = m_weightLut[(int) q]; + result += evalTexel(level, ut, vt) * weight; + denominator += weight; + ++nSamples; + } } q += dq; diff --git a/src/integrators/pssmlt/pssmlt.cpp b/src/integrators/pssmlt/pssmlt.cpp index 2df150e7..1f0d611c 100644 --- a/src/integrators/pssmlt/pssmlt.cpp +++ b/src/integrators/pssmlt/pssmlt.cpp @@ -17,7 +17,6 @@ */ #include -#include #include #include "pssmlt_proc.h" #include "pssmlt_sampler.h" @@ -305,11 +304,6 @@ public: return false; } Log(EInfo, "First MLT stage took %i ms", timer->getMilliseconds()); - - std::string debugFile = "mlt_stage1.exr"; - Log(EInfo, "Writing upsampled luminances to \"%s\"", debugFile.c_str()); - ref fs = new FileStream(debugFile, FileStream::ETruncReadWrite); - m_config.importanceMap->write(Bitmap::EOpenEXR, fs); } bool nested = m_config.twoStage && m_config.firstStage; diff --git a/src/integrators/pssmlt/pssmlt_proc.cpp b/src/integrators/pssmlt/pssmlt_proc.cpp index 32e6a178..a58c57a8 100644 --- a/src/integrators/pssmlt/pssmlt_proc.cpp +++ b/src/integrators/pssmlt/pssmlt_proc.cpp @@ -104,7 +104,6 @@ public: m_rplSampler->setSampleIndex(seed.sampleIndex); m_pathSampler->sampleSplats(Point2i(-1), *current); - current->normalize(m_config.importanceMap); result->clear(); ref random = m_origSampler->getRandom(); @@ -132,8 +131,10 @@ public: /* MLT main loop */ Float cumulativeWeight = 0; + current->normalize(m_config.importanceMap); for (uint64_t mutationCtr=0; mutationCtrgetTimeout() > 0 && (mutationCtr % 8192) == 0 && (int) timer->getMilliseconds() > wu->getTimeout()) + if (wu->getTimeout() > 0 && (mutationCtr % 8192) == 0 + && (int) timer->getMilliseconds() > wu->getTimeout()) break; bool largeStep = random->nextFloat() < m_config.pLarge; @@ -156,8 +157,8 @@ public: Float currentWeight, proposedWeight; if (a > 0) { - if (m_config.kelemenStyleWeights) { - /* Kelemen-style MLT weights */ + if (m_config.kelemenStyleWeights && !m_config.importanceMap) { + /* Kelemen-style MLT weights (these don't work for 2-stage MLT) */ currentWeight = (1 - a) * current->luminance / (current->luminance/m_config.luminance + m_config.pLarge); proposedWeight = (a + (largeStep ? 1 : 0)) * proposed->luminance @@ -282,8 +283,13 @@ void PSSMLTProcess::develop() { /* Compute the luminance correction factor */ Float avgLuminance = 0; - for (size_t i=0; irayIntersect(ray, ray.mint, ray.maxt)) + if (m_specialShapes[i]->rayIntersect(ray, mint, ray.maxt)) return true; } @@ -690,10 +695,16 @@ bool Scene::rayIntersectAll(const Ray &ray, Float &t, uint8_t buffer[MTS_KD_INTERSECTION_TEMP]; Float tempT, maxt = result ? t : ray.maxt; + Float mint = ray.mint; + if (mint == Epsilon) + mint *= std::max(std::max(std::max(std::abs(ray.o.x), + std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon); + + for (size_t i=0; irayIntersect(ray, ray.mint, maxt, tempT, buffer)) { + if (shape->rayIntersect(ray, mint, maxt, tempT, buffer)) { /// Uh oh... -- much unnecessary work is done here Intersection its; its.t = tempT; @@ -716,12 +727,16 @@ bool Scene::rayIntersectAll(const Ray &ray, Intersection &its) const { uint8_t buffer[MTS_KD_INTERSECTION_TEMP]; Float maxt = result ? its.t : ray.maxt; + Float mint = ray.mint; + if (mint == Epsilon) + mint *= std::max(std::max(std::max(std::abs(ray.o.x), + std::abs(ray.o.y)), std::abs(ray.o.z)), Epsilon); Float tempT; for (size_t i=0; irayIntersect(ray, ray.mint, maxt, tempT, buffer)) { + if (shape->rayIntersect(ray, mint, maxt, tempT, buffer)) { its.time = ray.time; its.t = tempT; shape->fillIntersectionRecord(ray, buffer, its); From 333f4d0b2834aecb05b08d594799fbfb53f2a978 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 29 Sep 2012 18:03:04 -0400 Subject: [PATCH 4/8] fixed a measure-related issue in the BDPT code --- data/tests/test_bsdf.xml | 20 ++++++++++---------- include/mitsuba/bidir/util.h | 9 +++++++++ src/integrators/bdpt/bdpt_proc.cpp | 14 ++++++++++---- src/libbidir/pathsampler.cpp | 19 +++++++++++++++++-- 4 files changed, 46 insertions(+), 16 deletions(-) diff --git a/data/tests/test_bsdf.xml b/data/tests/test_bsdf.xml index 5b8b4ba4..ceaaee9a 100644 --- a/data/tests/test_bsdf.xml +++ b/data/tests/test_bsdf.xml @@ -2,6 +2,16 @@ to be tested for consistency. This is done using the testcase 'test_chisquare' --> + + + + + + + + + + @@ -118,16 +128,6 @@ - - - - - - - - - - diff --git a/include/mitsuba/bidir/util.h b/include/mitsuba/bidir/util.h index a1311b63..ec3998c6 100644 --- a/include/mitsuba/bidir/util.h +++ b/include/mitsuba/bidir/util.h @@ -69,6 +69,15 @@ public: RenderQueue *queue, int sizeFactor, ref &nestedJob); }; +/// Restores the measure of a path vertex after going out of scope +struct RestoreMeasureHelper { + RestoreMeasureHelper(PathVertex *vertex) + : vertex(vertex), measure(vertex->measure) { } + ~RestoreMeasureHelper() { vertex->measure = measure; } + PathVertex *vertex; + uint8_t measure; +}; + MTS_NAMESPACE_END #endif /* __MITSUBA_BIDIR_UTIL_H_ */ diff --git a/src/integrators/bdpt/bdpt_proc.cpp b/src/integrators/bdpt/bdpt_proc.cpp index 817259f2..b17c9822 100644 --- a/src/integrators/bdpt/bdpt_proc.cpp +++ b/src/integrators/bdpt/bdpt_proc.cpp @@ -18,7 +18,7 @@ #include #include -#include +#include #include "bdpt_proc.h" MTS_NAMESPACE_BEGIN @@ -175,6 +175,8 @@ public: *vsEdge = emitterSubpath.edgeOrNull(s-1), *vtEdge = sensorSubpath.edgeOrNull(t-1); + RestoreMeasureHelper rmh0(vs), rmh1(vt); + /* Will be set to true if direct sampling was used */ bool sampleDirect = false; @@ -215,16 +217,14 @@ public: if (s == 1) { if (vt->isDegenerate()) continue; - /* Generate a position on an emitter using direct sampling */ value = radianceWeights[t] * vt->sampleDirect(scene, m_sampler, &tempEndpoint, &tempEdge, &tempSample, EImportance); - if (value.isZero()) continue; vs = &tempSample; vsPred = &tempEndpoint; vsEdge = &tempEdge; value *= vt->eval(scene, vtPred, vs, ERadiance); - + vt->measure = EArea; } else { if (vs->isDegenerate()) continue; @@ -235,6 +235,7 @@ public: continue; vt = &tempSample; vtPred = &tempEndpoint; vtEdge = &tempEdge; value *= vs->eval(scene, vsPred, vt, EImportance); + vs->measure = EArea; } sampleDirect = true; @@ -246,6 +247,10 @@ public: value = importanceWeights[s] * radianceWeights[t] * vs->eval(scene, vsPred, vt, EImportance) * vt->eval(scene, vtPred, vs, ERadiance); + + /* Temporarily force vertex measure to EArea. Needed to + handle BSDFs with diffuse + specular components */ + vs->measure = vt->measure = EArea; } /* Attempt to connect the two endpoints, which could result in @@ -273,6 +278,7 @@ public: emitterSubpath.swapEndpoints(vsPred, vsEdge, vs); } + /* Compute the multiple importance sampling weight */ Float miWeight = Path::miWeight(scene, emitterSubpath, &connectionEdge, sensorSubpath, s, t, m_config.sampleDirect, m_config.lightImage); diff --git a/src/libbidir/pathsampler.cpp b/src/libbidir/pathsampler.cpp index 2aab66d7..517febfc 100644 --- a/src/libbidir/pathsampler.cpp +++ b/src/libbidir/pathsampler.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include #include @@ -129,6 +130,8 @@ void PathSampler::sampleSplats(const Point2i &offset, SplatList &list) { *vsEdge = m_emitterSubpath.edgeOrNull(s-1), *vtEdge = m_sensorSubpath.edgeOrNull(t-1); + RestoreMeasureHelper rmh0(vs), rmh1(vt); + /* Will be set to true if direct sampling was used */ bool sampleDirect = false; @@ -178,7 +181,7 @@ void PathSampler::sampleSplats(const Point2i &offset, SplatList &list) { continue; vs = &tempSample; vsPred = &tempEndpoint; vsEdge = &tempEdge; value *= vt->eval(m_scene, vtPred, vs, ERadiance); - + vt->measure = EArea; } else { if (vs->isDegenerate()) continue; @@ -189,6 +192,7 @@ void PathSampler::sampleSplats(const Point2i &offset, SplatList &list) { continue; vt = &tempSample; vtPred = &tempEndpoint; vtEdge = &tempEdge; value *= vs->eval(m_scene, vsPred, vt, EImportance); + vs->measure = EArea; } sampleDirect = true; @@ -200,8 +204,11 @@ void PathSampler::sampleSplats(const Point2i &offset, SplatList &list) { value = importanceWeights[s] * radianceWeights[t] * vs->eval(m_scene, vsPred, vt, EImportance) * vt->eval(m_scene, vtPred, vs, ERadiance); - } + /* Temporarily force vertex measure to EArea. Needed to + handle BSDFs with diffuse + specular components */ + vs->measure = vt->measure = EArea; + } /* Attempt to connect the two endpoints, which could result in the creation of additional vertices (index-matched boundaries etc.) */ @@ -366,6 +373,8 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { PathEdge *vsEdge = m_emitterSubpath.edgeOrNull(s-1), *vtEdge = m_sensorSubpath.edgeOrNull(t-1); + + RestoreMeasureHelper rmh0(vs), rmh1(vt); /* Will be set to true if direct sampling was used */ bool sampleDirect = false; @@ -420,6 +429,7 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { vs = &tempSample; vsPred = &tempEndpoint; vsEdge = &tempEdge; value *= vt->eval(m_scene, vtPred, vs, ERadiance); vsMeasure = vs->getAbstractEmitter()->needsDirectionSample() ? EArea : EDiscrete; + vt->measure = EArea; } else { if (vs->isDegenerate()) continue; @@ -431,6 +441,7 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { vt = &tempSample; vtPred = &tempEndpoint; vtEdge = &tempEdge; value *= vs->eval(m_scene, vsPred, vt, EImportance); vtMeasure = vt->getAbstractEmitter()->needsDirectionSample() ? EArea : EDiscrete; + vs->measure = EArea; } sampleDirect = true; @@ -442,6 +453,10 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { value = importanceWeights[s] * radianceWeights[t] * vs->eval(m_scene, vsPred, vt, EImportance) * vt->eval(m_scene, vtPred, vs, ERadiance); + + /* Temporarily force vertex measure to EArea. Needed to + handle BSDFs with diffuse + specular components */ + vs->measure = vt->measure = EArea; } /* Attempt to connect the two endpoints, which could result in From 1af8453a584114cc9980c9995406910b90947349 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 29 Sep 2012 18:35:36 -0400 Subject: [PATCH 5/8] parameter documentation updates --- src/integrators/mlt/mlt.cpp | 20 ++++--------- src/integrators/pssmlt/pssmlt.cpp | 13 ++++---- src/mtsgui/resources/docs.xml | 49 ------------------------------- 3 files changed, 13 insertions(+), 69 deletions(-) diff --git a/src/integrators/mlt/mlt.cpp b/src/integrators/mlt/mlt.cpp index 1a7e74b3..4b557164 100644 --- a/src/integrators/mlt/mlt.cpp +++ b/src/integrators/mlt/mlt.cpp @@ -67,24 +67,14 @@ public: received by the scene's sensor */ m_config.luminanceSamples = props.getInteger("luminanceSamples", 100000); - /* Should direct illumination be handled separately? (i.e. not - using MLT) This is usually the right way to go, since direct - illumination is easily handled using more optimized rendering - techniques that can make use of low-discrepancy point sets. - This in turn lets MLT focus on the more difficult parts of the - light transport. On the other hand, some scenes use very - hard to find paths even for direct illumination, in which case - it may make more sense to set this property to 'false' */ - m_config.separateDirect = props.getBoolean("separateDirect", - true); - - /* When 'separateDirect' is set to 'true', this parameter can - be used to specify the samples per pixel used to render the - direct component. Should be a power of two (otherwise, it will + /* This parameter can be used to specify the samples per pixel used to + render the direct component. Should be a power of two (otherwise, it will be rounded to the next one). When set to zero or less, the direct illumination component will be hidden, which is useful - for analyzing the component rendered by MLT. */ + for analyzing the component rendered by MLT. When set to -1, + MLT will handle direct illumination as well */ m_config.directSamples = props.getInteger("directSamples", 16); + m_config.separateDirect = m_config.directSamples >= 0; /* Specifies the number of parallel work units required for multithreaded and network rendering. When set to -1, the diff --git a/src/integrators/pssmlt/pssmlt.cpp b/src/integrators/pssmlt/pssmlt.cpp index 1f0d611c..da07747d 100644 --- a/src/integrators/pssmlt/pssmlt.cpp +++ b/src/integrators/pssmlt/pssmlt.cpp @@ -201,12 +201,12 @@ public: MLT variant. The default is 0.3. */ m_config.pLarge = props.getFloat("pLarge", 0.3f); - /* When 'separateDirect' is set to 'true', this parameter can - be used to specify the samples per pixel used to render the - direct component. Should be a power of two (otherwise, it will + /* This parameter can be used to specify the samples per pixel used to + render the direct component. Should be a power of two (otherwise, it will be rounded to the next one). When set to zero or less, the direct illumination component will be hidden, which is useful - for analyzing the component rendered by MLT. */ + for analyzing the component rendered by MLT. When set to -1, + PSSMLT will handle direct illumination as well */ m_config.directSamples = props.getInteger("directSamples", 16); m_config.separateDirect = m_config.directSamples >= 0; @@ -314,9 +314,12 @@ public: nested ? "nested " : "", cropSize.x, cropSize.y, nCores, nCores == 1 ? "core" : "cores", sampleCount); + size_t desiredMutationsPerWorkUnit = + m_config.technique == PathSampler::EBidirectional ? 100000 : 200000; + if (m_config.workUnits <= 0) m_config.workUnits = (size_t) std::ceil((cropSize.x * cropSize.y - * sampleCount) / 200000.0f); + * sampleCount) / (Float) desiredMutationsPerWorkUnit); m_config.nMutations = (cropSize.x * cropSize.y * sampleCount) / m_config.workUnits; diff --git a/src/mtsgui/resources/docs.xml b/src/mtsgui/resources/docs.xml index 499007c9..bba5963a 100644 --- a/src/mtsgui/resources/docs.xml +++ b/src/mtsgui/resources/docs.xml @@ -454,16 +454,6 @@ corresponds to ∞). A value of 1 will only render directly visible light sources. 2 will lead to single-bounce (direct-only) illumination, and so on. - - Should direct illumination be handled separately? (i.e. not - using MLT) This is usually the right way to go, since direct - illumination is easily handled using more optimized rendering - techniques that can make use of low-discrepancy point sets. - This in turn lets MLT focus on the more difficult parts of the - light transport. On the other hand, some scenes use very - hard to find paths even for direct illumination, in which case - it may make more sense to set this property to false. - When separateDirect is set to true, this parameter can be used to specify the samples per pixel used to render the @@ -517,16 +507,6 @@ corresponds to ∞). A value of 1 will only render directly visible light sources. 2 will lead to single-bounce (direct-only) illumination, and so on. - - Should direct illumination be handled separately? (i.e. not - using MLT) This is usually the right way to go, since direct - illumination is easily handled using more optimized rendering - techniques that can make use of low-discrepancy point sets. - This in turn lets MLT focus on the more difficult parts of the - light transport. On the other hand, some scenes use very - hard to find paths even for direct illumination, in which case - it may make more sense to set this property to false. - When separateDirect is set to true, this parameter can be used to specify the samples per pixel used to render the @@ -545,25 +525,6 @@ more uniform over time image -- specifically, since MLT tends to get stuck in very bright regions at the cost of the remainder of the image. - - When running two-stage MLT, this parameter influences the size - of the downsampled image created in the first pass (i.e. setting this - to 16 means that the horizontal/vertical resolution will be 16 times - lower). When the two-stage process introduces noisy halos around - very bright image regions, it might might be good to reduce this - parameter to 4 or even 1. Generally though, it should be safe to leave - it unchanged. - - - Specifies the number of parallel work units required for - multithreaded and network rendering. When set to -1, the - amount will default to four times the number of cores. Note that - every additional work unit entails a significant amount of - communication overhead (a full-sized floating put image must be - transmitted), hence it is important to set this value as low as - possible, while ensuring that there are enough units to keep all - workers busy. - Number of samples used to estimate the total luminance received by the camera's sensor. @@ -616,16 +577,6 @@ Specifies the number of mutations to be performed in each Markov Chain - - Should direct illumination be handled separately? (i.e. not - using ERPT) This is usually the right way to go, since direct - illumination is easily handled using more optimized rendering - techniques that can make use of low-discrepancy point sets. - This in turn lets ERPT focus on the more difficult parts of the - light transport. On the other hand, some scenes use very - hard to find paths even for direct illumination, in which case - it may make more sense to set this property to false. - When separateDirect is set to true, this parameter can be used to specify the samples per pixel used to render the From 4689d4fb92a7c5a59d610c18ecc3c3f8998d648e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 29 Sep 2012 19:51:58 -0400 Subject: [PATCH 6/8] pass direct illumination sampler in the MLT seeding process --- src/integrators/mlt/mlt.cpp | 14 +++++++------- src/integrators/mlt/mlt_proc.cpp | 2 +- src/libbidir/pathsampler.cpp | 16 ++++++++-------- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/integrators/mlt/mlt.cpp b/src/integrators/mlt/mlt.cpp index 4b557164..2c7443f0 100644 --- a/src/integrators/mlt/mlt.cpp +++ b/src/integrators/mlt/mlt.cpp @@ -161,11 +161,6 @@ public: return false; } Log(EInfo, "First MLT stage took %i ms", timer->getMilliseconds()); - - std::string debugFile = "mlt_stage1.exr"; - Log(EInfo, "Writing upsampled luminances to \"%s\"", debugFile.c_str()); - ref fs = new FileStream(debugFile, FileStream::ETruncReadWrite); - m_config.importanceMap->write(Bitmap::EOpenEXR, fs); } bool nested = m_config.twoStage && m_config.firstStage; @@ -193,12 +188,17 @@ public: ref rplSampler = new ReplayableSampler(); ref pathSampler = new PathSampler(PathSampler::EBidirectional, scene, - rplSampler, rplSampler, NULL, m_config.maxDepth, 10, + rplSampler, rplSampler, rplSampler, m_config.maxDepth, 10, m_config.separateDirect, true); - + std::vector pathSeeds; ref process = new MLTProcess(job, queue, m_config, directImage, pathSeeds); + + m_config.luminance = pathSampler->generateSeeds(m_config.luminanceSamples, + m_config.workUnits, false, pathSeeds); + + pathSeeds.clear(); m_config.luminance = pathSampler->generateSeeds(m_config.luminanceSamples, m_config.workUnits, true, pathSeeds); diff --git a/src/integrators/mlt/mlt_proc.cpp b/src/integrators/mlt/mlt_proc.cpp index 5df58291..e6e01cfe 100644 --- a/src/integrators/mlt/mlt_proc.cpp +++ b/src/integrators/mlt/mlt_proc.cpp @@ -79,7 +79,7 @@ public: static_cast(getResource("rplSampler"))->clone().get()); m_pathSampler = new PathSampler(PathSampler::EBidirectional, m_scene, - m_rplSampler, m_rplSampler, NULL, m_config.maxDepth, 10, + m_rplSampler, m_rplSampler, m_rplSampler, m_config.maxDepth, 10, m_config.separateDirect, true); m_pool = &m_pathSampler->getMemoryPool(); diff --git a/src/libbidir/pathsampler.cpp b/src/libbidir/pathsampler.cpp index 517febfc..a7fee312 100644 --- a/src/libbidir/pathsampler.cpp +++ b/src/libbidir/pathsampler.cpp @@ -474,6 +474,14 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { PathEdge connectionEdge; m_connectionSubpath.collapseTo(connectionEdge); + /* Account for the terms of the measurement contribution + function that are coupled to the connection edge */ + if (!sampleDirect) + value *= connectionEdge.evalCached(vs, vt, PathEdge::EGeneralizedGeometricTerm); + else + value *= connectionEdge.evalCached(vs, vt, PathEdge::ETransmittance | + (s == 1 ? PathEdge::ECosineRad : PathEdge::ECosineImp)); + if (sampleDirect) { /* A direct sampling strategy was used, which generated two new vertices at one of the path ends. Temporarily @@ -488,14 +496,6 @@ void PathSampler::samplePaths(const Point2i &offset, PathCallback &callback) { value *= Path::miWeight(m_scene, m_emitterSubpath, &connectionEdge, m_sensorSubpath, s, t, m_sampleDirect, m_lightImage); - /* Account for the terms of the measurement contribution - function that are coupled to the connection edge */ - if (!sampleDirect) - value *= connectionEdge.evalCached(vs, vt, PathEdge::EGeneralizedGeometricTerm); - else - value *= connectionEdge.evalCached(vs, vt, PathEdge::ETransmittance | - (s == 1 ? PathEdge::ECosineRad : PathEdge::ECosineImp)); - if (!value.isZero()) { int k = (int) m_connectionSubpath.vertexCount(); /* Construct the full path, make a temporary backup copy of the connection vertices */ From 65c195bffb3c5c98e2450334cca6523cab9a37c3 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sat, 29 Sep 2012 20:15:23 -0400 Subject: [PATCH 7/8] fixed a long-standing annoyance: the GUI window now resizes properly upon tab switches if a render job finished in the meantime --- src/mtsgui/common.h | 4 +++- src/mtsgui/mainwindow.cpp | 19 +++++++++++++------ 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/src/mtsgui/common.h b/src/mtsgui/common.h index d9d772e3..439831e9 100644 --- a/src/mtsgui/common.h +++ b/src/mtsgui/common.h @@ -179,6 +179,7 @@ struct SceneContext { /* Rendering/Preview-related */ RenderJob *renderJob; bool cancelled; + bool wasRendering; float progress; QString eta, progressName; ref framebuffer; @@ -206,7 +207,8 @@ struct SceneContext { PreviewQueueEntry previewBuffer; SceneContext() : scene(NULL), sceneResID(-1), - renderJob(NULL), selectionMode(ENothing), + renderJob(NULL), wasRendering(false), + selectionMode(ENothing), selectedShape(NULL) { } /// Detect the path length diff --git a/src/mtsgui/mainwindow.cpp b/src/mtsgui/mainwindow.cpp index 88ce8b3b..6570d307 100644 --- a/src/mtsgui/mainwindow.cpp +++ b/src/mtsgui/mainwindow.cpp @@ -775,8 +775,8 @@ void MainWindow::updateUI() { if (isRendering) { if (!m_progress->isVisible()) { - QGridLayout *centralLayout = static_cast(centralWidget()->layout()); - centralLayout->addWidget(m_progressWidget, 3, 0, 1, 3); + static_cast(centralWidget()->layout())-> + addWidget(m_progressWidget, 3, 0, 1, 3); m_progressWidget->show(); } m_progress->setValue(context->progress); @@ -831,8 +831,10 @@ void MainWindow::on_tabBar_tabMoved(int from, int to) { } void MainWindow::on_tabBar_currentChanged(int index) { - if (m_lastTab != NULL) + if (m_lastTab != NULL) { m_lastTab->windowSize = size(); + m_lastTab->wasRendering = m_lastTab->renderJob != NULL; + } ui->glView->ignoreResizeEvents(true); if (ui->tabBar->currentIndex() != -1) @@ -862,8 +864,12 @@ void MainWindow::on_tabBar_currentChanged(int index) { ui->menuCamera->clear(); if (index != -1) { - const QSize &windowSize = m_context[index]->windowSize; + SceneContext *context = m_context[index]; + QSize windowSize = context->windowSize; + if (windowSize.isValid()) { + if (context->wasRendering && !context->renderJob) + windowSize -= context->sizeIncrease; #if defined(__LINUX__) int error = (sizeHint()-windowSize).height(); if (error > 0 && error <= 5) @@ -877,9 +883,9 @@ void MainWindow::on_tabBar_currentChanged(int index) { adjustSize(); } - m_lastTab = m_context[index]; + m_lastTab = context; - const Scene *scene = m_context[index]->scene; + const Scene *scene = context->scene; if (scene) { const ref_vector &sensors = scene->getSensors(); for (size_t i = 0; i < sensors.size(); ++i) { @@ -1890,6 +1896,7 @@ SceneContext::SceneContext(SceneContext *ctx) { movementScale = ctx->movementScale; up = ctx->up; renderJob = NULL; + wasRendering = false; cancelled = false; progress = 0.0f; framebuffer = ctx->framebuffer->clone(); From 6c969dce4a1487fee4b44e59514dbdb97a23360e Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Sun, 30 Sep 2012 00:05:20 -0400 Subject: [PATCH 8/8] initial MLT documentation --- doc/main.bib | 11 +++++++ src/integrators/mlt/mlt.cpp | 52 ++++++++++++++++++++++++++----- src/integrators/pssmlt/pssmlt.cpp | 4 ++- 3 files changed, 59 insertions(+), 8 deletions(-) diff --git a/doc/main.bib b/doc/main.bib index 09ed83f4..74dcc1fc 100644 --- a/doc/main.bib +++ b/doc/main.bib @@ -247,6 +247,17 @@ year={1994} } +@inproceedings{Veach1997Metropolis, + author = {Veach, Eric and Guibas, Leonidas J.}, + title = {Metropolis light transport}, + booktitle = {Proceedings of the 24th annual conference on Computer graphics and interactive techniques}, + series = {SIGGRAPH '97}, + year = {1997}, + pages = {65--76}, + publisher = {ACM Press/Addison-Wesley Publishing Co.}, + address = {New York, NY, USA} +} + @article{Grunschloss2010Enumerating, title={Enumerating Quasi-Monte Carlo Point Sequences in Elementary Intervals}, author={Gr{\"u}nschlo{\ss}, L. and Raab, M. and Keller, A.}, diff --git a/src/integrators/mlt/mlt.cpp b/src/integrators/mlt/mlt.cpp index 2c7443f0..2087ca21 100644 --- a/src/integrators/mlt/mlt.cpp +++ b/src/integrators/mlt/mlt.cpp @@ -22,14 +22,50 @@ MTS_NAMESPACE_BEGIN -/** +/*!\plugin{mlt}{Path Space Metropolis Light Transport} * \order{10} - * Veach-style Metropolis Light Transport implementation with support for - * bidirectional mutations, lens perturbations, caustic perturbations and - * multi-chain perturbations. Several optimizations are also implemented, - * namely separate direct illumination, two-stage MLT, - * and importance sampling of mutation strategies. For details, see the - * respective parameter descriptions. + * \parameters{ + * \parameter{directSamples}{\Integer}{ + * By default, this plugin renders the direct illumination component + * separately using an optimized direct illumination sampling strategy + * that uses low-discrepancy number sequences for superior performance + * (in other words, it is \emph{not} rendered by MLT). This + * parameter specifies the number of samples allocated to that method. To + * force PSSMLT to be responsible for the direct illumination + * component as well, set this parameter to \code{-1}. \default{16} + * } + * \parameter{maxDepth}{\Integer}{Specifies the longest path depth + * in the generated output image (where \code{-1} corresponds to $\infty$). + * A value of \code{1} will only render directly visible light sources. + * \code{2} will lead to single-bounce (direct-only) illumination, + * and so on. \default{\code{-1}} + * } + * \parameter{rrDepth}{\Integer}{Specifies the minimum path depth, after + * which the implementation will start to use the ``russian roulette'' + * path termination criterion. \default{\code{5}} + * } + * \parameter{luminanceSamples}{\Integer}{ + * MLT-type algorithms create output images that are only + * \emph{relative}. The algorithm can e.g. determine that a certain pixel + * is approximately twice as bright as another one, but the absolute + * scale is unknown. To recover it, this plugin computes + * the average luminance arriving at the sensor by generating a + * number of samples. \default{\code{100000} samples} + * } + * \parameter{twoStage}{\Boolean}{Use two-stage MLT? + * See below for details. \default{{\footnotesize\code{false}}}} + * \parameter{\footnotesize bidirectional\showbreak Mutation, + * [lens,caustic,multiChain,manifold]Perturbation, + * causticPerturbation, multiChain\showbreak Perturbation, manifoldPerturbation}{\Boolean}{ + * These parameters can be used to choose the mutation strategies that + * should be used. By default, only the bidirectional mutation is + * enabled. + * } + * } + * Metropolis Light Transport is a seminal rendering technique proposed by Veach and + * Guibas \cite{Veach1997Metropolis}, which applies the Metropolis-Hastings + * algorithm to the problem of light transport in the path-space setting. + * */ class MLT : public Integrator { public: @@ -101,6 +137,8 @@ public: /* Selectively enable/disable the manifold perturbation */ m_config.manifoldPerturbation = props.getBoolean("manifoldPerturbation", false); m_config.probFactor = props.getFloat("probFactor", 50); + + /* Stop MLT after X seconds -- useful for equal-time comparisons */ m_config.timeout = props.getInteger("timeout", 0); } diff --git a/src/integrators/pssmlt/pssmlt.cpp b/src/integrators/pssmlt/pssmlt.cpp index da07747d..d25cafdf 100644 --- a/src/integrators/pssmlt/pssmlt.cpp +++ b/src/integrators/pssmlt/pssmlt.cpp @@ -225,7 +225,7 @@ public: m_config.directSampling = props.getBoolean( "directSampling", true); - /* Recommended mutation sizes in the primary sample space */ + /* Recommended mutation sizes in primary sample space */ m_config.mutationSizeLow = props.getFloat("mutationSizeLow", 1.0f/1024.0f); m_config.mutationSizeHigh = props.getFloat("mutationSizeHigh", 1.0f/64.0f); Assert(m_config.mutationSizeLow > 0 && m_config.mutationSizeHigh > 0 && @@ -241,6 +241,8 @@ public: possible, while ensuring that there are enough units to keep all workers busy. */ m_config.workUnits = props.getInteger("workUnits", -1); + + /* Stop MLT after X seconds -- useful for equal-time comparisons */ m_config.timeout = props.getInteger("timeout", 0); }