From 30a93f4bbc4cdddcaa34b3d6029ab6b8f31b8241 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Mon, 19 Sep 2011 19:19:00 -0400 Subject: [PATCH] texturable specular reflectance for coating + roughcoating --- src/bsdfs/coating.cpp | 44 +++++++++++++++++++++++++++----------- src/bsdfs/roughcoating.cpp | 27 ++++++++++++++++++----- 2 files changed, 53 insertions(+), 18 deletions(-) diff --git a/src/bsdfs/coating.cpp b/src/bsdfs/coating.cpp index e076fee9..19209886 100644 --- a/src/bsdfs/coating.cpp +++ b/src/bsdfs/coating.cpp @@ -35,6 +35,9 @@ MTS_NAMESPACE_BEGIN * model absorption --- should be specified in inverse units of \code{sigmaA})\default{1}} * \parameter{sigmaA}{\Spectrum\Or\Texture}{The absorption coefficient of the * coating layer. \default{0, i.e. there is no absorption}} + * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional + * factor that can be used to modulate the specular transmission component. Note + * that for physical realism, this parameter should never be touched. \default{1.0}} * \parameter{\Unnamed}{\BSDF}{A nested BSDF model that should be coated.} * } * @@ -62,13 +65,7 @@ MTS_NAMESPACE_BEGIN * Therefore, users are discouraged to use this plugin to coat smooth * diffuse materials, since there is a separately available plugin * named \pluginref{plastic}, which covers the same case and does not - * suffer from energy loss. - * - * Evaluating the internal component of this model entails refracting the - * incident and exitant rays through the dielectric interface, followed by - * querying the nested material with this modified direction pair. The result - * is attenuated by the two Fresnel transmittances and the absorption, if - * any.\newpage + * suffer from energy loss.\newpage * * \renderings{ * \smallrendering{$\code{thickness}=0$}{bsdf_coating_0} @@ -99,6 +96,13 @@ MTS_NAMESPACE_BEGIN * \caption{Some interesting materials can be created simply by applying * Mitsuba's material modifiers in different orders.} * } + * + * \subsubsection*{Technical details} + * Evaluating the internal component of this model entails refracting the + * incident and exitant rays through the dielectric interface, followed by + * querying the nested material with this modified direction pair. The result + * is attenuated by the two Fresnel transmittances and the absorption, if + * any. */ class SmoothCoating : public BSDF { public: @@ -122,7 +126,11 @@ public: /* Specifies the absorption within the layer */ m_sigmaA = new ConstantSpectrumTexture( props.getSpectrum("sigmaA", Spectrum(0.0f))); - + + /* Specifies a multiplier for the specular reflectance component */ + m_specularReflectance = new ConstantSpectrumTexture( + props.getSpectrum("specularReflectance", Spectrum(1.0f))); + if (m_intIOR < 0 || m_extIOR < 0 || m_intIOR == m_extIOR) Log(EError, "The interior and exterior indices of " "refraction must be positive and differ!"); @@ -135,6 +143,7 @@ public: m_thickness = stream->readFloat(); m_nested = static_cast(manager->getInstance(stream)); m_sigmaA = static_cast(manager->getInstance(stream)); + m_specularReflectance = static_cast(manager->getInstance(stream)); configure(); } @@ -150,10 +159,12 @@ public: for (int i=0; igetComponentCount(); ++i) m_components.push_back(m_nested->getType(i) | extraFlags); - m_components.push_back(EDeltaReflection | EFrontSide | EBackSide); + m_components.push_back(EDeltaReflection | EFrontSide | EBackSide + | (m_specularReflectance->isConstant() ? 0 : ESpatiallyVarying)); m_usesRayDifferentials = m_nested->usesRayDifferentials() - || m_sigmaA->usesRayDifferentials(); + || m_sigmaA->usesRayDifferentials() + || m_specularReflectance->usesRayDifferentials(); /* Compute weights that further steer samples towards the specular or nested components */ @@ -162,6 +173,10 @@ public: m_specularSamplingWeight = 1.0f / (avgAbsorption + 1.0f); + /* Verify the input parameters and fix them if necessary */ + m_specularReflectance = ensureEnergyConservation( + m_specularReflectance, "specularReflectance", 1.0f); + BSDF::configure(); } @@ -173,6 +188,7 @@ public: stream->writeFloat(m_thickness); manager->serialize(stream, m_nested.get()); manager->serialize(stream, m_sigmaA.get()); + manager->serialize(stream, m_specularReflectance.get()); } void addChild(const std::string &name, ConfigurableObject *child) { @@ -238,8 +254,8 @@ public: if (measure == EDiscrete && sampleSpecular && std::abs(1-dot(reflect(bRec.wi), bRec.wo)) < Epsilon) { - return Spectrum(fresnel( - std::abs(Frame::cosTheta(bRec.wi)), m_extIOR, m_intIOR)); + return m_specularReflectance->getValue(bRec.its) * + fresnel(std::abs(Frame::cosTheta(bRec.wi)), m_extIOR, m_intIOR); } else if (sampleNested) { Float R12, R21; BSDFQueryRecord bRecInt(bRec); @@ -346,7 +362,7 @@ public: bRec.sampledType = EDeltaReflection; bRec.wo = reflect(bRec.wi); pdf = sampleNested ? probSpecular : 1.0f; - return Spectrum(R12) / pdf; + return m_specularReflectance->getValue(bRec.its) * (R12/pdf); } else { if (R12 == 1.0f) /* Total internal reflection */ return Spectrum(0.0f); @@ -405,6 +421,7 @@ public: << " extIOR = " << m_extIOR << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl + << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl << " thickness = " << m_thickness << "," << endl << " nested = " << indent(m_nested.toString()) << endl << "]"; @@ -416,6 +433,7 @@ protected: Float m_specularSamplingWeight; Float m_intIOR, m_extIOR; ref m_sigmaA; + ref m_specularReflectance; ref m_nested; Float m_thickness; }; diff --git a/src/bsdfs/roughcoating.cpp b/src/bsdfs/roughcoating.cpp index a8c7f383..1d49741c 100644 --- a/src/bsdfs/roughcoating.cpp +++ b/src/bsdfs/roughcoating.cpp @@ -57,6 +57,9 @@ MTS_NAMESPACE_BEGIN * numerically or using a known material name. \default{\texttt{air} / 1.000277}} * \parameter{sigmaA}{\Spectrum\Or\Texture}{The absorption coefficient of the * coating layer. \default{0, i.e. there is no absorption}} + * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional + * factor that can be used to modulate the specular transmission component. Note + * that for physical realism, this parameter should never be touched. \default{1.0}} * \parameter{\Unnamed}{\BSDF}{A nested BSDF model that should be coated.} * }\vspace{-4mm} * \renderings{ @@ -75,8 +78,8 @@ MTS_NAMESPACE_BEGIN * \pluginref{coating} plugin produces specular highlights that are too sharp.} * model that simulates a rough dielectric coating. It is essentially the * roughened version of \pluginref{coating}. - * Any BSDF in Mitsuba can be coated using this plugin, and multiple coating - * layers can even be applied in sequence. This allows designing interesting + * Any BSDF in Mitsuba can be coated using this plugin and multiple coating + * layers can even be applied in sequence, which allows designing interesting * custom materials. The coating layer can optionally be tinted (i.e. filled * with an absorbing medium), in which case this model also accounts for the * directionally dependent absorption within the layer. @@ -109,6 +112,10 @@ public: m_sigmaA = new ConstantSpectrumTexture( props.getSpectrum("sigmaA", Spectrum(0.0f))); + /* Specifies a multiplier for the specular reflectance component */ + m_specularReflectance = new ConstantSpectrumTexture( + props.getSpectrum("specularReflectance", Spectrum(1.0f))); + if (m_intIOR < 0 || m_extIOR < 0 || m_intIOR == m_extIOR) Log(EError, "The interior and exterior indices of " "refraction must be positive and differ!"); @@ -134,6 +141,7 @@ public: ); m_nested = static_cast(manager->getInstance(stream)); m_sigmaA = static_cast(manager->getInstance(stream)); + m_specularReflectance = static_cast(manager->getInstance(stream)); m_alpha = static_cast(manager->getInstance(stream)); m_intIOR = stream->readFloat(); m_extIOR = stream->readFloat(); @@ -151,11 +159,13 @@ public: for (int i=0; igetComponentCount(); ++i) m_components.push_back(m_nested->getType(i) | extraFlags); - m_components.push_back(EGlossyReflection | EFrontSide | EBackSide); + m_components.push_back(EGlossyReflection | EFrontSide | EBackSide + | (m_specularReflectance->isConstant() ? 0 : ESpatiallyVarying)); m_usesRayDifferentials = m_nested->usesRayDifferentials() || m_sigmaA->usesRayDifferentials() - || m_alpha->usesRayDifferentials(); + || m_alpha->usesRayDifferentials() + || m_specularReflectance->usesRayDifferentials(); /* Compute weights that further steer samples towards the specular or nested components */ @@ -164,6 +174,10 @@ public: m_specularSamplingWeight = 1.0f / (avgAbsorption + 1.0f); + /* Verify the input parameters and fix them if necessary */ + m_specularReflectance = ensureEnergyConservation( + m_specularReflectance, "specularReflectance", 1.0f); + if (!m_roughTransmittance.get()) { /* Load precomputed data used to compute the rough transmittance through the dielectric interface */ @@ -258,7 +272,7 @@ public: Float value = F * D * G / (4.0f * std::abs(Frame::cosTheta(bRec.wi))); - result += Spectrum(value); + result += m_specularReflectance->getValue(bRec.its) * value; } if (hasNested) { @@ -425,6 +439,7 @@ public: stream->writeUInt((uint32_t) m_distribution.getType()); manager->serialize(stream, m_nested.get()); manager->serialize(stream, m_sigmaA.get()); + manager->serialize(stream, m_specularReflectance.get()); manager->serialize(stream, m_alpha.get()); stream->writeFloat(m_intIOR); stream->writeFloat(m_extIOR); @@ -455,6 +470,7 @@ public: << " distribution = " << m_distribution.toString() << "," << endl << " alpha = " << indent(m_alpha->toString()) << "," << endl << " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl + << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl << " intIOR = " << m_intIOR << "," << endl @@ -472,6 +488,7 @@ private: ref m_roughTransmittance; ref m_sigmaA; ref m_alpha; + ref m_specularReflectance; ref m_nested; Float m_intIOR, m_extIOR; Float m_specularSamplingWeight;