MLT documentation improvements

metadata
Wenzel Jakob 2012-09-30 00:34:29 -04:00
parent 8f25f1ee46
commit d83a7bae52
17 changed files with 1827 additions and 185 deletions

View File

@ -2,6 +2,16 @@
to be tested for consistency. This is done
using the testcase 'test_chisquare' -->
<scene version="0.4.0">
<!-- Test the smooth plastic model with preserveColors=false -->
<bsdf type="plastic">
<boolean name="preserveColors" value="false"/>
</bsdf>
<!-- Test the smooth plastic model with preserveColors=true -->
<bsdf type="plastic">
<boolean name="preserveColors" value="true"/>
</bsdf>
<!-- Test the smooth diffuse model -->
<bsdf type="diffuse"/>
@ -118,16 +128,6 @@
<float name="alphaV" value="0.3"/>
</bsdf>
<!-- Test the smooth plastic model with preserveColors=false -->
<bsdf type="plastic">
<boolean name="preserveColors" value="false"/>
</bsdf>
<!-- Test the smooth plastic model with preserveColors=true -->
<bsdf type="plastic">
<boolean name="preserveColors" value="true"/>
</bsdf>
<!-- Test the rough plastic model with the
Beckmann microfacet distribution -->
<bsdf type="roughplastic">

File diff suppressed because one or more lines are too long

View File

@ -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.},

View File

@ -69,6 +69,15 @@ public:
RenderQueue *queue, int sizeFactor, ref<RenderJob> &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_ */

View File

@ -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;

View File

@ -18,7 +18,7 @@
#include <mitsuba/core/statistics.h>
#include <mitsuba/core/sfcurve.h>
#include <mitsuba/bidir/path.h>
#include <mitsuba/bidir/util.h>
#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);

View File

@ -22,14 +22,54 @@
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, the implementation renders direct illumination component
* separately using the \pluginref{direct} plugin, which
* uses low-discrepancy number sequences for superior performance
* (in other words, it is \emph{not} handled by MLT). This
* parameter specifies the number of samples allocated to that method. To
* force MLT to be responsible for the direct illumination
* component as well, set this 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{bidirectional\showbreak Mutation, [lens,caustic,multiChain]\showbreak Perturbation}{\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.
*
* \renderings{
* \vspace{-2mm}
* \includegraphics[width=\textwidth]{images/integrator_mlt_sketch.pdf}\hfill\,
* \vspace{-3mm}
* \caption{The available mutation types}
* }
*/
class MLT : public Integrator {
public:
@ -67,24 +107,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 <tt>-1</tt>, the
@ -111,6 +141,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);
}
@ -171,11 +203,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<FileStream> fs = new FileStream(debugFile, FileStream::ETruncReadWrite);
m_config.importanceMap->write(Bitmap::EOpenEXR, fs);
}
bool nested = m_config.twoStage && m_config.firstStage;
@ -203,12 +230,17 @@ public:
ref<ReplayableSampler> rplSampler = new ReplayableSampler();
ref<PathSampler> 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<PathSeed> pathSeeds;
ref<MLTProcess> 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);

View File

@ -79,7 +79,7 @@ public:
static_cast<Sampler *>(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();

View File

@ -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> sampler = static_cast<Sampler *> (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<Scheduler> sched = Scheduler::getInstance();
/* Generate the global photon map */
ref<GatherPhotonProcess> 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;
};

View File

@ -17,7 +17,6 @@
*/
#include <mitsuba/bidir/util.h>
#include <mitsuba/core/fstream.h>
#include <mitsuba/core/plugin.h>
#include "pssmlt_proc.h"
#include "pssmlt_sampler.h"
@ -202,14 +201,14 @@ 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;
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
@ -226,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 &&
@ -242,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);
}
@ -305,11 +306,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<FileStream> fs = new FileStream(debugFile, FileStream::ETruncReadWrite);
m_config.importanceMap->write(Bitmap::EOpenEXR, fs);
}
bool nested = m_config.twoStage && m_config.firstStage;
@ -320,9 +316,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;

View File

