231 lines
8.6 KiB
C++
231 lines
8.6 KiB
C++
#include <mitsuba/render/scene.h>
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
/**
|
|
* Volumetric path tracer, which solves the full radiative transfer
|
|
* equation in the presence of participating media. Simplified version
|
|
* without multiple importance sampling - this version can be much
|
|
* faster than the multiple importance sampling version when when
|
|
* rendering heterogeneous participating media using the
|
|
* [Coleman et al.] sampling technique, since fewer attenuation
|
|
* evaluations will be required.
|
|
*/
|
|
class SimpleVolumetricPathTracer : public MonteCarloIntegrator {
|
|
public:
|
|
SimpleVolumetricPathTracer(const Properties &props) : MonteCarloIntegrator(props) {
|
|
}
|
|
|
|
/// Unserialize from a binary data stream
|
|
SimpleVolumetricPathTracer(Stream *stream, InstanceManager *manager)
|
|
: MonteCarloIntegrator(stream, manager) {
|
|
}
|
|
|
|
Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const {
|
|
/* Some aliases and local variables */
|
|
const Scene *scene = rRec.scene;
|
|
Intersection &its = rRec.its;
|
|
LuminaireSamplingRecord lRec;
|
|
MediumSamplingRecord mRec;
|
|
RayDifferential ray(r);
|
|
Intersection prevIts;
|
|
Spectrum Li(0.0f);
|
|
|
|
/* Perform the first ray intersection (or ignore if the
|
|
intersection has already been provided). */
|
|
rRec.rayIntersect(ray);
|
|
Spectrum pathThroughput(1.0f);
|
|
bool computeIntersection = false;
|
|
|
|
while (rRec.depth < m_maxDepth || m_maxDepth < 0) {
|
|
if (computeIntersection)
|
|
scene->rayIntersect(ray, its);
|
|
|
|
/* ==================================================================== */
|
|
/* Radiative Transfer Equation sampling */
|
|
/* ==================================================================== */
|
|
if (scene->sampleDistance(ray, its.t, mRec, rRec.sampler)) {
|
|
const PhaseFunction *phase = mRec.medium->getPhaseFunction();
|
|
Vector wo, wi = -ray.d;
|
|
|
|
/* Sample the integral
|
|
\int_x^y tau(x, x') [ \sigma_s \int_{S^2} \rho(\omega,\omega') L(x,\omega') d\omega' ] dx'
|
|
*/
|
|
pathThroughput *= mRec.sigmaS * mRec.attenuation * mRec.miWeight / mRec.pdf;
|
|
|
|
/* ==================================================================== */
|
|
/* Luminaire sampling */
|
|
/* ==================================================================== */
|
|
|
|
/* Estimate the single scattering component if this is requested */
|
|
if (rRec.type & RadianceQueryRecord::EInscatteredDirectRadiance &&
|
|
scene->sampleLuminaireAttenuated(mRec.p, lRec, rRec.nextSample2D())) {
|
|
Li += pathThroughput * lRec.Le * phase->f(mRec, -ray.d, -lRec.d);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* Phase function sampling */
|
|
/* ==================================================================== */
|
|
|
|
PhaseFunction::ESampledType sampledType;
|
|
Spectrum phaseVal = phase->sample(mRec, wi, wo, sampledType, rRec.nextSample2D());
|
|
if (phaseVal.max() == 0)
|
|
break;
|
|
prevIts = its;
|
|
|
|
/* Trace a ray in this direction */
|
|
ray = Ray(mRec.p, wo);
|
|
computeIntersection = true;
|
|
|
|
/* ==================================================================== */
|
|
/* Multiple scattering */
|
|
/* ==================================================================== */
|
|
|
|
/* Set the recursive query type */
|
|
if (!(rRec.type & RadianceQueryRecord::EInscatteredIndirectRadiance)) {
|
|
/* Stop if multiple scattering was not requested (except: sampled a delta phase function
|
|
- look for emitted radiance only) */
|
|
if (sampledType == PhaseFunction::EDelta)
|
|
rRec.type = RadianceQueryRecord::EEmittedRadiance;
|
|
else
|
|
break;
|
|
} else {
|
|
if (sampledType != PhaseFunction::EDelta || !(rRec.type & RadianceQueryRecord::EInscatteredDirectRadiance)) {
|
|
/* Emitted radiance is only included in the recursive query if:
|
|
- the sampled phase function component had a Dirac delta distribution AND
|
|
- the current query asks for single scattering
|
|
*/
|
|
rRec.type = RadianceQueryRecord::ERadianceNoEmission;
|
|
} else {
|
|
rRec.type = RadianceQueryRecord::ERadiance;
|
|
}
|
|
}
|
|
|
|
/* Russian roulette - Possibly stop the recursion */
|
|
if (rRec.depth >= m_rrDepth) {
|
|
if (rRec.nextSample1D() > mRec.albedo)
|
|
break;
|
|
else
|
|
pathThroughput /= mRec.albedo;
|
|
}
|
|
|
|
pathThroughput *= phaseVal;
|
|
rRec.depth++;
|
|
} else {
|
|
/* Sample
|
|
tau(x, y) (Surface integral). This happens with probability mRec.pdf
|
|
Divide this out and multiply with the proper per-color-channel attenuation.
|
|
*/
|
|
pathThroughput *= mRec.attenuation * mRec.miWeight / mRec.pdf;
|
|
|
|
if (!its.isValid()) {
|
|
/* If no intersection could be found, possibly return
|
|
attenuated radiance from a background luminaire */
|
|
if (rRec.type & RadianceQueryRecord::EEmittedRadiance)
|
|
Li += pathThroughput * scene->LeBackground(ray);
|
|
break;
|
|
}
|
|
|
|
const BSDF *bsdf = its.getBSDF(ray);
|
|
|
|
/* Possibly include emitted radiance if requested */
|
|
if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance))
|
|
Li += pathThroughput * its.Le(-ray.d);
|
|
|
|
/* Include radiance from a subsurface integrator if requested */
|
|
if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance))
|
|
Li += pathThroughput * its.LoSub(rRec.scene, -ray.d);
|
|
|
|
/* ==================================================================== */
|
|
/* Luminaire sampling */
|
|
/* ==================================================================== */
|
|
|
|
/* Estimate the direct illumination if this is requested */
|
|
if (rRec.type & RadianceQueryRecord::EDirectRadiance &&
|
|
scene->sampleLuminaireAttenuated(its, lRec, rRec.nextSample2D())) {
|
|
/* Allocate a record for querying the BSDF */
|
|
const BSDFQueryRecord bRec(rRec, its, its.toLocal(-lRec.d));
|
|
|
|
Li += pathThroughput * lRec.Le * bsdf->fCos(bRec);
|
|
}
|
|
|
|
/* ==================================================================== */
|
|
/* BSDF sampling */
|
|
/* ==================================================================== */
|
|
|
|
/* Sample BSDF * cos(theta) */
|
|
BSDFQueryRecord bRec(rRec, its, rRec.nextSample2D());
|
|
Spectrum bsdfVal = bsdf->sampleCos(bRec);
|
|
if (bsdfVal.isBlack())
|
|
break;
|
|
prevIts = its;
|
|
|
|
/* Trace a ray in this direction */
|
|
ray = Ray(its.p, its.toWorld(bRec.wo));
|
|
computeIntersection = true;
|
|
|
|
/* ==================================================================== */
|
|
/* Indirect illumination */
|
|
/* ==================================================================== */
|
|
|
|
/* Stop if indirect illumination was not requested */
|
|
if (!(rRec.type & RadianceQueryRecord::EIndirectRadiance)) {
|
|
/* Stop if indirect illumination was not requested (except: sampled a delta BSDF
|
|
- look for emitted radiance only) */
|
|
if (bRec.sampledType & BSDF::EDelta)
|
|
rRec.type = RadianceQueryRecord::EEmittedRadiance;
|
|
else
|
|
break;
|
|
} else {
|
|
if (!(bRec.sampledType & BSDF::EDelta) || !(rRec.type & RadianceQueryRecord::EDirectRadiance)) {
|
|
/* Emitted radiance is only included in the recursive query if:
|
|
- the sampled BSDF component had a Dirac delta distribution AND
|
|
- the current query asks for direct illumination
|
|
*/
|
|
rRec.type = RadianceQueryRecord::ERadianceNoEmission;
|
|
} else {
|
|
rRec.type = RadianceQueryRecord::ERadiance;
|
|
}
|
|
}
|
|
|
|
/* Russian roulette - Possibly stop the recursion */
|
|
if (rRec.depth >= m_rrDepth) {
|
|
/* Assuming that BSDF importance sampling is perfect,
|
|
the following should equal the maximum albedo
|
|
over all spectral samples */
|
|
Float approxAlbedo = std::min((Float) 0.9, bsdfVal.max());
|
|
if (rRec.nextSample1D() > approxAlbedo)
|
|
break;
|
|
else
|
|
pathThroughput /= approxAlbedo;
|
|
}
|
|
|
|
pathThroughput *= bsdfVal;
|
|
rRec.depth++;
|
|
}
|
|
}
|
|
return Li;
|
|
}
|
|
|
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
|
MonteCarloIntegrator::serialize(stream, manager);
|
|
}
|
|
|
|
std::string toString() const {
|
|
std::ostringstream oss;
|
|
oss << "SimpleVolumetricPathTracer[" << std::endl
|
|
<< " maxDepth = " << m_maxDepth << "," << std::endl
|
|
<< " rrDepth = " << m_rrDepth << std::endl
|
|
<< "]";
|
|
return oss.str();
|
|
}
|
|
|
|
MTS_DECLARE_CLASS()
|
|
private:
|
|
Float m_beta;
|
|
};
|
|
|
|
MTS_IMPLEMENT_CLASS_S(SimpleVolumetricPathTracer, false, MonteCarloIntegrator)
|
|
MTS_EXPORT_PLUGIN(SimpleVolumetricPathTracer, "Simple volumetric path tracer");
|
|
MTS_NAMESPACE_END
|