mitsuba/src/medium/homogeneous.cpp

293 lines
8.9 KiB
C++

/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 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 <http://www.gnu.org/licenses/>.
*/
#include <mitsuba/render/scene.h>
#include "maxexp.h"
MTS_NAMESPACE_BEGIN
/**
* Homogeneous participating medium with support for general phase functions
*/
class HomogeneousMedium : public Medium {
public:
/**
* This class supports the following sampling strategies for choosing
* a suitable scattering location when sampling the RTE
*/
enum ESamplingStrategy {
EBalance, /// Exponential distrib.; pick a random channel each time
ESingle, /// Exponential distrib.; pick a specified channel
EManual, /// Exponential distrib.; manually specify the falloff
EMaximum /// Maximum-of-exponential distribution
};
HomogeneousMedium(const Properties &props)
: Medium(props), m_maxExpDist(NULL) {
std::string strategy = props.getString("strategy", "balance");
/**
* The goal of the medium sampling weight is to be able to
* sample medium intarctions according to
* sigma_s(t) * tau(0 <-> t)
* as opposed to
* sigma_t(t) * tau(0 <-> t)
* See the separate writeup for more details.
*/
m_mediumSamplingWeight = props.getFloat("mediumSamplingWeight", -1);
if (m_mediumSamplingWeight == -1) {
for (int i=0; i<SPECTRUM_SAMPLES; ++i) {
/// Record the highest albedo values across channels
Float albedo = m_sigmaS[i] / m_sigmaT[i];
if (albedo > m_mediumSamplingWeight)
m_mediumSamplingWeight = albedo;
}
if (m_mediumSamplingWeight > 0) {
/* The medium scatters some light -> place at least half
of the samples in it, otherwise we will render lots
of spatially varying noise where one pixel has a
medium interaction and the neighbors don't */
m_mediumSamplingWeight = std::max(m_mediumSamplingWeight,
(Float) 0.5f);
}
}
if (strategy == "balance") {
m_strategy = EBalance;
} else if (strategy == "single") {
m_strategy = ESingle;
/* By default, choose the lowest-variance channel
(the one with the smallest sigma_t, that is) */
int channel = 0;
Float smallest = std::numeric_limits<Float>::infinity();
for (int i=0; i<SPECTRUM_SAMPLES; ++i) {
if (m_sigmaT[i] < smallest) {
smallest = m_sigmaT[i];
channel = i;
}
}
channel = props.getInteger("channel", channel);
Assert(channel >= 0 && channel < SPECTRUM_SAMPLES);
m_samplingDensity = m_sigmaT[channel];
if (props.getBoolean("monochromatic", false)) {
/* Optionally turn this into a monochromatic medium
based on the chosen color channel. This is useful
when the whole scene is rendered once per channel
and then recombined to create a color image */
m_sigmaA = Spectrum(m_sigmaA[channel]);
m_sigmaS = Spectrum(m_sigmaS[channel]);
m_sigmaT = m_sigmaA + m_sigmaS;
}
} else if (strategy == "maximum") {
m_strategy = EMaximum;
std::vector<Float> coeffs(SPECTRUM_SAMPLES);
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
coeffs[i] = m_sigmaT[i];
m_maxExpDist = new MaxExpDist(coeffs);
} else if (strategy == "manual") {
m_strategy = EManual;
m_samplingDensity = props.getFloat("samplingDensity");
} else {
Log(EError, "Specified an unknown sampling strategy");
}
}
HomogeneousMedium(Stream *stream, InstanceManager *manager)
: Medium(stream, manager), m_maxExpDist(NULL) {
m_strategy = (ESamplingStrategy) stream->readInt();
m_samplingDensity= stream->readFloat();
m_mediumSamplingWeight = stream->readFloat();
if (m_strategy == EMaximum) {
std::vector<Float> coeffs(SPECTRUM_SAMPLES);
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
coeffs[i] = m_sigmaT[i];
m_maxExpDist = new MaxExpDist(coeffs);
}
configure();
}
virtual ~HomogeneousMedium() {
if (m_maxExpDist)
delete m_maxExpDist;
}
void configure() {
Medium::configure();
m_albedo = (m_sigmaS/m_sigmaT).max();
}
void serialize(Stream *stream, InstanceManager *manager) const {
Medium::serialize(stream, manager);
stream->writeInt(m_strategy);
stream->writeFloat(m_samplingDensity);
stream->writeFloat(m_mediumSamplingWeight);
}
Spectrum getTransmittance(const Ray &ray, Sampler *) const {
return (m_sigmaT * (ray.mint - ray.maxt)).exp();
}
bool sampleDistance(const Ray &ray, MediumSamplingRecord &mRec,
Sampler *sampler) const {
Float rand = sampler->next1D(), sampledDistance;
Float samplingDensity = m_samplingDensity;
if (rand <= m_mediumSamplingWeight) {
rand /= m_mediumSamplingWeight;
if (m_strategy != EMaximum) {
/* Choose the sampling density to be used */
if (m_strategy == EBalance) {
int channel = std::min((int) (sampler->next1D()
* SPECTRUM_SAMPLES), SPECTRUM_SAMPLES-1);
samplingDensity = m_sigmaT[channel];
}
sampledDistance = -std::log(1-rand) / samplingDensity;
} else {
sampledDistance = m_maxExpDist->sample(1-rand, mRec.pdfSuccess);
}
} else {
/* Don't generate a medium interaction */
sampledDistance = std::numeric_limits<Float>::infinity();
}
Float distSurf = ray.maxt - ray.mint;
bool success = true;
if (sampledDistance < distSurf) {
mRec.t = sampledDistance + ray.mint;
mRec.p = ray(mRec.t);
mRec.sigmaA = m_sigmaA;
mRec.sigmaS = m_sigmaS;
mRec.albedo = m_mediumSamplingWeight;
} else {
sampledDistance = distSurf;
success = false;
}
switch (m_strategy) {
case EMaximum:
mRec.pdfFailure = 1-m_maxExpDist->cdf(sampledDistance);
break;
case EBalance:
mRec.pdfFailure = 0;
mRec.pdfSuccess = 0;
for (int i=0; i<SPECTRUM_SAMPLES; ++i) {
Float tmp = std::exp(-m_sigmaT[i] * sampledDistance);
mRec.pdfFailure += tmp;
mRec.pdfSuccess += m_sigmaT[i] * tmp;
}
mRec.pdfFailure /= SPECTRUM_SAMPLES;
mRec.pdfSuccess /= SPECTRUM_SAMPLES;
break;
case ESingle:
case EManual:
mRec.pdfFailure = std::exp(-samplingDensity * sampledDistance);
mRec.pdfSuccess = samplingDensity * mRec.pdfFailure;
break;
default:
Log(EError, "Unknown sampling strategy!");
}
mRec.transmittance = (m_sigmaT * (-sampledDistance)).exp();
mRec.pdfSuccessRev = mRec.pdfSuccess = mRec.pdfSuccess * m_mediumSamplingWeight;
mRec.pdfFailure = m_mediumSamplingWeight * mRec.pdfFailure + (1-m_mediumSamplingWeight);
return success;
}
void pdfDistance(const Ray &ray, MediumSamplingRecord &mRec) const {
Float distance = ray.maxt - ray.mint;
switch (m_strategy) {
case EManual:
case ESingle: {
Float temp = std::exp(-m_samplingDensity * distance);
mRec.pdfSuccess = m_samplingDensity * temp;
mRec.pdfFailure = temp;
}
break;
case EBalance: {
mRec.pdfSuccess = 0;
mRec.pdfFailure = 0;
for (int i=0; i<SPECTRUM_SAMPLES; ++i) {
Float temp = std::exp(-m_sigmaT[i] * distance);
mRec.pdfSuccess += m_sigmaT[i] * temp;
mRec.pdfFailure += temp;
}
mRec.pdfSuccess /= SPECTRUM_SAMPLES;
mRec.pdfFailure /= SPECTRUM_SAMPLES;
}
break;
case EMaximum:
mRec.pdfSuccess = m_maxExpDist->pdf(distance);
mRec.pdfFailure = 1-m_maxExpDist->cdf(distance);
break;
default:
Log(EError, "Unknown sampling strategy!");
}
mRec.transmittance = (m_sigmaT * (-distance)).exp();
mRec.pdfSuccess = mRec.pdfSuccessRev = mRec.pdfSuccess * m_mediumSamplingWeight;
mRec.pdfFailure = mRec.pdfFailure * m_mediumSamplingWeight + (1-m_mediumSamplingWeight);
}
bool isHomogeneous() const {
return true;
}
std::string toString() const {
std::ostringstream oss;
oss << "HomogeneousMedium[" << endl
<< " sigmaA = " << m_sigmaA.toString() << "," << endl
<< " sigmaS = " << m_sigmaS.toString() << "," << endl
<< " sigmaT = " << m_sigmaT.toString() << "," << endl
<< " mediumSamplingWeight = " << m_mediumSamplingWeight << "," << endl
<< " samplingDensity = " << m_samplingDensity << "," << endl
<< " strategy = ";
switch (m_strategy) {
case ESingle: oss << "single," << endl; break;
case EManual: oss << "manual," << endl; break;
case EBalance: oss << "balance," << endl; break;
case EMaximum: oss << "maximum," << endl; break;
}
oss << " phase = " << indent(m_phaseFunction.toString()) << endl
<< "]";
return oss.str();
}
MTS_DECLARE_CLASS()
private:
Float m_samplingDensity, m_mediumSamplingWeight;
ESamplingStrategy m_strategy;
MaxExpDist *m_maxExpDist;
Float m_albedo;
};
MTS_IMPLEMENT_CLASS_S(HomogeneousMedium, false, Medium)
MTS_EXPORT_PLUGIN(HomogeneousMedium, "Homogeneous medium");
MTS_NAMESPACE_END