@ -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> 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; mutationCtr<m_config.nMutations && !stop; ++mutationCtr) {
if (wu->getTimeout() > 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; i<pixelCount; ++i)
avgLuminance += accum[i].getLuminance();
if (importanceMap) {
for (size_t i=0; i<pixelCount; ++i)
avgLuminance += accum[i].getLuminance() * importanceMap[i];
} else {
for (size_t i=0; i<pixelCount; ++i)
avgLuminance += accum[i].getLuminance();
}
avgLuminance /= (Float) pixelCount;
Float luminanceFactor = m_config.luminance / avgLuminance;

View File

@ -18,6 +18,7 @@
#include <mitsuba/bidir/pathsampler.h>
#include <mitsuba/bidir/rsampler.h>
#include <mitsuba/bidir/util.h>
#include <mitsuba/core/bitmap.h>
#include <mitsuba/core/plugin.h>
#include <boost/bind.hpp>
@ -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
@ -459,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
@ -473,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 */

View File

@ -240,6 +240,7 @@ bool PathVertex::sampleNext(const Scene *scene, Sampler *sampler,
return false;
ray.time = mRec.time;
ray.mint = 0;
ray.setOrigin(mRec.p);
ray.setDirection(pRec.wo);
measure = ESolidAngle;

View File

@ -673,8 +673,13 @@ bool Scene::rayIntersectAll(const Ray &ray) const {
if (rayIntersect(ray))
return true;
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; i<m_specialShapes.size(); ++i) {
if (m_specialShapes[i]->rayIntersect(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; i<m_specialShapes.size(); ++i) {
const Shape *shape = m_specialShapes[i].get();
if (shape->rayIntersect(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; i<m_specialShapes.size(); ++i) {
const Shape *shape = m_specialShapes[i].get();
if (shape->rayIntersect(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);

View File

@ -179,6 +179,7 @@ struct SceneContext {
/* Rendering/Preview-related */
RenderJob *renderJob;
bool cancelled;
bool wasRendering;
float progress;
QString eta, progressName;
ref<Bitmap> 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

View File

@ -775,8 +775,8 @@ void MainWindow::updateUI() {
if (isRendering) {
if (!m_progress->isVisible()) {
QGridLayout *centralLayout = static_cast<QGridLayout *>(centralWidget()->layout());
centralLayout->addWidget(m_progressWidget, 3, 0, 1, 3);
static_cast<QGridLayout *>(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<Sensor> &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();

View File

@ -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.
</param>
<param name="separateDirect" readableName="Separate direct illumination" type="boolean" default="true">
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 <tt>false</tt>.
</param>
<param name="directSamples" readableName="Direct samples" type="integer" default="16">
When <tt>separateDirect</tt> is set to <tt>true</tt>, this parameter can
be used to specify the samples per pixel used to render the
@ -489,45 +479,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.
</param>
<param name="firstStageSizeReduction" readableName="First-stage size reduction" type="integer" default="16" importance="1">
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.
</param>
<param name="stratifyLargeMutations" readableName="Stratify mutations" type="boolean" default="true">
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
<tt>true</tt>.
</param>
<param name="sampleDirect" readableName="Use direct illumination sampling" type="boolean" default="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 <tt>separateDirect</tt>
parameter.
</param>
<param name="workUnits" readableName="Parallel work units" type="integer" default="-1">
Specifies the number of parallel work units required for
multithreaded and network rendering. When set to <tt>-1</tt>, 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.
</param>
<param name="kelemenWeights" readableName="Kelemen-style weights" type="boolean" default="true">
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.
</param>
<param name="luminanceSamples" readableName="Luminance samples" type="integer" default="100000" importance="1">
Number of samples used to estimate the total luminance
received by the camera's sensor.
@ -540,9 +491,6 @@
Specifies the minimum path depth, after which the implementation will start to use the
"russian roulette" path termination criterion (set to <tt>-1</tt> to disable).
</param>
<param name="timeout" readableName="Timeout" type="integer" default="0" importance="1">
If set to a nonzero value, the rendering process will automatically be stopped after this many seconds.
</param>
</plugin>
<plugin type="integrator" name="mlt" readableName="Path Space MLT" show="true"
@ -559,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.
</param>
<param name="separateDirect" readableName="Separate direct illumination" type="boolean" default="true">
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 <tt>false</tt>.
</param>
<param name="directSamples" readableName="Direct samples" type="integer" default="16">
When <tt>separateDirect</tt> is set to <tt>true</tt>, this parameter can
be used to specify the samples per pixel used to render the
@ -587,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.
</param>
<param name="firstStageSizeReduction" readableName="First-stage size reduction" type="integer" default="16" importance="1">
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.
</param>
<param name="workUnits" readableName="Parallel work units" type="integer" default="-1">
Specifies the number of parallel work units required for
multithreaded and network rendering. When set to <tt>-1</tt>, 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.
</param>
<param name="luminanceSamples" readableName="Luminance samples" type="integer" default="100000" importance="1">
Number of samples used to estimate the total luminance
received by the camera's sensor.
@ -658,16 +577,6 @@
<param name="chainLength" readableName="Mutations per chain" type="integer" default="100">
Specifies the number of mutations to be performed in each Markov Chain
</param>
<param name="separateDirect" readableName="Separate direct illumination" type="boolean" default="true">
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 <tt>false</tt>.
</param>
<param name="directSamples" readableName="Direct samples" type="integer" default="16">
When <tt>separateDirect</tt> is set to <tt>true</tt>, this parameter can
be used to specify the samples per pixel used to render the