/* 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 #include "maxexp.h" MTS_NAMESPACE_BEGIN /** * Homogeneous participating medium. An arbitrary (manifold) shape * must be specified as a child object. */ class HomogeneousMedium : public Medium { public: enum ESamplingStrategy { ERandom, ESingle, EBalance, EManual, EMaximum }; HomogeneousMedium(const Properties &props) : Medium(props) { std::string strategy = props.getString("strategy", "random"); if (strategy == "random") m_strategy = ERandom; else if (strategy == "single") m_strategy = ESingle; else if (strategy == "balance") m_strategy = EBalance; else if (strategy == "maximum") m_strategy = EMaximum; else if (strategy == "manual") m_strategy = EManual; else Log(EError, "Specified an unknown sampling strategy"); m_channel = props.getInteger("channel", 0); m_sigma = props.getFloat("sigma", -1); // for the manual strategy Assert(m_channel >= 0 && m_channel < SPECTRUM_SAMPLES); if (props.getBoolean("mono", false)) { m_sigmaA = Spectrum(m_sigmaA[m_channel]); m_sigmaS = Spectrum(m_sigmaS[m_channel]); m_sigmaT = m_sigmaA + m_sigmaS; } std::vector coeffs(SPECTRUM_SAMPLES); for (int i=0; ireadUInt(); for (size_t i=0; i(manager->getInstance(stream))); m_channel = stream->readInt(); m_strategy = (ESamplingStrategy) stream->readInt(); m_sigma = stream->readFloat(); std::vector coeffs(SPECTRUM_SAMPLES); for (int i=0; idecRef(); delete m_maxExpDist; } void serialize(Stream *stream, InstanceManager *manager) const { Medium::serialize(stream, manager); stream->writeUInt((uint32_t) m_shapes.size()); for (size_t i=0; iserialize(stream, m_shapes[i]); stream->writeInt(m_channel); stream->writeInt(m_strategy); stream->writeFloat(m_sigma); } void configure() { Medium::configure(); if (m_shapes.size() == 0) Log(EError, "This medium requires one or more Shape instance as a child"); m_kdTree->build(); m_aabb = m_kdTree->getAABB(); } bool isInside(const Ray &r) const { Ray ray(r(r.mint + Epsilon), r.d, 0.0f); Intersection its; if (!m_kdTree->rayIntersect(ray, its)) return false; return dot(ray.d, its.geoFrame.n) > 0; } Spectrum tau(const Ray &r) const { Float dLength = r.d.length(); Ray ray(r(r.mint), r.d / dLength, Epsilon, std::numeric_limits::infinity(), 0.0f); Float coveredLength = 0, remaining = (r.maxt - r.mint) * dLength; bool inside = isInside(r); Intersection its; int iterations = 0; while (remaining > 0 && m_kdTree->rayIntersect(ray, its)) { if (inside) coveredLength += std::min(remaining, its.t); remaining -= its.t; inside = !inside; ray.o = its.p; if (++iterations > 20) { /// Just a precaution.. Log(EWarn, "tau(): round-off error issues?"); break; } } return m_sigmaT * coveredLength; } bool sampleDistance(const Ray &theRay, Float distSurf, MediumSamplingRecord &mRec, Sampler *sampler) const { Intersection its; Ray ray(theRay.o, theRay.d, 0.0f); int iterations = 0; /* Check if the start of the ray is already inside the medium */ bool inside = isInside(ray); Point orig(theRay.o); /* Remaining distance in the medium */ Float distMed; Float sigmaT; switch (m_strategy) { case EManual: { sigmaT = m_sigma; Float desiredAttenuation = 1 - sampler->next1D(); distMed = -std::log(desiredAttenuation) / sigmaT; mRec.pdf = sigmaT * std::exp(-sigmaT * distMed); mRec.miWeight = 1; } break; case ESingle: { sigmaT = m_sigmaT[m_channel]; Float desiredAttenuation = 1 - sampler->next1D(); distMed = -std::log(desiredAttenuation) / sigmaT; mRec.pdf = sigmaT * std::exp(-sigmaT * distMed); mRec.miWeight = 1; } break; case ERandom: { Float rv = sampler->next1D(); int channel = std::min( (int) (rv * SPECTRUM_SAMPLES), SPECTRUM_SAMPLES-1); sigmaT = m_sigmaT[channel]; Float desiredAttenuation = 1 - sampler->next1D(); distMed = -std::log(desiredAttenuation) / sigmaT; mRec.pdf = sigmaT * std::exp(-sigmaT * distMed); mRec.miWeight = 1; } break; case EBalance: { Float rv = sampler->next1D(); int channel = std::min( (int) (rv * SPECTRUM_SAMPLES), SPECTRUM_SAMPLES-1); sigmaT = m_sigmaT[channel]; Float desiredAttenuation = 1 - sampler->next1D(); distMed = -std::log(desiredAttenuation) / sigmaT; Spectrum pdf = m_sigmaT * (Spectrum(-distMed) * m_sigmaT).exp(); mRec.pdf = pdf[channel] / SPECTRUM_SAMPLES; Float sum = 0; for (int i=0; inext1D(); distMed = m_maxExpDist->sample(rv, mRec.pdf); mRec.miWeight = 1; sigmaT = -1; // make the compiler happy }; break; default: Log(EError, "Unknown sampling strategy!"); return false; } Float traveled = 0, // Traveled ray distance covered = 0; // Distance covered by the medium while (m_kdTree->rayIntersect(ray, its)) { if (inside) { /* Moving through the medium */ if (its.t > distMed && distMed < distSurf) { /* A medium interaction occurred */ mRec.p = ray(distMed); mRec.sigmaA = m_sigmaA; mRec.sigmaS = m_sigmaS; mRec.albedo = m_albedo; mRec.medium = this; mRec.attenuation = (m_sigmaT * (-covered-distMed)).exp(); mRec.t = traveled + distMed; return true; } else if (its.t > distSurf) { /* A surface interaction occurred */ covered += distSurf; traveled += distSurf; break; } else { /* Still moving through the medium */ covered += its.t; distMed -= its.t; } } else { /* Moving through space outside of the medium */ if (its.t > distSurf) { /* A surface interaction occurred */ traveled += distSurf; break; } } traveled += its.t; distSurf -= its.t; inside = !inside; ray.o = its.p; if (++iterations > 10) { /// Just a precaution.. Log(EWarn, "selectDistance(): round-off error issues?"); break; } } /* There was no medium interaction inside the permitted ray interval. This occurred with the probability tau[channel](0, maxDist) */ if (m_strategy != EMaximum) mRec.pdf = std::exp(-sigmaT * covered); else mRec.pdf = 1-m_maxExpDist->cdf(covered); mRec.t = traveled; mRec.attenuation = (m_sigmaT * (-covered)).exp(); mRec.miWeight = 1; return false; } void setParent(ConfigurableObject *parent) { if (parent->getClass()->derivesFrom(Shape::m_theClass)) Log(EError, "Medium cannot be a parent of a shape"); } void addChild(const std::string &name, ConfigurableObject *child) { if (child->getClass()->derivesFrom(Shape::m_theClass)) { Shape *shape = static_cast(child); if (shape->isCompound()) { int ctr = 0; while (true) { ref childShape = shape->getElement(ctr++); if (!childShape) break; addChild("", childShape); } } else { m_kdTree->addShape(shape); shape->incRef(); m_shapes.push_back(shape); } } else { Medium::addChild(name, child); } } std::string toString() const { std::ostringstream oss; oss << "HomogeneousMedium[" << endl << " sigmaA = " << m_sigmaA.toString() << "," << std::endl << " sigmaS = " << m_sigmaS.toString() << "," << std::endl << " sigmaT = " << m_sigmaT.toString() << "," << std::endl << " phase = " << indent(m_phaseFunction->toString()) << "," << std::endl << " shapes = " << indent(listToString(m_shapes)) << std::endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: ref m_kdTree; std::vector m_shapes; int m_channel; Float m_sigma; ESamplingStrategy m_strategy; MaxExpDist *m_maxExpDist; }; MTS_IMPLEMENT_CLASS_S(HomogeneousMedium, false, Medium) MTS_EXPORT_PLUGIN(HomogeneousMedium, "Homogeneous medium"); MTS_NAMESPACE_END