volpath_simple: fixed a minor logic error that could cause errors in light paths that simultaneously involve multiple index-matched and index-mismatched medium transitions

metadata
Wenzel Jakob 2012-10-20 01:42:50 -04:00
parent f041cd58eb
commit 8e2bfb7340
1 changed files with 23 additions and 20 deletions

View File

@ -29,11 +29,11 @@ static StatsCounter avgPathLength("Volumetric path tracer", "Average path length
* \parameter{maxDepth}{\Integer}{Specifies the longest path depth * \parameter{maxDepth}{\Integer}{Specifies the longest path depth
* in the generated output image (where \code{-1} corresponds to $\infty$). * in the generated output image (where \code{-1} corresponds to $\infty$).
* A value of \code{1} will only render directly visible light sources. * A value of \code{1} will only render directly visible light sources.
* \code{2} will lead to single-bounce (direct-only) illumination, * \code{2} will lead to single-bounce (direct-only) illumination,
* and so on. \default{\code{-1}} * and so on. \default{\code{-1}}
* } * }
* \parameter{rrDepth}{\Integer}{Specifies the minimum path depth, after * \parameter{rrDepth}{\Integer}{Specifies the minimum path depth, after
* which the implementation will start to use the ``russian roulette'' * which the implementation will start to use the ``russian roulette''
* path termination criterion. \default{\code{5}} * path termination criterion. \default{\code{5}}
* } * }
* \parameter{strictNormals}{\Boolean}{Be strict about potential * \parameter{strictNormals}{\Boolean}{Be strict about potential
@ -51,12 +51,12 @@ static StatsCounter avgPathLength("Volumetric path tracer", "Average path length
* or one of the bidirectional techniques. * or one of the bidirectional techniques.
* *
* \remarks{ * \remarks{
* \item This integrator performs poorly when rendering * \item This integrator performs poorly when rendering
* participating media that have a different index of refraction compared * participating media that have a different index of refraction compared
* to the surrounding medium. * to the surrounding medium.
* \item This integrator has difficulties rendering * \item This integrator has difficulties rendering
* scenes that contain relatively glossy materials (\pluginref{volpath} is preferable in this case). * scenes that contain relatively glossy materials (\pluginref{volpath} is preferable in this case).
* \item This integrator has poor convergence properties when rendering * \item This integrator has poor convergence properties when rendering
* caustics and similar effects. In this case, \pluginref{bdpt} or * caustics and similar effects. In this case, \pluginref{bdpt} or
* one of the photon mappers may be preferable. * one of the photon mappers may be preferable.
* } * }
@ -79,7 +79,7 @@ public:
bool scattered = false; bool scattered = false;
Float eta = 1.0f; Float eta = 1.0f;
/* Perform the first ray intersection (or ignore if the /* Perform the first ray intersection (or ignore if the
intersection has already been provided). */ intersection has already been provided). */
rRec.rayIntersect(ray); rRec.rayIntersect(ray);
Spectrum throughput(1.0f); Spectrum throughput(1.0f);
@ -144,7 +144,7 @@ public:
scene->rayIntersect(ray, its); scene->rayIntersect(ray, its);
scattered = true; scattered = true;
} else { } else {
/* Sample /* Sample
tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure tau(x, y) * (Surface integral). This happens with probability mRec.pdfFailure
Account for this and multiply by the proper per-color-channel transmittance. Account for this and multiply by the proper per-color-channel transmittance.
*/ */
@ -153,9 +153,9 @@ public:
throughput *= mRec.transmittance / mRec.pdfFailure; throughput *= mRec.transmittance / mRec.pdfFailure;
if (!its.isValid()) { if (!its.isValid()) {
/* If no intersection could be found, possibly return /* If no intersection could be found, possibly return
attenuated radiance from a background luminaire */ attenuated radiance from a background luminaire */
if (rRec.type & RadianceQueryRecord::EEmittedRadiance) if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
Li += throughput * scene->evalEnvironment(ray); Li += throughput * scene->evalEnvironment(ray);
break; break;
} }
@ -171,17 +171,17 @@ public:
/* Prevent light leaks due to the use of shading normals */ /* Prevent light leaks due to the use of shading normals */
Float wiDotGeoN = -dot(its.geoFrame.n, ray.d), Float wiDotGeoN = -dot(its.geoFrame.n, ray.d),
wiDotShN = Frame::cosTheta(its.wi); wiDotShN = Frame::cosTheta(its.wi);
if (m_strictNormals && wiDotGeoN * wiDotShN < 0) if (m_strictNormals && wiDotGeoN * wiDotShN < 0)
break; break;
/* ==================================================================== */ /* ==================================================================== */
/* Direct illumination sampling */ /* Direct illumination sampling */
/* ==================================================================== */ /* ==================================================================== */
const BSDF *bsdf = its.getBSDF(ray); const BSDF *bsdf = its.getBSDF(ray);
/* Estimate the direct illumination if this is requested */ /* Estimate the direct illumination if this is requested */
if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance && if (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance &&
(bsdf->getType() & BSDF::ESmooth)) { (bsdf->getType() & BSDF::ESmooth)) {
DirectSamplingRecord dRec(its); DirectSamplingRecord dRec(its);
int maxInteractions = m_maxDepth - rRec.depth - 1; int maxInteractions = m_maxDepth - rRec.depth - 1;
@ -210,7 +210,7 @@ public:
/* Sample BSDF * cos(theta) */ /* Sample BSDF * cos(theta) */
BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance); BSDFSamplingRecord bRec(its, rRec.sampler, ERadiance);
Spectrum bsdfVal = bsdf->sample(bRec, rRec.nextSample2D()); Spectrum bsdfVal = bsdf->sample(bRec, rRec.nextSample2D());
if (bsdfVal.isZero()) if (bsdfVal.isZero())
break; break;
/* Recursively gather indirect illumination? */ /* Recursively gather indirect illumination? */
@ -223,8 +223,12 @@ public:
if ((rRec.depth < m_maxDepth || m_maxDepth < 0) && if ((rRec.depth < m_maxDepth || m_maxDepth < 0) &&
(rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) && (rRec.type & RadianceQueryRecord::EDirectSurfaceRadiance) &&
(bRec.sampledType & BSDF::EDelta) && (bRec.sampledType & BSDF::EDelta) &&
!((bRec.sampledType & BSDF::ENull) && scattered)) !((bRec.sampledType & BSDF::ENull) && scattered)) {
recursiveType |= RadianceQueryRecord::EEmittedRadiance; recursiveType |= RadianceQueryRecord::EEmittedRadiance;
scattered = false;
} else {
scattered |= bRec.sampledType != BSDF::ENull;
}
/* Potentially stop the recursion if there is nothing more to do */ /* Potentially stop the recursion if there is nothing more to do */
if (recursiveType == 0) if (recursiveType == 0)
@ -236,28 +240,27 @@ public:
Float woDotGeoN = dot(its.geoFrame.n, wo); Float woDotGeoN = dot(its.geoFrame.n, wo);
if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals) if (woDotGeoN * Frame::cosTheta(bRec.wo) <= 0 && m_strictNormals)
break; break;
/* Keep track of the throughput, medium, and relative /* Keep track of the throughput, medium, and relative
refractive index along the path */ refractive index along the path */
throughput *= bsdfVal; throughput *= bsdfVal;
eta *= bRec.eta; eta *= bRec.eta;
if (its.isMediumTransition()) if (its.isMediumTransition())
rRec.medium = its.getTargetMedium(wo); rRec.medium = its.getTargetMedium(wo);
/* In the next iteration, trace a ray in this direction */ /* In the next iteration, trace a ray in this direction */
ray = Ray(its.p, wo, ray.time); ray = Ray(its.p, wo, ray.time);
scene->rayIntersect(ray, its); scene->rayIntersect(ray, its);
scattered |= bRec.sampledType != BSDF::ENull;
} }
if (rRec.depth++ >= m_rrDepth) { if (rRec.depth++ >= m_rrDepth) {
/* Russian roulette: try to keep path weights equal to one, /* Russian roulette: try to keep path weights equal to one,
while accounting for the solid angle compression at refractive while accounting for the solid angle compression at refractive
index boundaries. Stop with at least some probability to avoid index boundaries. Stop with at least some probability to avoid
getting stuck (e.g. due to total internal reflection) */ getting stuck (e.g. due to total internal reflection) */
Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f); Float q = std::min(throughput.max() * eta * eta, (Float) 0.95f);
if (rRec.nextSample1D() >= q) if (rRec.nextSample1D() >= q)
break; break;
throughput /= q; throughput /= q;
} }