MLT documentation improvements
parent
8f25f1ee46
commit
d83a7bae52
|
@ -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
11
doc/main.bib
11
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.},
|
||||
|
|
|
@ -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_ */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue