added an optional feature to hide directly visible light sources -- this is convenient for making paper figures etc. where one don't want the environment map to 'bleed' into the foreground objects (see the committed images for an example)

metadata
Wenzel Jakob 2013-01-24 23:52:10 -05:00
parent 947ac53df5
commit 97f0a28ffb
11 changed files with 134 additions and 36 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

View File

@ -112,4 +112,25 @@ Some of the rendering techniques in Mitsuba are \emph{progressive}.
What this means is that they display a rough preview, which improves over time.
Leaving them running indefinitely will continually reduce noise (in unbiased algorithms
such as Metropolis Light Transport) or noise and bias (in biased
rendering techniques such as Progressive Photon Mapping).
endering techniques such as Progressive Photon Mapping).
\newpage
\subsubsection*{Hiding directly visible emitters}
\label{sec:hideemitters}
Several rendering algorithms in Mitsuba have a feature to hide directly
visible light sources (e.g. environment maps or area lights). While not
particularly realistic, this feature is often convenient to remove a background
from a rendering so that it can be pasted into a differently-colored document.
Note that only directly visible emitters can be hidden using this feature---a
reflection on a shiny surface will be unaffected. To perform the kind of
compositing shown in Figure~\ref{fig:hideemitters}, it is also necessary to
enable the alpha channel in the scene's film instance (Section~\ref{sec:films}).
\renderings{
\unframedrendering{Daylit smoke rendered with \code{hideEmitters} set to \code{false}
(the default setting)}{integrator_volpath_normal}
\unframedrendering{Rendered with \code{hideEmitters} set to \code{true} and alpha-composited
onto a white background.}{integrator_volpath_hideemitters}
\caption{\label{fig:hideemitters}An example application of the \code{hideEmitters} parameter
together with alpha blending}
}

View File

@ -458,6 +458,7 @@ protected:
int m_maxDepth;
int m_rrDepth;
bool m_strictNormals;
bool m_hideEmitters;
};
MTS_NAMESPACE_END

View File

@ -25,20 +25,29 @@ MTS_NAMESPACE_BEGIN
* \parameters{
* \parameter{shadingSamples}{\Integer}{This convenience parameter can be
* used to set both \code{emitterSamples} and \code{bsdfSamples} at
* the same time.}
* the same time.
* }
* \parameter{emitterSamples}{\Integer}{Optional more fine-grained
* parameter: specifies the number of samples that should be generated
* using the direct illumination strategies implemented by the scene's
* emitters\default{set to the value of \code{shadingSamples}}}
* emitters\default{set to the value of \code{shadingSamples}}
* }
* \parameter{bsdfSamples}{\Integer}{Optional more fine-grained
* parameter: specifies the number of samples that should be generated
* using the BSDF sampling strategies implemented by the scene's
* surfaces\default{set to the value of \code{shadingSamples}}}
* surfaces\default{set to the value of \code{shadingSamples}}
* }
* \parameter{strictNormals}{\Boolean}{Be strict about potential
* inconsistencies involving shading normals? See \pluginref{path}
* for details.\default{no, i.e. \code{false}}}
* inconsistencies involving shading normals? See
* page~\pageref{sec:strictnormals} for details.
* \default{no, i.e. \code{false}}
* }
* \parameter{hideEmitters}{\Boolean}{Hide directly visible emitters?
* See page~\pageref{sec:hideemitters} for details.
* \default{no, i.e. \code{false}}
* }
* }
*
* \vspace{-1mm}
* \renderings{
* \medrendering{Only BSDF sampling}{integrator_direct_bsdf}
* \medrendering{Only emitter sampling}{integrator_direct_lum}
@ -92,6 +101,9 @@ public:
m_bsdfSamples = props.getSize("bsdfSamples", shadingSamples);
/* Be strict about potential inconsistencies involving shading normals? */
m_strictNormals = props.getBoolean("strictNormals", false);
/* When this flag is set to true, contributions from directly
* visible emitters will not be included in the rendered image */
m_hideEmitters = props.getBoolean("hideEmitters", false);
Assert(m_emitterSamples + m_bsdfSamples > 0);
}
@ -101,6 +113,7 @@ public:
m_emitterSamples = stream->readSize();
m_bsdfSamples = stream->readSize();
m_strictNormals = stream->readBool();
m_hideEmitters = stream->readBool();
configure();
}
@ -109,6 +122,7 @@ public:
stream->writeSize(m_emitterSamples);
stream->writeSize(m_bsdfSamples);
stream->writeBool(m_strictNormals);
stream->writeBool(m_hideEmitters);
}
void configure() {
@ -142,14 +156,14 @@ public:
if (!rRec.rayIntersect(ray)) {
/* If no intersection could be found, possibly return
radiance from a background emitter */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
if (rRec.type & RadianceQueryRecord::EEmittedRadiance && !m_hideEmitters)
return scene->evalEnvironment(ray);
else
return Spectrum(0.0f);
}
/* Possibly include emitted radiance if requested */
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters)
Li += its.Le(-ray.d);
/* Include radiance from a subsurface scattering model if requested */
@ -271,7 +285,7 @@ public:
/* Intersected nothing -- perhaps there is an environment map? */
const Emitter *env = scene->getEnvironmentEmitter();
if (!env)
if (!env || (m_hideEmitters && bRec.sampledType == BSDF::ENull))
continue;
value = env->evalEnvironment(RayDifferential(bsdfRay));
@ -316,6 +330,7 @@ private:
Float m_fracBSDF, m_fracLum;
Float m_weightBSDF, m_weightLum;
bool m_strictNormals;
bool m_hideEmitters;
};
MTS_IMPLEMENT_CLASS_S(MIDirectIntegrator, false, SamplingIntegrator)

