fixed rough coating, renamed sssbrdf -> rmbrdf to avoid further confusion

metadata
Wenzel Jakob 2011-09-17 22:44:07 -04:00
parent 45d3be5ec5
commit 1a731394c8
7 changed files with 107 additions and 62 deletions

BIN
doc/images/bsdf_sssbrdf.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

View File

@ -24,7 +24,7 @@ plugins += env.SharedLibrary('phong', ['phong.cpp'])
plugins += env.SharedLibrary('difftrans', ['difftrans.cpp']) plugins += env.SharedLibrary('difftrans', ['difftrans.cpp'])
plugins += env.SharedLibrary('hk', ['hk.cpp']) plugins += env.SharedLibrary('hk', ['hk.cpp'])
plugins += env.SharedLibrary('dipolebrdf', ['dipolebrdf.cpp']) plugins += env.SharedLibrary('dipolebrdf', ['dipolebrdf.cpp'])
plugins += env.SharedLibrary('sssbrdf', ['sssbrdf.cpp']) plugins += env.SharedLibrary('rmbrdf', ['rmbrdf.cpp'])
# The Irawan-Marschner plugin uses a Boost::Spirit parser, which makes it # The Irawan-Marschner plugin uses a Boost::Spirit parser, which makes it
# pretty heavy stuff to compile. Go easy on the compiler flags: # pretty heavy stuff to compile. Go easy on the compiler flags:

View File

@ -181,7 +181,7 @@ public:
Log(EError, "Only a single nested BRDF can be added!"); Log(EError, "Only a single nested BRDF can be added!");
m_nested = static_cast<BSDF *>(child); m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") {
m_sigmaA = static_cast<Texture *>(m_sigmaA); m_sigmaA = static_cast<Texture *>(child);
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }

View File

@ -25,7 +25,7 @@
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
/*!\plugin{sssbrdf}{Subsurface scattering BRDF} /*!\plugin{rmbrdf}{Random medium BRDF}
* *
* \parameters{ * \parameters{
* \parameter{material}{\String}{Name of a material preset, see * \parameter{material}{\String}{Name of a material preset, see
@ -46,20 +46,29 @@ MTS_NAMESPACE_BEGIN
* numerically or using a known material name. \default{\texttt{air} / 1.000277}} * numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy * \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy
* --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}} * --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}}
* \parameter{alpha}{\Float}{ * \parameter{alpha}{\Float\Or\Texture}{
* Specifies the roughness of the unresolved surface micro-geometry. * Specifies the roughness of the unresolved surface micro-geometry.
* \default{0.0, i.e. the surface has a smooth finish} * \default{0.1, i.e. the surface has a slightly rough finish}
* } * }
* } * }
* *
* \renderings{
* \rendering{Rendering using the whole milk material preset}{bsdf_sssbrdf}
* }
*
* This plugin implements a BRDF scattering model that emulates interactions * This plugin implements a BRDF scattering model that emulates interactions
* with a participating medium embedded inside a dielectric layer. By * with a random medium embedded inside a dielectric layer. By
* approximating these events using a BRDF, any scattered illumination * approximating these events using a BRDF, any scattered illumination
* is assumed to exit the material \emph{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 * To simulate actual subsurface scattering, refer to Sections~\ref{sec:media}
* and exitant positions, please refer to Sections~\ref{sec:media}
* and \ref{sec:subsurface}. * and \ref{sec:subsurface}.
* *
* Note that renderings with this BRDF will usually look very similar to what might
* also be obtained using \pluginref{plastic}. The plugin's reason for existance
* is that can be configured using parameters that are traditionally reserved
* for participating media.
*
* \subsection*{Implementation details}
* Internally, the model is implemented by instantiating a Hanrahan-Krueger * Internally, the model is implemented by instantiating a Hanrahan-Krueger
* BSDF for single scattering in an infinitely thick layer together with * BSDF for single scattering in an infinitely thick layer together with
* an approximate multiple scattering component based on Jensen's * an approximate multiple scattering component based on Jensen's
@ -72,9 +81,9 @@ MTS_NAMESPACE_BEGIN
* in terms of the scattering and absorption coefficients \code{sigmaS} * in terms of the scattering and absorption coefficients \code{sigmaS}
* and \code{sigmaA}. * and \code{sigmaA}.
*/ */
class SSSBRDF : public BSDF { class RandomMediumBRDF : public BSDF {
public: public:
SSSBRDF(const Properties &props) RandomMediumBRDF(const Properties &props)
: BSDF(props), m_configured(false) { : BSDF(props), m_configured(false) {
Spectrum sigmaS, sigmaA; // ignored here Spectrum sigmaS, sigmaA; // ignored here
@ -85,7 +94,7 @@ public:
Properties hgProps("hg"); Properties hgProps("hg");
hgProps.setFloat("g", g); hgProps.setFloat("g", g);
Float alpha = props.getFloat("alpha", 0.0f); Float alpha = props.getFloat("alpha", 0.1f);
ref<PhaseFunction> hg = static_cast<PhaseFunction *> ( ref<PhaseFunction> hg = static_cast<PhaseFunction *> (
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(
@ -129,7 +138,7 @@ public:
props.markQueried("alpha"); props.markQueried("alpha");
} }
SSSBRDF(Stream *stream, InstanceManager *manager) RandomMediumBRDF(Stream *stream, InstanceManager *manager)
: BSDF(stream, manager), m_configured(true) { : BSDF(stream, manager), m_configured(true) {
m_coating = static_cast<BSDF *>(manager->getInstance(stream)); m_coating = static_cast<BSDF *>(manager->getInstance(stream));
m_hk = static_cast<BSDF *>(manager->getInstance(stream)); m_hk = static_cast<BSDF *>(manager->getInstance(stream));
@ -194,8 +203,14 @@ public:
void addChild(const std::string &name, ConfigurableObject *child) { void addChild(const std::string &name, ConfigurableObject *child) {
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) { if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_hk->addChild(name, child); if (name == "sigmaS" || name == "sigmaA" || name == "sigmaT" || name == "albedo") {
m_dipole->addChild(name, child); m_hk->addChild(name, child);
m_dipole->addChild(name, child);
} else if (name == "alpha") {
m_coating->addChild(name, child);
} else {
BSDF::addChild(name, child);
}
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }
@ -207,7 +222,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "SSSBRDF[" << endl oss << "RandomMediumBRDF[" << endl
<< " name = \"" << m_name << "\"" << endl << " name = \"" << m_name << "\"" << endl
<< " nested = " << indent(m_coating->toString()) << endl << " nested = " << indent(m_coating->toString()) << endl
<< "]"; << "]";
@ -223,6 +238,6 @@ private:
bool m_configured; bool m_configured;
}; };
MTS_IMPLEMENT_CLASS_S(SSSBRDF, false, BSDF) MTS_IMPLEMENT_CLASS_S(RandomMediumBRDF, false, BSDF)
MTS_EXPORT_PLUGIN(SSSBRDF, "Subsurface scattering BRDF"); MTS_EXPORT_PLUGIN(RandomMediumBRDF, "Random medium BRDF");
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -45,7 +45,7 @@ MTS_NAMESPACE_BEGIN
* \vspace{-4mm} * \vspace{-4mm}
* \end{enumerate} * \end{enumerate}
* } * }
* \parameter{alpha}{\Float}{ * \parameter{alpha}{\Float\Or\Texture}{
* Specifies the roughness of the unresolved surface micro-geometry. * Specifies the roughness of the unresolved surface micro-geometry.
* When the Beckmann distribution is used, this parameter is equal to the * When the Beckmann distribution is used, this parameter is equal to the
* \emph{root mean square} (RMS) slope of the microfacets. * \emph{root mean square} (RMS) slope of the microfacets.
@ -121,7 +121,7 @@ public:
Log(EError, "The 'roughplastic' plugin currently does not support " Log(EError, "The 'roughplastic' plugin currently does not support "
"anisotropic microfacet distributions!"); "anisotropic microfacet distributions!");
m_alpha = m_distribution.transformRoughness( m_alpha = new ConstantFloatTexture(
props.getFloat("alpha", 0.1f)); props.getFloat("alpha", 0.1f));
m_specularSamplingWeight = 0.0f; m_specularSamplingWeight = 0.0f;
@ -134,7 +134,7 @@ public:
); );
m_nested = static_cast<BSDF *>(manager->getInstance(stream)); m_nested = static_cast<BSDF *>(manager->getInstance(stream));
m_sigmaA = static_cast<Texture *>(manager->getInstance(stream)); m_sigmaA = static_cast<Texture *>(manager->getInstance(stream));
m_alpha = stream->readFloat(); m_alpha = static_cast<Texture *>(manager->getInstance(stream));
m_intIOR = stream->readFloat(); m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat(); m_extIOR = stream->readFloat();
m_thickness = stream->readFloat(); m_thickness = stream->readFloat();
@ -144,7 +144,7 @@ public:
void configure() { void configure() {
unsigned int extraFlags = 0; unsigned int extraFlags = 0;
if (!m_sigmaA->isConstant()) if (!m_sigmaA->isConstant() || !m_alpha->isConstant())
extraFlags |= ESpatiallyVarying; extraFlags |= ESpatiallyVarying;
m_components.clear(); m_components.clear();
@ -154,7 +154,8 @@ public:
m_components.push_back(EGlossyReflection | EFrontSide | EBackSide); m_components.push_back(EGlossyReflection | EFrontSide | EBackSide);
m_usesRayDifferentials = m_nested->usesRayDifferentials() m_usesRayDifferentials = m_nested->usesRayDifferentials()
|| m_sigmaA->usesRayDifferentials(); || m_sigmaA->usesRayDifferentials()
|| m_alpha->usesRayDifferentials();
/* Compute weights that further steer samples towards /* Compute weights that further steer samples towards
the specular or nested components */ the specular or nested components */
@ -169,8 +170,18 @@ public:
m_roughTransmittance = new RoughTransmittance( m_roughTransmittance = new RoughTransmittance(
m_distribution.getType()); m_distribution.getType());
Float eta = m_intIOR / m_extIOR;
m_roughTransmittance->checkEta(eta);
m_roughTransmittance->checkAlpha(m_alpha->getMinimum().average());
m_roughTransmittance->checkAlpha(m_alpha->getMaximum().average());
/* Reduce the rough transmittance data to a 2D slice */ /* Reduce the rough transmittance data to a 2D slice */
m_roughTransmittance->setEta(m_intIOR / m_extIOR); m_roughTransmittance->setEta(eta);
/* If possible, even reduce it to a 1D slice */
if (m_alpha->isConstant())
m_roughTransmittance->setAlpha(
m_alpha->getValue(Intersection()).average());
} }
BSDF::configure(); BSDF::configure();
@ -223,7 +234,11 @@ public:
bool hasSpecular = (bRec.typeMask & EGlossyReflection) bool hasSpecular = (bRec.typeMask & EGlossyReflection)
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1) && (bRec.component == -1 || bRec.component == (int) m_components.size()-1)
&& measure == ESolidAngle; && measure == ESolidAngle;
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
Spectrum result(0.0f); Spectrum result(0.0f);
if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) { if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
/* Calculate the reflection half-vector */ /* Calculate the reflection half-vector */
@ -231,13 +246,13 @@ public:
* signum(Frame::cosTheta(bRec.wo)); * signum(Frame::cosTheta(bRec.wo));
/* Evaluate the microsurface normal distribution */ /* Evaluate the microsurface normal distribution */
const Float D = m_distribution.eval(H, m_alpha); const Float D = m_distribution.eval(H, alphaT);
/* Fresnel term */ /* Fresnel term */
const Float F = fresnel(absDot(bRec.wi, H), m_extIOR, m_intIOR); const Float F = fresnel(absDot(bRec.wi, H), m_extIOR, m_intIOR);
/* Smith's shadow-masking function */ /* Smith's shadow-masking function */
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, m_alpha); const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);
/* Calculate the specular reflection component */ /* Calculate the specular reflection component */
Float value = F * D * G / Float value = F * D * G /
@ -252,8 +267,8 @@ public:
bRecInt.wo = refractTo(EInterior, bRec.wo); bRecInt.wo = refractTo(EInterior, bRec.wo);
Spectrum nestedResult = m_nested->eval(bRecInt, measure) * Spectrum nestedResult = m_nested->eval(bRecInt, measure) *
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), m_alpha) * m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha) *
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), m_alpha); m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), alpha);
Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness; Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness;
if (!sigmaA.isZero()) if (!sigmaA.isZero())
@ -286,11 +301,15 @@ public:
const Vector H = normalize(bRec.wo+bRec.wi) const Vector H = normalize(bRec.wo+bRec.wi)
* signum(Frame::cosTheta(bRec.wo)); * signum(Frame::cosTheta(bRec.wo));
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
Float probNested, probSpecular; Float probNested, probSpecular;
if (hasSpecular && hasNested) { if (hasSpecular && hasNested) {
/* Find the probability of sampling the specular component */ /* Find the probability of sampling the specular component */
probSpecular = 1-m_roughTransmittance->eval( probSpecular = 1-m_roughTransmittance->eval(
std::abs(Frame::cosTheta(bRec.wi)), m_alpha); std::abs(Frame::cosTheta(bRec.wi)), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -308,7 +327,7 @@ public:
const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H)); const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H));
/* Evaluate the microsurface normal distribution */ /* Evaluate the microsurface normal distribution */
const Float prob = m_distribution.pdf(H, m_alpha); const Float prob = m_distribution.pdf(H, alphaT);
result = prob * dwh_dwo * probSpecular; result = prob * dwh_dwo * probSpecular;
} }
@ -341,10 +360,14 @@ public:
bool choseSpecular = hasSpecular; bool choseSpecular = hasSpecular;
Point2 sample(_sample); Point2 sample(_sample);
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
Float probSpecular; Float probSpecular;
if (hasSpecular && hasNested) { if (hasSpecular && hasNested) {
/* Find the probability of sampling the diffuse component */ /* Find the probability of sampling the diffuse component */
probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), m_alpha); probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -361,7 +384,7 @@ public:
if (choseSpecular) { if (choseSpecular) {
/* Perfect specular reflection based on the microsurface normal */ /* Perfect specular reflection based on the microsurface normal */
Normal m = m_distribution.sample(sample, m_alpha); Normal m = m_distribution.sample(sample, alphaT);
bRec.wo = reflect(bRec.wi, m); bRec.wo = reflect(bRec.wi, m);
bRec.sampledComponent = m_components.size()-1; bRec.sampledComponent = m_components.size()-1;
bRec.sampledType = EGlossyReflection; bRec.sampledType = EGlossyReflection;
@ -402,7 +425,7 @@ public:
stream->writeUInt((uint32_t) m_distribution.getType()); stream->writeUInt((uint32_t) m_distribution.getType());
manager->serialize(stream, m_nested.get()); manager->serialize(stream, m_nested.get());
manager->serialize(stream, m_sigmaA.get()); manager->serialize(stream, m_sigmaA.get());
stream->writeFloat(m_alpha); manager->serialize(stream, m_alpha.get());
stream->writeFloat(m_intIOR); stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR); stream->writeFloat(m_extIOR);
stream->writeFloat(m_thickness); stream->writeFloat(m_thickness);
@ -413,8 +436,13 @@ public:
if (m_nested != NULL) if (m_nested != NULL)
Log(EError, "Only a single nested BRDF can be added!"); Log(EError, "Only a single nested BRDF can be added!");
m_nested = static_cast<BSDF *>(child); m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_sigmaA = static_cast<Texture *>(m_sigmaA); if (name == "sigmaA")
m_sigmaA = static_cast<Texture *>(child);
else if (name == "alpha")
m_alpha = static_cast<Texture *>(child);
else
BSDF::addChild(name, child);
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }
@ -425,8 +453,8 @@ public:
oss << "RoughCoating[" << endl oss << "RoughCoating[" << endl
<< " name = \"" << getName() << "\"," << endl << " name = \"" << getName() << "\"," << endl
<< " distribution = " << m_distribution.toString() << "," << endl << " distribution = " << m_distribution.toString() << "," << endl
<< " alpha = " << m_alpha << "," << endl << " alpha = " << indent(m_alpha->toString()) << "," << endl
<< " sigmaA = " << m_sigmaA->toString() << "," << endl << " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
@ -443,8 +471,9 @@ private:
MicrofacetDistribution m_distribution; MicrofacetDistribution m_distribution;
ref<RoughTransmittance> m_roughTransmittance; ref<RoughTransmittance> m_roughTransmittance;
ref<Texture> m_sigmaA; ref<Texture> m_sigmaA;
ref<Texture> m_alpha;
ref<BSDF> m_nested; ref<BSDF> m_nested;
Float m_alpha, m_intIOR, m_extIOR; Float m_intIOR, m_extIOR;
Float m_specularSamplingWeight; Float m_specularSamplingWeight;
Float m_thickness; Float m_thickness;
}; };
@ -460,46 +489,44 @@ private:
*/ */
class RoughCoatingShader : public Shader { class RoughCoatingShader : public Shader {
public: public:
RoughCoatingShader(Renderer *renderer, RoughCoatingShader(Renderer *renderer, const BSDF *nested,
const BSDF *nested, const Texture *sigmaA, const Texture *alpha,
const Texture *sigmaA, Float extIOR, Float intIOR) : Shader(renderer, EBSDFShader),
Float alpha, Float extIOR, m_nested(nested), m_sigmaA(sigmaA), m_alpha(alpha),
Float intIOR) : Shader(renderer, EBSDFShader), m_extIOR(extIOR), m_intIOR(intIOR) {
m_nested(nested),
m_sigmaA(sigmaA),
m_alpha(alpha), m_extIOR(extIOR), m_intIOR(intIOR) {
m_nestedShader = renderer->registerShaderForResource(m_nested.get()); m_nestedShader = renderer->registerShaderForResource(m_nested.get());
m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get()); m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get());
m_alpha = std::max(m_alpha, (Float) 0.2f); m_alphaShader = renderer->registerShaderForResource(m_alpha.get());
m_R0 = fresnel(1.0f, m_extIOR, m_intIOR); m_R0 = fresnel(1.0f, m_extIOR, m_intIOR);
m_eta = extIOR / intIOR; m_eta = extIOR / intIOR;
} }
bool isComplete() const { bool isComplete() const {
return m_nestedShader.get() != NULL return m_nestedShader.get() != NULL
&& m_sigmaAShader.get() != NULL; && m_sigmaAShader.get() != NULL
&& m_alphaShader.get() != NULL;
} }
void putDependencies(std::vector<Shader *> &deps) { void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_nestedShader.get()); deps.push_back(m_nestedShader.get());
deps.push_back(m_sigmaAShader.get()); deps.push_back(m_sigmaAShader.get());
deps.push_back(m_alphaShader.get());
} }
void cleanup(Renderer *renderer) { void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_nested.get()); renderer->unregisterShaderForResource(m_nested.get());
renderer->unregisterShaderForResource(m_sigmaA.get()); renderer->unregisterShaderForResource(m_sigmaA.get());
renderer->unregisterShaderForResource(m_alpha.get());
} }
void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const { void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const {
parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); parameterIDs.push_back(program->getParameterID(evalName + "_R0", false));
parameterIDs.push_back(program->getParameterID(evalName + "_eta", false)); parameterIDs.push_back(program->getParameterID(evalName + "_eta", false));
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
} }
void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const { void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const {
program->setParameter(parameterIDs[0], m_R0); program->setParameter(parameterIDs[0], m_R0);
program->setParameter(parameterIDs[1], m_eta); program->setParameter(parameterIDs[1], m_eta);
program->setParameter(parameterIDs[2], m_alpha);
} }
void generateCode(std::ostringstream &oss, void generateCode(std::ostringstream &oss,
@ -507,7 +534,6 @@ public:
const std::vector<std::string> &depNames) const { const std::vector<std::string> &depNames) const {
oss << "uniform float " << evalName << "_R0;" << endl oss << "uniform float " << evalName << "_R0;" << endl
<< "uniform float " << evalName << "_eta;" << endl << "uniform float " << evalName << "_eta;" << endl
<< "uniform float " << evalName << "_alpha;" << endl
<< endl << endl
<< "float " << evalName << "_schlick(float ct) {" << endl << "float " << evalName << "_schlick(float ct) {" << endl
<< " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl << " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl
@ -529,13 +555,13 @@ public:
<< " }" << endl << " }" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "float " << evalName << "_D(vec3 m) {" << endl << "float " << evalName << "_D(vec3 m, float alpha) {" << endl
<< " float ct = cosTheta(m);" << endl << " float ct = cosTheta(m);" << endl
<< " if (cosTheta(m) <= 0.0)" << endl << " if (cosTheta(m) <= 0.0)" << endl
<< " return 0.0;" << endl << " return 0.0;" << endl
<< " float ex = tanTheta(m) / " << evalName << "_alpha;" << endl << " float ex = tanTheta(m) / alpha;" << endl
<< " return exp(-(ex*ex)) / (pi * " << evalName << "_alpha" << endl << " return exp(-(ex*ex)) / (pi * alpha * alpha *" << endl
<< " * " << evalName << "_alpha * pow(cosTheta(m), 4.0));" << endl << " pow(cosTheta(m), 4.0));" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl << "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl
@ -562,7 +588,8 @@ public:
<< " 1/abs(cosTheta(woPrime))));" << endl << " 1/abs(cosTheta(woPrime))));" << endl
<< " if (cosTheta(wi)*cosTheta(wo) > 0) {" << endl << " if (cosTheta(wi)*cosTheta(wo) > 0) {" << endl
<< " vec3 H = normalize(wi + wo);" << endl << " vec3 H = normalize(wi + wo);" << endl
<< " float D = " << evalName << "_D(H)" << ";" << endl << " float alpha = max(0.2, " << depNames[2] << "(uv)[0]);" << endl
<< " float D = " << evalName << "_D(H, alpha)" << ";" << endl
<< " float G = " << evalName << "_G(H, wi, wo);" << endl << " float G = " << evalName << "_G(H, wi, wo);" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl << " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl
<< " result += vec3(F * D * G / (4*cosTheta(wi)));" << endl << " result += vec3(F * D * G / (4*cosTheta(wi)));" << endl
@ -581,12 +608,14 @@ private:
ref<Shader> m_nestedShader; ref<Shader> m_nestedShader;
ref<const Texture> m_sigmaA; ref<const Texture> m_sigmaA;
ref<Shader> m_sigmaAShader; ref<Shader> m_sigmaAShader;
Float m_alpha, m_extIOR, m_intIOR, m_R0, m_eta; ref<const Texture> m_alpha;
ref<Shader> m_alphaShader;
Float m_extIOR, m_intIOR, m_R0, m_eta;
}; };
Shader *RoughCoating::createShader(Renderer *renderer) const { Shader *RoughCoating::createShader(Renderer *renderer) const {
return new RoughCoatingShader(renderer, m_nested.get(), return new RoughCoatingShader(renderer, m_nested.get(),
m_sigmaA.get(), m_alpha, m_extIOR, m_intIOR); m_sigmaA.get(), m_alpha.get(), m_extIOR, m_intIOR);
} }
MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader) MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader)

View File

@ -237,10 +237,10 @@ public:
} }
void configure() { void configure() {
m_components.clear();
bool constAlpha = m_alpha->isConstant(); bool constAlpha = m_alpha->isConstant();
m_components.clear();
m_components.push_back(EGlossyReflection | EFrontSide m_components.push_back(EGlossyReflection | EFrontSide
| ((constAlpha && m_specularReflectance->isConstant()) | ((constAlpha && m_specularReflectance->isConstant())
? 0 : ESpatiallyVarying)); ? 0 : ESpatiallyVarying));
@ -286,7 +286,8 @@ public:
m_usesRayDifferentials = m_usesRayDifferentials =
m_specularReflectance->usesRayDifferentials() || m_specularReflectance->usesRayDifferentials() ||
m_diffuseReflectance->usesRayDifferentials(); m_diffuseReflectance->usesRayDifferentials() ||
m_alpha->usesRayDifferentials();
BSDF::configure(); BSDF::configure();
} }

View File

@ -43,7 +43,7 @@ Shape::~Shape() { }
void Shape::configure() { void Shape::configure() {
if (isLuminaire() && m_bsdf == NULL) { if ((hasSubsurface() || isLuminaire()) && m_bsdf == NULL) {
/* Light source & no BSDF -> set an all-absorbing BSDF to turn /* Light source & no BSDF -> set an all-absorbing BSDF to turn
the shape into an occluder. This is needed for the path the shape into an occluder. This is needed for the path
tracer implementation to work correctly. */ tracer implementation to work correctly. */