sssbrdf-related bugfixes/improvements

metadata
Wenzel Jakob 2011-08-13 01:07:31 -04:00
parent ba8b62dd74
commit cd69e50a73
8 changed files with 149 additions and 69 deletions

View File

@ -400,7 +400,7 @@ public:
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
<< " thickness = " << m_thickness << "," << endl
<< " nested = " << indent(m_nested->toString()) << endl
<< " nested = " << indent(m_nested.toString()) << endl
<< "]";
return oss.str();
}

View File

@ -32,8 +32,12 @@ public:
DipoleBRDF(const Properties &props)
: BSDF(props) {
Spectrum sigmaS, sigmaA;
Float eta;
lookupMaterial(props, sigmaS, sigmaA, &eta, false);
/* Specifies the internal index of refraction at the interface */
m_intIOR = lookupIOR(props, "intIOR", "bk7");
m_intIOR = lookupIOR(props, "intIOR", eta);
/* Specifies the external index of refraction at the interface */
m_extIOR = lookupIOR(props, "extIOR", "air");
@ -41,9 +45,6 @@ public:
/* Mean cosine angle of the phase function */
m_g = props.getFloat("g", 0.0f);
Spectrum sigmaS, sigmaA;
lookupMaterial(props, sigmaS, sigmaA);
/* Scattering coefficient of the layer */
m_sigmaS = new ConstantSpectrumTexture(
props.getSpectrum("sigmaS", sigmaS));
@ -89,6 +90,9 @@ public:
| (m_sigmaS->isConstant() && m_sigmaA->isConstant() ? 0 : ESpatiallyVarying));
m_usesRayDifferentials = m_sigmaS->usesRayDifferentials() || m_sigmaA->usesRayDifferentials();
if ((m_sigmaS->getMaximum()+m_sigmaA->getMaximum()).isZero())
Log(EError, "Please specify nonzero sigmaS/sigmaA-values!");
/* relative index of refraction */
const Float eta = m_intIOR / m_extIOR;
@ -103,28 +107,25 @@ public:
Spectrum sigmaA = m_sigmaA->getValue(its),
sigmaS = m_sigmaS->getValue(its),
sigmaT = sigmaA + sigmaS,
reducedAlbedo(0.0f),
Rd(0.0f);
reducedAlbedo;
/* ==================================================================== */
/* Diffuse Reflectance due to Multiple Scattering */
/* Diffuse Reflectance due to Multiple Scattering */
/* ==================================================================== */
/* Reduced Scattering Albedo */
const Spectrum reducedSigmaS = sigmaS * (1.0f - m_g);
const Spectrum reducedSigmaT = reducedSigmaS + sigmaA;
/* Reduced scattering albedo */
const Spectrum reducedSigmaS = sigmaS * (1.0f - m_g),
reducedSigmaT = reducedSigmaS + sigmaA;
/* Avoid divisions by 0 */
for (int i = 0; i < SPECTRUM_SAMPLES; i++)
reducedAlbedo[i] = reducedSigmaT[i] > 0.0f ? (reducedSigmaS[i]/reducedSigmaT[i]) : 0.0f;
reducedAlbedo[i] = reducedSigmaT[i] > 0.0f ?
(reducedSigmaS[i]/reducedSigmaT[i]) : 0.0f;
/* Diffuse Reflectance */
const Spectrum rootExp = ((Spectrum(1.0f) - reducedAlbedo) * 3.0f).sqrt();
Rd = (reducedAlbedo * 0.5f) * (Spectrum(1.0f) + (-rootExp*(4.0f/3.0f*m_A)).exp())
* (-rootExp).exp();
return Rd;
return (reducedAlbedo * 0.5f) * (Spectrum(1.0f) +
(-rootExp*(4.0f/3.0f*m_A)).exp()) * (-rootExp).exp();
}
Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const {
@ -146,16 +147,19 @@ public:
}
Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const {
Float pdf;
return DipoleBRDF::sample(bRec, pdf, sample);
if (!(bRec.typeMask & EDiffuseReflection) || Frame::cosTheta(bRec.wi) <= 0)
return Spectrum(0.0f);
bRec.wo = squareToHemispherePSA(sample);
bRec.sampledComponent = 0;
bRec.sampledType = EDiffuseReflection;
return getDiffuseReflectance(bRec.its);
}
Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const {
if (!(bRec.typeMask & EDiffuseReflection) || Frame::cosTheta(bRec.wi) <= 0) {
pdf = 0.0f;
if (!(bRec.typeMask & EDiffuseReflection) || Frame::cosTheta(bRec.wi) <= 0)
return Spectrum(0.0f);
}
bRec.wo = squareToHemispherePSA(sample);
bRec.sampledComponent = 0;
@ -258,22 +262,23 @@ public:
<< "uniform float " << evalName << "_g;" << endl
<< endl
<< "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " float cosThetaI = cosTheta(wi);" << endl
<< " float cosThetaO = cosTheta(wo);" << endl
<< " if (cosThetaI < 0.0 || cosThetaO < 0.0)" << endl
<< " return vec3(0.0);" << endl
<< " vec3 sigmaS = " << depNames[0] << "(uv);" << endl
<< " vec3 sigmaA = " << depNames[1] << "(uv);" << endl
<< " vec3 reducedSigmaS = (1-" << evalName << "_g)*sigmaS/(sigmaS + sigmaA);" << endl
<< " vec3 reducedSigmaT = reducedSigmaS + sigmaA;" << endl
<< " vec3 reducedAlbedo = reducedSigmaS/reducedSigmaT;" << endl
<< " vec3 rootExp = sqrt((1.0 - reducedAlbedo) * 3.0);" << endl
<< " return (reducedAlbedo * 0.5) * (1+exp(-rootExp*(4.0/3.0 * " << endl
<< " " << evalName << "_A" << "))) * exp(-rootExp) * 0.31831 * abs(cosThetaO);" << endl
<< " float cosThetaI = cosTheta(wi);" << endl
<< " float cosThetaO = cosTheta(wo);" << endl
<< " if (cosThetaI < 0.0 || cosThetaO < 0.0)" << endl
<< " return vec3(0.0);" << endl
<< " vec3 sigmaS = " << depNames[0] << "(uv);" << endl
<< " vec3 sigmaA = " << depNames[1] << "(uv);" << endl
<< " vec3 reducedSigmaS = (1.0-" << evalName << "_g)*sigmaS;" << endl
<< " vec3 reducedSigmaT = reducedSigmaS + sigmaA, reducedAlbedo;" << endl
<< " for (int i=0; i<3; ++i)" << endl
<< " reducedAlbedo[i] = reducedSigmaT[i] > 0.0 ? reducedSigmaS[i]/reducedSigmaT[i] : 0.0;" << endl
<< " vec3 rootExp = sqrt((1.0 - reducedAlbedo) * 3.0);" << endl
<< " return (reducedAlbedo * 0.5) * (1+exp(-rootExp*(4.0/3.0 * " << endl
<< " " << evalName << "_A" << "))) * exp(-rootExp) * 0.31831 * cosThetaO;" << endl
<< "}" << endl
<< endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " return " << evalName << "(uv, wi, wo);" << endl
<< " return " << evalName << "(uv, wi, wo);" << endl
<< "}" << endl;
}

View File

@ -84,7 +84,7 @@ MTS_NAMESPACE_BEGIN
* Note that this model does not account for light that undergoes multiple
* scattering events within the layer. This leads to energy loss,
* particularly at grazing angles, which can be seen in the left-hand image of
* \figref{hk-example}. A solution is to use the \pluginref{chkms} plugin,
* \figref{hk-example}. A solution is to use the \pluginref{sssbrdf} plugin,
* which adds an approximate multiple scattering component.
*
* \begin{xml}[caption=A thin dielectric layer with measured ketchup scattering parameters, label=lst:hk-coated]
@ -111,7 +111,7 @@ class HanrahanKrueger : public BSDF {
public:
HanrahanKrueger(const Properties &props) : BSDF(props) {
Spectrum sigmaS, sigmaA;
lookupMaterial(props, sigmaS, sigmaA);
lookupMaterial(props, sigmaS, sigmaA, NULL, false);
/* Scattering coefficient of the layer */
m_sigmaS = new ConstantSpectrumTexture(
@ -140,6 +140,9 @@ public:
m_phase = static_cast<PhaseFunction *> (PluginManager::getInstance()->
createObject(MTS_CLASS(PhaseFunction), Properties("isotropic")));
if ((m_sigmaS->getMaximum()+m_sigmaA->getMaximum()).isZero())
Log(EError, "Please specify nonzero sigmaS/sigmaA-values!");
m_components.clear();
m_components.push_back(EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler);
m_components.push_back(EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler);
@ -361,9 +364,15 @@ public:
if (cClass->derivesFrom(MTS_CLASS(PhaseFunction))) {
Assert(m_phase == NULL);
m_phase = static_cast<PhaseFunction *>(child);
} else if (cClass->derivesFrom(MTS_CLASS(Texture))) {
if (name == "sigmaS")
m_sigmaS = static_cast<Texture *>(child);
else if (name == "sigmaA")
m_sigmaA = static_cast<Texture *>(child);
else
BSDF::addChild(name, child);
} else {
Log(EError, "Invalid child node! (\"%s\")",
cClass->getName().c_str());
BSDF::addChild(name, child);
}
}

View File

@ -92,13 +92,24 @@ static Float lookupIOR(const std::string &name) {
return 0.0f;
}
static Float lookupIOR(const Properties &props, const std::string &paramName, const std::string &defaultValue) {
inline Float lookupIOR(const Properties &props, const std::string &paramName, const std::string &defaultValue) {
if (props.hasProperty(paramName) && props.getType(paramName) == Properties::EFloat)
return props.getFloat(paramName);
else
return lookupIOR(props.getString(paramName, defaultValue));
}
inline Float lookupIOR(const Properties &props, const std::string &paramName, Float defaultValue) {
if (props.hasProperty(paramName)) {
if (props.getType(paramName) == Properties::EFloat)
return props.getFloat(paramName);
else
return lookupIOR(props.getString(paramName));
} else {
return defaultValue;
}
}
MTS_NAMESPACE_END
#endif /* __IOR_DATA_H */

View File

@ -19,7 +19,6 @@
#include <mitsuba/render/bsdf.h>
#include <mitsuba/render/sampler.h>
#include <mitsuba/render/phase.h>
#include <mitsuba/render/medium.h>
#include <mitsuba/core/plugin.h>
#include <mitsuba/hw/basicshader.h>
#include "../medium/materials.h"
@ -32,43 +31,78 @@ MTS_NAMESPACE_BEGIN
* \parameter{material}{\String}{Name of a material preset, see
* \tblref{medium-coefficients}. \default{\texttt{skin1}}}
* \parameter{sigmaS}{\Spectrum\Or\Texture}{Specifies the scattering coefficient
* of the scattering layer. \default{based on \code{material}}}
* of the layer. \default{based on \code{material}}}
* \parameter{sigmaA}{\Spectrum\Or\Texture}{Specifies the absorption coefficient
* of the scattering layer. \default{based on \code{material}}}
* of the layer. \default{based on \code{material}}}
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
* numerically or using a known material name. \default{based on \code{material}}}
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy
* --- see the \pluginref{hg} plugin for details\default{0}}
* --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}}
* }
*
* This plugin implements a BRDF scattering model that emulates interactions
* with a participating medium embedded within a dielectric layer. By
* with a participating medium embedded inside a smooth dielectric layer. By
* approximating these events using a BRDF, any scattered illumination
* is assumed to exit the material directly at the original point of incidence.
* is assumed to exit the material \emph{directly} at the original point of incidence.
* To account for internal light transport with \emph{different} incident
* and exitant positions, please refer to Sections~\ref{sec:media}
* and \ref{sec:subsurface}.
*
* Internally, the model is implemented by instantiating a
* Hanrahan-Krueger BSDF for single scattering together with an approximate multiple
* scattering component based on Jensen's \cite{Jensen2001Practical} integrated
* dipole BRDF. These are then embedded into a dielectric layer using
* the \pluginref{coating} plugin.
* Internally, the model is implemented by instantiating a Hanrahan-Krueger
* BSDF for single scattering in an infinitely thick layer together with
* an approximate multiple scattering component based on Jensen's
* \cite{Jensen2001Practical} integrated dipole BRDF. These are then
* embedded into a dielectric layer using the \pluginref{coating} plugin.
* This yields a very convenient parameterization of a scattering model
* that roughly behaves like a coated diffuse material, but expressed
* in terms of the scattering and absorption coefficients \code{sigmaS}
* and \code{sigmaA}.
*/
class SSSBRDF : public BSDF {
public:
SSSBRDF(const Properties &props)
: BSDF(props), m_configured(false) {
Spectrum sigmaS, sigmaA; // ignored here
Float eta;
lookupMaterial(props, sigmaS, sigmaA, &eta, false);
Float g = props.getFloat("g", 0.0f);
Properties hgProps("hg");
hgProps.setFloat("g", g);
ref<PhaseFunction> hg = static_cast<PhaseFunction *> (
PluginManager::getInstance()->createObject(
MTS_CLASS(PhaseFunction), hgProps));
Properties hkProps(props);
hkProps.setPluginName("hk");
hkProps.setFloat("thickness", std::numeric_limits<Float>::infinity());
m_hk = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), hkProps));
m_hk->addChild("", hg);
Properties coatingProps(props);
coatingProps.setPluginName("coating");
if (!props.hasProperty("intIOR"))
coatingProps.setFloat("intIOR", eta);
m_coating = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), coatingProps));
Properties dipoleProps(props);
dipoleProps.setPluginName("dipolebrdf");
m_dipole = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), dipoleProps));
Properties mixtureProps("mixture");
mixtureProps.setString("weights", "1.0, 1.0");
mixtureProps.setBoolean("ensureEnergyConservation", false);
m_mixture = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), mixtureProps));
props.markQueried("material");
props.markQueried("sigmaS");
props.markQueried("sigmaA");
@ -78,19 +112,24 @@ public:
SSSBRDF(Stream *stream, InstanceManager *manager)
: BSDF(stream, manager), m_configured(true) {
m_hk = static_cast<BSDF *>(manager->getInstance(stream));
m_coating = static_cast<BSDF *>(manager->getInstance(stream));
m_hk = static_cast<BSDF *>(manager->getInstance(stream));
m_dipole = static_cast<BSDF *>(manager->getInstance(stream));
}
void configure() {
if (!m_configured) {
m_configured = true;
m_hk->configure();
m_coating->addChild("", m_hk);
m_dipole->configure();
m_mixture->addChild("", m_hk);
m_mixture->addChild("", m_dipole);
m_mixture->configure();
m_coating->addChild("", m_mixture);
m_coating->configure();
m_components.clear();
for (size_t i=0; i<m_coating->getComponentCount(); ++i)
for (int i=0; i<m_coating->getComponentCount(); ++i)
m_components.push_back(m_coating->getType(i));
BSDF::configure();
}
@ -118,33 +157,39 @@ public:
void serialize(Stream *stream, InstanceManager *manager) const {
BSDF::serialize(stream, manager);
manager->serialize(stream, m_hk.get());
manager->serialize(stream, m_coating.get());
manager->serialize(stream, m_hk.get());
manager->serialize(stream, m_dipole.get());
}
void addChild(const std::string &name, ConfigurableObject *child) {
const Class *cClass = child->getClass();
if (cClass->derivesFrom(MTS_CLASS(PhaseFunction))) {
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_hk->addChild(name, child);
m_dipole->addChild(name, child);
} else {
Log(EError, "Invalid child node! (\"%s\")",
cClass->getName().c_str());
BSDF::addChild(name, child);
}
}
Shader *createShader(Renderer *renderer) const {
return m_coating->createShader(renderer);
}
std::string toString() const {
std::ostringstream oss;
oss << "SSSBRDF[" << endl
<< " coating = " << indent(m_coating->toString()) << endl
<< " name = \"" << m_name << "\"" << endl
<< " nested = " << indent(m_coating->toString()) << endl
<< "]";
return oss.str();
}
MTS_DECLARE_CLASS()
private:
ref<BSDF> m_coating, m_hk;
ref<BSDF> m_coating;
ref<BSDF> m_dipole;
ref<BSDF> m_hk;
ref<BSDF> m_mixture;
bool m_configured;
};

View File

@ -396,7 +396,7 @@ std::string Properties::toString() const {
void Properties::markQueried(const std::string &name) const {
std::map<std::string, Element>::const_iterator it = m_elements.find(name);
if (it == m_elements.end())
SLog(EError, "Could not find parameter \"%s\"!", name.c_str());
return;
it->second.queried = true;
}

View File

@ -48,7 +48,7 @@ static MaterialEntry materialData[] = {
{ NULL, { 0.00, 0.00, 0.00 }, { 0.00, 0.00, 0.00 }, 0.0 }
};
static void lookupMaterial(const Properties &props, Spectrum &sigmaS, Spectrum &sigmaA, Float *eta = NULL) {
static void lookupMaterial(const Properties &props, Spectrum &sigmaS, Spectrum &sigmaA, Float *eta = NULL, bool requireValues = true) {
bool manual = (props.hasProperty("sigmaS") || props.hasProperty("sigmaA"));
bool preset = props.hasProperty("material");
@ -62,8 +62,13 @@ static void lookupMaterial(const Properties &props, Spectrum &sigmaS, Spectrum &
Float densityMultiplier = props.getFloat("densityMultiplier", 1.0f);
if (manual) {
sigmaS = props.getSpectrum("sigmaS") * densityMultiplier;
sigmaA = props.getSpectrum("sigmaA") * densityMultiplier;
if (requireValues) {
sigmaS = props.getSpectrum("sigmaS") * densityMultiplier;
sigmaA = props.getSpectrum("sigmaA") * densityMultiplier;
} else {
sigmaS = props.getSpectrum("sigmaS", Spectrum(0.0f)) * densityMultiplier;
sigmaA = props.getSpectrum("sigmaA", Spectrum(0.0f)) * densityMultiplier;
}
if (eta)
*eta = props.getFloat("eta", 1.3f);
} else {

View File

@ -76,7 +76,12 @@ public:
}
std::string toString() const {
return "Checkerboard[]";
std::ostringstream oss;
oss << "Checkerboard[" << endl
<< " darkColor = " << m_darkColor.toString() << "," << endl
<< " brightColor = " << m_brightColor.toString() << endl
<< "]";
return oss.str();
}
Shader *createShader(Renderer *renderer) const;