View File

@ -38,7 +38,12 @@ static StatsCounter avgPathLength("Path tracer", "Average path length", EAverage
* }
* \parameter{strictNormals}{\Boolean}{Be strict about potential
* inconsistencies involving shading normals? See the description below
* for details.\default{no, i.e. \code{false}}}
* for details.\default{no, i.e. \code{false}}
* }
* \parameter{hideEmitters}{\Boolean}{Hide directly visible emitters?
* See page~\pageref{sec:hideemitters} for details.
* \default{no, i.e. \code{false}}
* }
* }
*
* This integrator implements a basic path tracer and is a \emph{good default choice}
@ -75,7 +80,8 @@ static StatsCounter avgPathLength("Path tracer", "Average path length", EAverage
* low-discrepancy sample generators (i.e. \pluginref{ldsampler},
* \pluginref{halton}, or \pluginref{sobol}).
*
* \paragraph{Strict normals:} Triangle meshes often rely on interpolated shading normals
* \paragraph{Strict normals:}\label{sec:strictnormals}
* Triangle meshes often rely on interpolated shading normals
* to suppress the inherently faceted appearance of the underlying geometry. These
* ``fake'' normals are not without problems, however. They can lead to paradoxical
* situations where a light ray impinges on an object from a direction that is classified as ``outside''
@ -116,6 +122,7 @@ public:
Intersection &its = rRec.its;
RayDifferential ray(r);
Spectrum Li(0.0f);
bool scattered = false;
/* Perform the first ray intersection (or ignore if the
intersection has already been provided). */
@ -129,7 +136,8 @@ public:
if (!its.isValid()) {
/* If no intersection could be found, potentially return
radiance from a environment luminaire if it exists */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
if ((rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered))
Li += throughput * scene->evalEnvironment(ray);
break;
}
@ -137,7 +145,8 @@ public:
const BSDF *bsdf = its.getBSDF(ray);
/* Possibly include emitted radiance if requested */
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered))
Li += throughput * its.Le(-ray.d);
/* Include radiance from a subsurface scattering model if requested */
@ -201,6 +210,8 @@ public:
if (bsdfWeight.isZero())
break;
scattered |= bRec.sampledType != BSDF::ENull;
/* Prevent light leaks due to the use of shading normals */
const Vector wo = its.toWorld(bRec.wo);
Float woDotGeoN = dot(its.geoFrame.n, wo);
@ -224,6 +235,9 @@ public:
const Emitter *env = scene->getEnvironmentEmitter();
if (env) {
if (m_hideEmitters && !scattered)
break;
value = env->evalEnvironment(ray);
if (!env->fillDirectSamplingRecord(dRec, ray))
break;

View File

@ -37,8 +37,14 @@ static StatsCounter avgPathLength("Volumetric path tracer", "Average path length
* path termination criterion. \default{\code{5}}
* }
* \parameter{strictNormals}{\Boolean}{Be strict about potential
* inconsistencies involving shading normals? See \pluginref{path}
* for details.\default{no, i.e. \code{false}}}
* inconsistencies involving shading normals? See
* page~\pageref{sec:strictnormals} for details.
* \default{no, i.e. \code{false}}
* }
* \parameter{hideEmitters}{\Boolean}{Hide directly visible emitters?
* See page~\pageref{sec:hideemitters} for details.
* \default{no, i.e. \code{false}}
* }
* }
*
* This plugin provides a volumetric path tracer that can be used to
@ -151,7 +157,6 @@ public:
break;
throughput *= phaseVal;
/* Trace a ray in this direction */
ray = Ray(mRec.p, pRec.wo, ray.time);
ray.mint = 0;
@ -186,7 +191,8 @@ public:
if (!its.isValid()) {
/* If no intersection could be found, possibly return
attenuated radiance from a background luminaire */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance) {
if ((rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered)) {
Spectrum value = throughput * scene->evalEnvironment(ray);
if (rRec.medium)
value *= rRec.medium->evalTransmittance(ray);
@ -197,7 +203,8 @@ public:
}
/* Possibly include emitted radiance if requested */
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered))
Li += throughput * its.Le(-ray.d);
/* Include radiance from a subsurface integrator if requested */

