renamed the mixture BSDF, added support for reparameterizing the HK & SSBRDF plugins in terms of albedo

metadata
Wenzel Jakob 2011-08-13 12:04:28 -04:00
parent cd69e50a73
commit bc16235f6f
10 changed files with 417 additions and 27 deletions

View File

@ -205,7 +205,7 @@
<!-- Update the name of the composite plugin -->
<xsl:template match="bsdf[@type='composite']/@type">
<xsl:attribute name="type">mixture</xsl:attribute>
<xsl:attribute name="type">mixturebsdf</xsl:attribute>
</xsl:template>
<!-- Update the name of the exrtexture plugin -->

View File

@ -126,6 +126,159 @@ protected:
Float m_value;
};
/**
* \brief Componentwise addition of two textures
*
* Includes a \ref Shader implementation for hardware rendering
*/
class MTS_EXPORT_HW SpectrumAdditionTexture : public Texture {
public:
inline SpectrumAdditionTexture(const Texture *a, const Texture *b)
: Texture(Properties()), m_a(a), m_b(b) { }
SpectrumAdditionTexture(Stream *stream, InstanceManager *manager);
inline Spectrum getValue(const Intersection &its) const {
return m_a->getValue(its) + m_b->getValue(its);
}
inline Spectrum getAverage() const {
return m_a->getAverage() + m_b->getAverage();
}
inline Spectrum getMaximum() const {
SLog(EError, "SpectrumAdditionTexture::getMaximum() -- information unavailable!");
return Spectrum(0.0f);
}
inline bool isConstant() const {
return m_a->isConstant() && m_b->isConstant();
}
inline std::string toString() const {
std::ostringstream oss;
oss << "SpectrumAdditionTexture[" << endl
<< " a = " << indent(m_a->toString()) << "," << endl
<< " b = " << indent(m_a->toString()) << endl
<< "]";
return oss.str();
}
inline bool usesRayDifferentials() const {
return m_a->usesRayDifferentials() || m_b->usesRayDifferentials();
}
Shader *createShader(Renderer *renderer) const;
void serialize(Stream *stream, InstanceManager *manager) const;
MTS_DECLARE_CLASS()
protected:
ref<const Texture> m_a, m_b;
};
/**
* \brief Componentwise subtraction of two textures
*
* Includes a \ref Shader implementation for hardware rendering
*/
class MTS_EXPORT_HW SpectrumSubtractionTexture : public Texture {
public:
inline SpectrumSubtractionTexture(const Texture *a, const Texture *b)
: Texture(Properties()), m_a(a), m_b(b) { }
SpectrumSubtractionTexture(Stream *stream, InstanceManager *manager);
inline Spectrum getValue(const Intersection &its) const {
return m_a->getValue(its) - m_b->getValue(its);
}
inline Spectrum getAverage() const {
return m_a->getAverage() - m_b->getAverage();
}
inline Spectrum getMaximum() const {
SLog(EError, "SpectrumSubtractionTexture::getMaximum() -- information unavailable!");
return Spectrum(0.0f);
}
inline bool isConstant() const {
return m_a->isConstant() && m_b->isConstant();
}
inline std::string toString() const {
std::ostringstream oss;
oss << "SpectrumSubtractionTexture[" << endl
<< " a = " << indent(m_a->toString()) << "," << endl
<< " b = " << indent(m_b->toString()) << endl
<< "]";
return oss.str();
}
inline bool usesRayDifferentials() const {
return m_a->usesRayDifferentials() || m_b->usesRayDifferentials();
}
Shader *createShader(Renderer *renderer) const;
void serialize(Stream *stream, InstanceManager *manager) const;
MTS_DECLARE_CLASS()
protected:
ref<const Texture> m_a, m_b;
};
/**
* \brief Componentwise product of two textures
*
* Includes a \ref Shader implementation for hardware rendering
*/
class MTS_EXPORT_HW SpectrumProductTexture : public Texture {
public:
inline SpectrumProductTexture(const Texture *a, const Texture *b)
: Texture(Properties()), m_a(a), m_b(b) { }
SpectrumProductTexture(Stream *stream, InstanceManager *manager);
inline Spectrum getValue(const Intersection &its) const {
return m_a->getValue(its) * m_b->getValue(its);
}
inline Spectrum getAverage() const {
SLog(EError, "SpectrumProductTexture::getAverage() -- information unavailable!");
return Spectrum(0.0f);
}
inline Spectrum getMaximum() const {
return m_a->getMaximum() * m_b->getMaximum();
}
inline bool isConstant() const {
return m_a->isConstant() && m_b->isConstant();
}
inline std::string toString() const {
std::ostringstream oss;
oss << "SpectrumProductTexture[" << endl
<< " a = " << indent(m_a->toString()) << "," << endl
<< " b = " << indent(m_b->toString()) << endl
<< "]";
return oss.str();
}
inline bool usesRayDifferentials() const {
return m_a->usesRayDifferentials() || m_b->usesRayDifferentials();
}
Shader *createShader(Renderer *renderer) const;
void serialize(Stream *stream, InstanceManager *manager) const;
MTS_DECLARE_CLASS()
protected:
ref<const Texture> m_a, m_b;
};
MTS_NAMESPACE_END
#endif /* __BASIC_SHADER_H */

