/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2010 by Wenzel Jakob and others. Mitsuba is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License Version 3 as published by the Free Software Foundation. Mitsuba is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include MTS_NAMESPACE_BEGIN /** * Direct-only integrator using multiple importance sampling and * the power heuristic. Takes a user-specifiable amount of luminaire * and BSDF samples By setting one of the strategies to zero, this * class can effectively be turned into a luminaire sampling or * BSDF sampling-based integrator. Ignores participating media. */ class MIDirectIntegrator : public SampleIntegrator { public: MIDirectIntegrator(const Properties &props) : SampleIntegrator(props) { /* Number of samples to take using the luminaire sampling technique */ m_luminaireSamples = props.getInteger("luminaireSamples", 1); /* Number of samples to take using the BSDF sampling technique */ m_bsdfSamples = props.getInteger("bsdfSamples", 1); /* Beta factor for the power heuristic */ m_beta = props.getFloat("beta", 2.0f); Assert(m_luminaireSamples + m_bsdfSamples > 0); } /// Unserialize from a binary data stream MIDirectIntegrator(Stream *stream, InstanceManager *manager) : SampleIntegrator(stream, manager) { m_luminaireSamples = stream->readInt(); m_bsdfSamples = stream->readInt(); m_beta = stream->readFloat(); configure(); } void configure() { m_weightBSDF = 1 / (Float) m_bsdfSamples; m_weightLum = 1 / (Float) m_luminaireSamples; m_fracLum = m_luminaireSamples / (Float) (m_luminaireSamples + m_bsdfSamples); m_fracBSDF = m_bsdfSamples / (Float) (m_luminaireSamples + m_bsdfSamples); } void configureSampler(Sampler *sampler) { sampler->request2DArray(m_luminaireSamples); sampler->request2DArray(m_bsdfSamples); } Spectrum Li(const RayDifferential &r, RadianceQueryRecord &rRec) const { /* Some aliases and local variables */ const Scene *scene = rRec.scene; Intersection &its = rRec.its, bsdfIts; RayDifferential ray(r); LuminaireSamplingRecord lRec; Spectrum Li(0.0f); Point2 sample; /* Perform the first ray intersection (or ignore if the intersection has already been provided). */ if (rRec.rayIntersect(ray)) { ray.mint = 0; ray.maxt = rRec.its.t; } if (!its.isValid()) { /* If no intersection could be found, possibly return radiance from a background luminaire */ if (rRec.type & RadianceQueryRecord::EEmittedRadiance) return scene->LeBackground(ray); else return Spectrum(0.0f); } const BSDF *bsdf = its.getBSDF(ray); /* Possibly include emitted radiance if requested */ if (its.isLuminaire() && (rRec.type & RadianceQueryRecord::EEmittedRadiance)) Li += its.Le(-ray.d); /* Include radiance from a subsurface integrator if requested */ if (its.hasSubsurface() && (rRec.type & RadianceQueryRecord::ESubsurfaceRadiance)) Li += its.LoSub(scene, -ray.d); /* Leave here if direct illumination was not requested */ if (!(rRec.type & RadianceQueryRecord::EDirectRadiance)) return Li; /* ==================================================================== */ /* Luminaire sampling */ /* ==================================================================== */ Point2 *sampleArray; int numLuminaireSamples = m_luminaireSamples, numBSDFSamples = m_bsdfSamples; Float fracLum = m_fracLum, fracBSDF = m_fracBSDF, weightLum = m_weightLum, weightBSDF = m_weightBSDF; if (rRec.depth > 1) { /* This integrator is used by another integrator. Be less accurate as this sample will not directly be observed. */ numBSDFSamples = numLuminaireSamples = 1; fracLum = fracBSDF = .5f; weightLum = weightBSDF = 1.0f; } if (numLuminaireSamples > 1) { sampleArray = rRec.sampler->next2DArray(numLuminaireSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; } for (int i=0; isampleLuminaire(its, lRec, sampleArray[i])) { /* Allocate a record for querying the BSDF */ const BSDFQueryRecord bRec(rRec, its, its.toLocal(-lRec.d)); /* Evaluate BSDF * cos(theta) */ const Spectrum bsdfVal = bsdf->fCos(bRec); if (!bsdfVal.isBlack()) { /* Calculate prob. of having sampled that direction using BSDF sampling */ Float bsdfPdf = (lRec.luminaire->isIntersectable() || lRec.luminaire->isBackgroundLuminaire()) ? bsdf->pdf(bRec) : 0; /* Weight using the power heuristic */ const Float weight = miWeight(lRec.pdf * fracLum, bsdfPdf * fracBSDF) * weightLum; Li += lRec.Le * bsdfVal * weight; } } } /* ==================================================================== */ /* BSDF sampling */ /* ==================================================================== */ if (numBSDFSamples > 1) { sampleArray = rRec.sampler->next2DArray(numBSDFSamples); } else { sample = rRec.nextSample2D(); sampleArray = &sample; Assert(sampleArray[0] == sample); } for (int i=0; isampleCos(bRec, bsdfPdf); if (bsdfVal.isBlack()) break; bsdfVal /= bsdfPdf; /* Trace a ray in this direction */ Ray bsdfRay(its.p, its.toWorld(bRec.wo), ray.time); bool hitLuminaire = false; if (scene->rayIntersect(bsdfRay, bsdfIts)) { bsdfRay.mint = 0; bsdfRay.maxt = bsdfIts.t; /* Intersected something - check if it was a luminaire */ if (bsdfIts.isLuminaire()) { lRec = LuminaireSamplingRecord(bsdfIts, -bsdfRay.d); hitLuminaire = true; } } else { /* No intersection found. Possibly, there is a background luminaire such as an environment map? */ if (scene->hasBackgroundLuminaire()) { lRec.luminaire = scene->getBackgroundLuminaire(); lRec.d = -bsdfRay.d; hitLuminaire = true; } } /* If a luminaire was hit, estimate the local illumination and sample weight using the power heuristic */ if (hitLuminaire && (rRec.type & RadianceQueryRecord::EDirectRadiance)) { lRec.Le = lRec.luminaire->Le(lRec); Float lumPdf = scene->pdfLuminaire(its, lRec); if (bRec.sampledType & BSDF::EDelta) lumPdf = 0; const Float weight = miWeight(bsdfPdf * fracBSDF, lumPdf * fracLum) * weightBSDF; Li += lRec.Le * bsdfVal * weight; } } return Li; } inline Float miWeight(Float pdfA, Float pdfB) const { pdfA = std::pow(pdfA, m_beta); pdfB = std::pow(pdfB, m_beta); return pdfA / (pdfA + pdfB); } void serialize(Stream *stream, InstanceManager *manager) const { SampleIntegrator::serialize(stream, manager); stream->writeInt(m_luminaireSamples); stream->writeInt(m_bsdfSamples); stream->writeFloat(m_beta); } std::string toString() const { std::ostringstream oss; oss << "MIDirectIntegrator[" << std::endl << " luminaireSamples = " << m_luminaireSamples << "," << std::endl << " bsdfSamples = " << m_bsdfSamples << "," << std::endl << " beta = " << m_beta << std::endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: int m_luminaireSamples, m_bsdfSamples; Float m_fracBSDF, m_fracLum; Float m_weightBSDF, m_weightLum; Float m_beta; }; MTS_IMPLEMENT_CLASS_S(MIDirectIntegrator, false, SampleIntegrator) MTS_EXPORT_PLUGIN(MIDirectIntegrator, "Direct-only integrator"); MTS_NAMESPACE_END