View File

@ -37,8 +37,14 @@ static StatsCounter avgPathLength("Volumetric path tracer", "Average path length
* path termination criterion. \default{\code{5}}
* }
* \parameter{strictNormals}{\Boolean}{Be strict about potential
* inconsistencies involving shading normals? See \pluginref{path}
* for details.\default{no, i.e. \code{false}}}
* inconsistencies involving shading normals? See
* page~\pageref{sec:strictnormals} for details.
* \default{no, i.e. \code{false}}
* }
* \parameter{hideEmitters}{\Boolean}{Hide directly visible emitters?
* See page~\pageref{sec:hideemitters} for details.
* \default{no, i.e. \code{false}}
* }
* }
*
* This plugin provides a basic volumetric path tracer that can be used to
@ -86,7 +92,7 @@ public:
MediumSamplingRecord mRec;
RayDifferential ray(r);
Spectrum Li(0.0f);
bool nullChain = true;
bool nullChain = true, scattered = false;
Float eta = 1.0f;
/* Perform the first ray intersection (or ignore if the
@ -153,6 +159,7 @@ public:
ray.mint = 0;
scene->rayIntersect(ray, its);
nullChain = false;
scattered = true;
} else {
/* Sample
tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure
@ -165,7 +172,8 @@ public:
if (!its.isValid()) {
/* If no intersection could be found, possibly return
attenuated radiance from a background luminaire */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance) {
if ((rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered)) {
Spectrum value = throughput * scene->evalEnvironment(ray);
if (rRec.medium)
value *= rRec.medium->evalTransmittance(ray);
@ -175,7 +183,8 @@ public:
}
/* Possibly include emitted radiance if requested */
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)
&& (!m_hideEmitters || scattered))
Li += throughput * its.Le(-ray.d);
/* Include radiance from a subsurface integrator if requested */
@ -267,6 +276,7 @@ public:
/* In the next iteration, trace a ray in this direction */
ray = Ray(its.p, wo, ray.time);
scene->rayIntersect(ray, its);
scattered |= bRec.sampledType != BSDF::ENull;
}
if (rRec.depth++ >= m_rrDepth) {

View File

@ -46,6 +46,10 @@ MTS_NAMESPACE_BEGIN
* Granularity of photon tracing work units for the purpose
* of parallelization (in \# of shot particles) \default{0, i.e. decide automatically}
* }
* \parameter{hideEmitters}{\Boolean}{Hide directly visible emitters?
* See page~\pageref{sec:hideemitters} for details.
* \default{no, i.e. \code{false}}
* }
* \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}}
@ -126,6 +130,9 @@ public:
m_gatherLocally = props.getBoolean("gatherLocally", true);
/* Indicates if the gathering steps should be canceled if not enough photons are generated. */
m_autoCancelGathering = props.getBoolean("autoCancelGathering", true);
/* When this flag is set to true, contributions from directly
* visible emitters will not be included in the rendered image */
m_hideEmitters = props.getBoolean("hideEmitters", false);
if (m_maxDepth == 0) {
Log(EError, "maxDepth must be greater than zero!");
@ -159,6 +166,7 @@ public:
m_volumeLookupSize = stream->readInt();
m_gatherLocally = stream->readBool();
m_autoCancelGathering = stream->readBool();
m_hideEmitters = stream->readBool();
m_causticPhotonMapID = m_globalPhotonMapID = m_breID = 0;
configure();
}
@ -190,6 +198,7 @@ public:
stream->writeInt(m_volumeLookupSize);
stream->writeBool(m_gatherLocally);
stream->writeBool(m_autoCancelGathering);
stream->writeBool(m_hideEmitters);
}
/// Configure the sampler for a specified amount of direct illumination samples
@ -261,11 +270,11 @@ public:
if (proc->getReturnStatus() != ParallelProcess::ESuccess)
return false;
Log(EDebug, "Global photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
ref<PhotonMap> globalPhotonMap = proc->getPhotonMap();
if (globalPhotonMap->isFull()) {
Log(EDebug, "Global photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
m_globalPhotonMap = globalPhotonMap;
m_globalPhotonMap->setScaleFactor(1 / (Float) proc->getShotParticles());
m_globalPhotonMap->build();
@ -292,11 +301,11 @@ public:
if (proc->getReturnStatus() != ParallelProcess::ESuccess)
return false;
Log(EDebug, "Caustic photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
ref<PhotonMap> causticPhotonMap = proc->getPhotonMap();
if (causticPhotonMap->isFull()) {
Log(EDebug, "Caustic photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
m_causticPhotonMap = causticPhotonMap;
m_causticPhotonMap->setScaleFactor(1 / (Float) proc->getShotParticles());
m_causticPhotonMap->build();
@ -324,11 +333,11 @@ public:
if (proc->getReturnStatus() != ParallelProcess::ESuccess)
return false;
Log(EDebug, "Volume photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
ref<PhotonMap> volumePhotonMap = proc->getPhotonMap();
if (volumePhotonMap->isFull()) {
Log(EDebug, "Volume photon map full. Shot " SIZE_T_FMT " particles, excess photons due to parallelism: "
SIZE_T_FMT, proc->getShotParticles(), proc->getExcessPhotons());
volumePhotonMap->setScaleFactor(1 / (Float) proc->getShotParticles());
volumePhotonMap->build();
m_bre = new BeamRadianceEstimator(volumePhotonMap, m_volumeLookupSize);
@ -407,13 +416,13 @@ public:
if (!its.isValid()) {
/* If no intersection could be found, possibly return
attenuated radiance from a background luminaire */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
if ((rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters)
LiSurf = scene->evalEnvironment(ray);
return LiSurf * transmittance + LiMedium;
}
/* Possibly include emitted radiance if requested */
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
if (its.isEmitter() && (rRec.type & RadianceQueryRecord::EEmittedRadiance) && !m_hideEmitters)
LiSurf += its.Le(-ray.d);
/* Include radiance from a subsurface scattering model if requested */
@ -446,7 +455,7 @@ public:
RadianceQueryRecord rRec2;
for (int i=0; i<compCount; i++) {
unsigned int type = bsdf->getType(i);
if (!(type & BSDF::EDelta) && type == BSDF::ENull)
if (!(type & BSDF::EDelta))
continue;
/* Sample the BSDF and recurse */
BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
@ -676,6 +685,7 @@ private:
int m_granularity, m_directSamples, m_glossySamples;
int m_rrDepth, m_maxDepth, m_maxSpecularDepth;
bool m_gatherLocally, m_autoCancelGathering;
bool m_hideEmitters;
};
MTS_IMPLEMENT_CLASS_S(PhotonMapIntegrator, false, SamplingIntegrator)

View File

@ -211,6 +211,12 @@ MonteCarloIntegrator::MonteCarloIntegrator(const Properties &props) : SamplingIn
*/
m_strictNormals = props.getBoolean("strictNormals", false);
/**
* When this flag is set to true, contributions from directly
* visible emitters will not be included in the rendered image
*/
m_hideEmitters = props.getBoolean("hideEmitters", false);
if (m_rrDepth <= 0)
Log(EError, "'rrDepth' must be set to a value greater than zero!");
@ -223,6 +229,7 @@ MonteCarloIntegrator::MonteCarloIntegrator(Stream *stream, InstanceManager *mana
m_rrDepth = stream->readInt();
m_maxDepth = stream->readInt();
m_strictNormals = stream->readBool();
m_hideEmitters = stream->readBool();
}
void MonteCarloIntegrator::serialize(Stream *stream, InstanceManager *manager) const {
@ -230,6 +237,7 @@ void MonteCarloIntegrator::serialize(Stream *stream, InstanceManager *manager) c
stream->writeInt(m_rrDepth);
stream->writeInt(m_maxDepth);
stream->writeBool(m_strictNormals);
stream->writeBool(m_hideEmitters);
}
std::string RadianceQueryRecord::toString() const {

View File

@ -41,6 +41,10 @@
implicitly have <tt>strictNormals</tt> activated. Hence, another use of this parameter
is to match renderings created by these methods.</p>
</param>
<param name="hideEmitters" readableName="Hide directly visible emitters" type="boolean" default="false" importance="1">
Hide light sources (e.g. area or environment light sources) that are directly visible to the camera?
Reflections of light sources remain unaffected.
</param>
</plugin>
<plugin type="integrator" name="irrcache" readableName="Irradiance cache"
@ -191,6 +195,10 @@
implicitly have <tt>strictNormals</tt> activated. Hence, another use of this parameter
is to match renderings created by these methods.</p>
</param>
<param name="hideEmitters" readableName="Hide directly visible emitters" type="boolean" default="false" importance="1">
Hide light sources (e.g. area or environment light sources) that are directly visible to the camera?
Reflections of light sources remain unaffected.
</param>
</plugin>
<plugin type="integrator" name="path" readableName="Path tracer"
@ -350,6 +358,10 @@
Specifies the minimum path depth, after which the implementation will start to use the
"russian roulette" path termination criterion when tracing photons (set to <tt>-1</tt> to disable).
</param>
<param name="hideEmitters" readableName="Hide directly visible emitters" type="boolean" default="false" importance="1">
Hide light sources (e.g. area or environment light sources) that are directly visible to the camera?
Reflections of light sources remain unaffected.
</param>
</plugin>
<plugin type="integrator" name="ppm" readableName="Progressive photon mapper"