View File

@ -13,7 +13,7 @@ plugins += env.SharedLibrary('roughplastic', ['roughplastic.cpp'])
# Materials that act as modifiers
plugins += env.SharedLibrary('twosided', ['twosided.cpp'])
plugins += env.SharedLibrary('mask', ['mask.cpp'])
plugins += env.SharedLibrary('mixture', ['mixture.cpp'])
plugins += env.SharedLibrary('mixturebsdf', ['mixturebsdf.cpp'])
plugins += env.SharedLibrary('coating', ['coating.cpp'])
plugins += env.SharedLibrary('bump', ['bump.cpp'])

View File

@ -38,9 +38,10 @@ MTS_NAMESPACE_BEGIN
*
* This BSDF models a non-reflective material, where any entering light loses
* its directionality and is diffusely scattered from the other side. This
* model can be combined\footnote{For instance using the \pluginref{mixture}
* plugin.} with a surface reflection model to describe translucent substances
* that have internal multiple scattering processes (e.g. plant leaves).
* model can be combined\footnote{For instance using the
* \pluginref{mixturebsdf} plugin.} with a surface reflection model to
* describe translucent substances that have internal multiple scattering
* processes (e.g. plant leaves).
*/
class DiffuseTransmitter : public BSDF {
public:

View File

@ -33,7 +33,7 @@ public:
: BSDF(props) {
Spectrum sigmaS, sigmaA;
Float eta;
Float eta = 1.33f;
lookupMaterial(props, sigmaS, sigmaA, &eta, false);
/* Specifies the internal index of refraction at the interface */
@ -85,10 +85,23 @@ public:
}
void configure() {
if (m_sigmaT != NULL || m_albedo != NULL) {
/* Support for the alternative scattering/absorption
* coefficient parameter passing convention */
if (m_sigmaT == NULL || m_albedo == NULL)
SLog(EError, "Please provide *both* sigmaT & albedo!");
m_sigmaS = new SpectrumProductTexture(m_sigmaT, m_albedo);
m_sigmaA = new SpectrumSubtractionTexture(m_sigmaT, m_sigmaS);
m_sigmaT = NULL;
m_albedo = NULL;
}
m_components.clear();
m_components.push_back(EDiffuseReflection | EFrontSide
| (m_sigmaS->isConstant() && m_sigmaA->isConstant() ? 0 : ESpatiallyVarying));
m_usesRayDifferentials = m_sigmaS->usesRayDifferentials() || m_sigmaA->usesRayDifferentials();
m_usesRayDifferentials = m_sigmaS->usesRayDifferentials()
|| m_sigmaA->usesRayDifferentials();
if ((m_sigmaS->getMaximum()+m_sigmaA->getMaximum()).isZero())
Log(EError, "Please specify nonzero sigmaS/sigmaA-values!");
@ -176,6 +189,10 @@ public:
m_sigmaS = static_cast<Texture *>(child);
else if (name == "sigmaA")
m_sigmaA = static_cast<Texture *>(child);
else if (name == "sigmaT")
m_sigmaT = static_cast<Texture *>(child);
else if (name == "albedo")
m_albedo = static_cast<Texture *>(child);
else
BSDF::addChild(name, child);
} else {
@ -212,6 +229,9 @@ public:
private:
Float m_intIOR, m_extIOR, m_g, m_A;
ref<Texture> m_sigmaS, m_sigmaA;
/* Temporary fields */
ref<Texture> m_sigmaT;
ref<Texture> m_albedo;
};
// ================ Hardware shader implementation ================

View File

@ -36,6 +36,12 @@ MTS_NAMESPACE_BEGIN
* of the internal layer. \default{based on \code{material}}}
* \parameter{sigmaA}{\Spectrum\Or\Texture}{Specifies the absorption coefficient
* of the internal layer. \default{based on \code{material}}}
* \parameter{sigmaT \& albedo}{\Spectrum\Or\Texture}{
* Optional: Alternatively, the scattering and absorption coefficients may also be
* specified using the extinction coefficient \code{sigmaT} and the
* single-scattering albedo. Note that only one of the parameter passing
* conventions can be used at a time (i.e. use either \code{sigmaS\&sigmaA}
* \emph{or} \code{sigmaT\&albedo})}
* \parameter{thickness}{\Float}{Denotes the thickness of the layer.
* (should be specified in inverse units of \code{sigmaA} and \code{sigmaS})\default{1}}
* \parameter{\Unnamed}{\Phase}{A nested phase function instance that represents
@ -45,12 +51,11 @@ MTS_NAMESPACE_BEGIN
* \renderings{
* \rendering{An index-matched scattering layer with parameters $\sigma_s=2$, $\sigma_a=0.1$, thickness$=0.1$}{bsdf_hk_1}
* \rendering{Example of the HK model with a dielectric coating (and the \code{ketchup} material preset, see \lstref{hk-coated})}{bsdf_hk_2}
* \vspace{-3mm}
* \caption{
* \label{fig:hk-example}
* Renderings using the uncoated and coated form of the Hanrahan-Krueger model.
* }
* \vspace{1mm}
* \vspace{3mm}
* }
*
* This plugin provides an implementation of the Hanrahan-Krueger BSDF
@ -62,7 +67,7 @@ MTS_NAMESPACE_BEGIN
* This BSDF requires a phase function to model scattering interactions within the
* random medium. When no phase function is explicitly specified, it uses an
* isotropic one ($g=0$) by default. A sample usage for instantiating the
* plugin is given below:
* plugin is given on the next page:\newpage
* \begin{xml}
* <bsdf type="hk">
* <spectrum name="sigmaS" value="2"/>
@ -123,7 +128,6 @@ public:
/* Slab thickness in inverse units of sigmaS and sigmaA */
m_thickness = props.getFloat("thickness", 1);
}
HanrahanKrueger(Stream *stream, InstanceManager *manager)
@ -140,13 +144,29 @@ public:
m_phase = static_cast<PhaseFunction *> (PluginManager::getInstance()->
createObject(MTS_CLASS(PhaseFunction), Properties("isotropic")));
if (m_sigmaT != NULL || m_albedo != NULL) {
/* Support for the alternative scattering/absorption
* coefficient parameter passing convention */
if (m_sigmaT == NULL || m_albedo == NULL)
SLog(EError, "Please provide *both* sigmaT & albedo!");
m_sigmaS = new SpectrumProductTexture(m_sigmaT, m_albedo);
m_sigmaA = new SpectrumSubtractionTexture(m_sigmaT, m_sigmaS);
m_sigmaT = NULL;
m_albedo = NULL;
}
if ((m_sigmaS->getMaximum()+m_sigmaA->getMaximum()).isZero())
Log(EError, "Please specify nonzero sigmaS/sigmaA-values!");
int extraFlags = m_sigmaS->isConstant() && m_sigmaA->isConstant() ? 0 : ESpatiallyVarying;
m_components.clear();
m_components.push_back(EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler);
m_components.push_back(EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler);
m_components.push_back(EDeltaTransmission | EFrontSide | EBackSide | ECanUseSampler);
m_components.push_back(EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler | extraFlags);
m_components.push_back(EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler | extraFlags);
m_components.push_back(EDeltaTransmission | EFrontSide | EBackSide | ECanUseSampler | extraFlags);
m_usesRayDifferentials = m_sigmaS->usesRayDifferentials()
|| m_sigmaA->usesRayDifferentials();
BSDF::configure();
}
@ -369,6 +389,10 @@ public:
m_sigmaS = static_cast<Texture *>(child);
else if (name == "sigmaA")
m_sigmaA = static_cast<Texture *>(child);
else if (name == "sigmaT")
m_sigmaT = static_cast<Texture *>(child);
else if (name == "albedo")
m_albedo = static_cast<Texture *>(child);
else
BSDF::addChild(name, child);
} else {
@ -395,6 +419,9 @@ private:
ref<Texture> m_sigmaS;
ref<Texture> m_sigmaA;
Float m_thickness;
/* Temporary fields */
ref<Texture> m_sigmaT;
ref<Texture> m_albedo;
};

View File

@ -22,7 +22,7 @@
MTS_NAMESPACE_BEGIN
/*! \plugin{mixture}{Mixture material}
/*! \plugin{mixturebsdf}{Mixture material}
*
* \parameters{
* \parameter{weights}{\String}{A comma-separated list of BSDF weights}
@ -46,7 +46,7 @@ MTS_NAMESPACE_BEGIN
* \begin{xml}[caption={A material definition for a mixture of 70% smooth
* chromium, 20% of a greenish rough diffuse material (and 10% absorption)},
* label=lst:mixture-example]
* <bsdf type="mixture">
* <bsdf type="mixturebsdf">
* <string name="weights" value="0.7, 0.2"/>
*
* <bsdf type="conductor">

View File

@ -34,6 +34,12 @@ MTS_NAMESPACE_BEGIN
* of the layer. \default{based on \code{material}}}
* \parameter{sigmaA}{\Spectrum\Or\Texture}{Specifies the absorption coefficient
* of the layer. \default{based on \code{material}}}
* \parameter{sigmaT \& albedo}{\Spectrum\Or\Texture}{
* Optional: Alternatively, the scattering and absorption coefficients may also be
* specified using the extinction coefficient \code{sigmaT} and the
* single-scattering albedo. Note that only one of the parameter passing
* conventions can be used at a time (i.e. use either \code{sigmaS\&sigmaA}
* \emph{or} \code{sigmaT\&albedo})}
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
* numerically or using a known material name. \default{based on \code{material}}}
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
@ -97,7 +103,7 @@ public:
m_dipole = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), dipoleProps));
Properties mixtureProps("mixture");
Properties mixtureProps("mixturebsdf");
mixtureProps.setString("weights", "1.0, 1.0");
mixtureProps.setBoolean("ensureEnergyConservation", false);
m_mixture = static_cast<BSDF *> (PluginManager::getInstance()->
@ -106,6 +112,8 @@ public:
props.markQueried("material");
props.markQueried("sigmaS");
props.markQueried("sigmaA");
props.markQueried("coating");
props.markQueried("sigmaT");
props.markQueried("intIOR");
props.markQueried("extIOR");
}

View File

@ -41,6 +41,42 @@ void ConstantFloatTexture::serialize(Stream *stream, InstanceManager *manager) c
stream->writeFloat(m_value);
}
SpectrumProductTexture::SpectrumProductTexture(Stream *stream, InstanceManager *manager)
: Texture(stream, manager) {
m_a = static_cast<Texture *>(manager->getInstance(stream));
m_b = static_cast<Texture *>(manager->getInstance(stream));
}
void SpectrumProductTexture::serialize(Stream *stream, InstanceManager *manager) const {
Texture::serialize(stream, manager);
manager->serialize(stream, m_a.get());
manager->serialize(stream, m_b.get());
}
SpectrumAdditionTexture::SpectrumAdditionTexture(Stream *stream, InstanceManager *manager)
: Texture(stream, manager) {
m_a = static_cast<Texture *>(manager->getInstance(stream));
m_b = static_cast<Texture *>(manager->getInstance(stream));
}
void SpectrumAdditionTexture::serialize(Stream *stream, InstanceManager *manager) const {
Texture::serialize(stream, manager);
manager->serialize(stream, m_a.get());
manager->serialize(stream, m_b.get());
}
SpectrumSubtractionTexture::SpectrumSubtractionTexture(Stream *stream, InstanceManager *manager)
: Texture(stream, manager) {
m_a = static_cast<Texture *>(manager->getInstance(stream));
m_b = static_cast<Texture *>(manager->getInstance(stream));
}
void SpectrumSubtractionTexture::serialize(Stream *stream, InstanceManager *manager) const {
Texture::serialize(stream, manager);
manager->serialize(stream, m_a.get());
manager->serialize(stream, m_b.get());
}
class ConstantSpectrumTextureShader : public Shader {
public:
ConstantSpectrumTextureShader(Renderer *renderer, const Spectrum &value)
@ -99,6 +135,114 @@ private:
Float m_value;
};
class SpectrumProductTextureShader : public Shader {
public:
SpectrumProductTextureShader(Renderer *renderer, const Texture *a, const Texture *b)
: Shader(renderer, ETextureShader), m_a(a), m_b(b) {
m_aShader = renderer->registerShaderForResource(m_a.get());
m_bShader = renderer->registerShaderForResource(m_b.get());
}
bool isComplete() const {
return m_a.get() != NULL && m_b.get() != NULL;
}
void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_a.get());
renderer->unregisterShaderForResource(m_b.get());
}
void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_aShader.get());
deps.push_back(m_bShader.get());
}
void generateCode(std::ostringstream &oss,
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv) {" << endl
<< " return " << depNames[0] << "(uv) * " << depNames[1] << "(uv);" << endl
<< "}" << endl;
}
MTS_DECLARE_CLASS()
private:
ref<const Texture> m_a, m_b;
ref<Shader> m_aShader, m_bShader;
};
class SpectrumAdditionTextureShader : public Shader {
public:
SpectrumAdditionTextureShader(Renderer *renderer, const Texture *a, const Texture *b)
: Shader(renderer, ETextureShader), m_a(a), m_b(b) {
m_aShader = renderer->registerShaderForResource(m_a.get());
m_bShader = renderer->registerShaderForResource(m_b.get());
}
bool isComplete() const {
return m_a.get() != NULL && m_b.get() != NULL;
}
void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_a.get());
renderer->unregisterShaderForResource(m_b.get());
}
void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_aShader.get());
deps.push_back(m_bShader.get());
}
void generateCode(std::ostringstream &oss,
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv) {" << endl
<< " return " << depNames[0] << "(uv) + " << depNames[1] << "(uv);" << endl
<< "}" << endl;
}
MTS_DECLARE_CLASS()
private:
ref<const Texture> m_a, m_b;
ref<Shader> m_aShader, m_bShader;
};
class SpectrumSubtractionTextureShader : public Shader {
public:
SpectrumSubtractionTextureShader(Renderer *renderer, const Texture *a, const Texture *b)
: Shader(renderer, ETextureShader), m_a(a), m_b(b) {
m_aShader = renderer->registerShaderForResource(m_a.get());
m_bShader = renderer->registerShaderForResource(m_b.get());
}
bool isComplete() const {
return m_a.get() != NULL && m_b.get() != NULL;
}
void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_a.get());
renderer->unregisterShaderForResource(m_b.get());
}
void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_aShader.get());
deps.push_back(m_bShader.get());
}
void generateCode(std::ostringstream &oss,
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv) {" << endl
<< " return " << depNames[0] << "(uv) - " << depNames[1] << "(uv);" << endl
<< "}" << endl;
}
MTS_DECLARE_CLASS()
private:
ref<const Texture> m_a, m_b;
ref<Shader> m_aShader, m_bShader;
};
Shader *ConstantSpectrumTexture::createShader(Renderer *renderer) const {
return new ConstantSpectrumTextureShader(renderer, m_value);
}
@ -107,8 +251,26 @@ Shader *ConstantFloatTexture::createShader(Renderer *renderer) const {
return new ConstantFloatTextureShader(renderer, m_value);
}
Shader *SpectrumProductTexture::createShader(Renderer *renderer) const {
return new SpectrumProductTextureShader(renderer, m_a.get(), m_b.get());
}
Shader *SpectrumAdditionTexture::createShader(Renderer *renderer) const {
return new SpectrumAdditionTextureShader(renderer, m_a.get(), m_b.get());
}
Shader *SpectrumSubtractionTexture::createShader(Renderer *renderer) const {
return new SpectrumSubtractionTextureShader(renderer, m_a.get(), m_b.get());
}
MTS_IMPLEMENT_CLASS_S(ConstantSpectrumTexture, false, Texture)
MTS_IMPLEMENT_CLASS(ConstantSpectrumTextureShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(ConstantFloatTexture, false, Texture)
MTS_IMPLEMENT_CLASS(ConstantFloatTextureShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(SpectrumProductTexture, false, Texture)
MTS_IMPLEMENT_CLASS(SpectrumProductTextureShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(SpectrumAdditionTexture, false, Texture)
MTS_IMPLEMENT_CLASS(SpectrumAdditionTextureShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(SpectrumSubtractionTexture, false, Texture)
MTS_IMPLEMENT_CLASS(SpectrumSubtractionTextureShader, false, Shader)
MTS_NAMESPACE_END

View File

@ -49,12 +49,14 @@ static MaterialEntry materialData[] = {
};
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");
bool hasSigmaAS = props.hasProperty("sigmaS") || props.hasProperty("sigmaA"),
hasSigmaTAlbedo = props.hasProperty("sigmaT") || props.hasProperty("albedo"),
manual = hasSigmaAS || hasSigmaTAlbedo,
hasPreset = props.hasProperty("material");
if (manual && preset)
SLog(EError, "Please specify either a preset material, or "
"sigmaS & sigmaA (you provided both!)");
if (manual && hasPreset)
SLog(EError, "Please specify either a preset material or "
"scattering coefficients (you provided both!)");
SAssertEx(!props.hasProperty("sizeMultiplier"),
"The sizeMultiplier property was deprecated!");
@ -62,12 +64,29 @@ static void lookupMaterial(const Properties &props, Spectrum &sigmaS, Spectrum &
Float densityMultiplier = props.getFloat("densityMultiplier", 1.0f);
if (manual) {
if (requireValues) {
sigmaS = props.getSpectrum("sigmaS") * densityMultiplier;
sigmaA = props.getSpectrum("sigmaA") * densityMultiplier;
if (hasSigmaAS && hasSigmaTAlbedo) {
SLog(EError, "You can either specify sigmaS & sigmaA *or* "
"sigmaT & albedo, but no other combinations!");
}
if (hasSigmaAS) {
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;
}
} else {
sigmaS = props.getSpectrum("sigmaS", Spectrum(0.0f)) * densityMultiplier;
sigmaA = props.getSpectrum("sigmaA", Spectrum(0.0f)) * densityMultiplier;
Spectrum albedo, sigmaT;
if (requireValues) {
albedo = props.getSpectrum("albedo");
sigmaT = props.getSpectrum("sigmaT") * densityMultiplier;
} else {
albedo = props.getSpectrum("albedo", Spectrum(0.0f));
sigmaT = props.getSpectrum("sigmaT", Spectrum(0.0f)) * densityMultiplier;
}
sigmaS = albedo * sigmaT;
sigmaA = sigmaT - sigmaS;
}
if (eta)
*eta = props.getFloat("eta", 1.3f);