From a16b74eb0fa734fafdb04ad54d9d1c3f1ea0f0e5 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Wed, 7 Sep 2011 09:40:57 -0400 Subject: [PATCH] switched to a more realistic plastic material that accounts for internal scattering --- src/bsdfs/SConscript | 1 + src/bsdfs/oldplastic.cpp | 449 +++++++++++++++++++++++++++++++++++++++ src/bsdfs/plastic.cpp | 85 ++++++-- 3 files changed, 518 insertions(+), 17 deletions(-) create mode 100644 src/bsdfs/oldplastic.cpp diff --git a/src/bsdfs/SConscript b/src/bsdfs/SConscript index 6a60a79c..fb3d8aa9 100644 --- a/src/bsdfs/SConscript +++ b/src/bsdfs/SConscript @@ -5,6 +5,7 @@ plugins += env.SharedLibrary('diffuse', ['diffuse.cpp']) plugins += env.SharedLibrary('dielectric', ['dielectric.cpp']) plugins += env.SharedLibrary('conductor', ['conductor.cpp']) plugins += env.SharedLibrary('plastic', ['plastic.cpp']) +plugins += env.SharedLibrary('oldplastic', ['oldplastic.cpp']) plugins += env.SharedLibrary('roughdiffuse', ['roughdiffuse.cpp']) plugins += env.SharedLibrary('roughdielectric', ['roughdielectric.cpp']) plugins += env.SharedLibrary('roughconductor', ['roughconductor.cpp']) diff --git a/src/bsdfs/oldplastic.cpp b/src/bsdfs/oldplastic.cpp new file mode 100644 index 00000000..3f89d949 --- /dev/null +++ b/src/bsdfs/oldplastic.cpp @@ -0,0 +1,449 @@ +/* + 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 . +*/ + +#include +#include +#include "ior.h" + +MTS_NAMESPACE_BEGIN + +/*!\plugin{plastic}{Smooth plastic material} + * \order{7} + * \icon{bsdf_plastic} + * \parameters{ + * \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified + * numerically or using a known material name. \default{\texttt{polypropylene} / 1.49}} + * \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified + * numerically or using a known material name. \default{\texttt{air} / 1.000277}} + * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional + * factor used to modulate the specular reflection component. Note that for physical + * realism, this parameter should never be touched. \default{1.0}} + * \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional + * factor used to modulate the diffuse reflection component\default{0.5}} + * } + * + * \renderings{ + * \rendering{A rendering with the default parameters}{bsdf_plastic_default} + * \rendering{A rendering with custom parameters (\lstref{plastic-shiny})} + * {bsdf_plastic_shiny} + * } + * + * This plugin describes a perfectly smooth plastic-like dielectric material + * with internal scattering. The model interpolates between ideally specular + * and ideally diffuse reflection based on the Fresnel reflectance (i.e. it + * does so in a way that depends on the angle of incidence). Similar to the + * \pluginref{dielectric} plugin, IOR values can either be specified + * numerically, or based on a list of known materials (see + * \tblref{dielectric-iors} for an overview). + * + * Since it is very simple and fast, this model is often a better choice + * than the \pluginref{phong}, \pluginref{ward}, and \pluginref{roughplastic} + * plugins when rendering very smooth plastic-like materials. \vspace{4mm} + * + * \begin{xml}[caption=A shiny material whose diffuse reflectance is + * specified using sRGB, label=lst:plastic-shiny] + * + * + * + * + * \end{xml} + */ +class SmoothPlastic : public BSDF { +public: + SmoothPlastic(const Properties &props) : BSDF(props) { + /* Specifies the internal index of refraction at the interface */ + m_intIOR = lookupIOR(props, "intIOR", "polypropylene"); + + /* Specifies the external index of refraction at the interface */ + m_extIOR = lookupIOR(props, "extIOR", "air"); + + m_specularReflectance = new ConstantSpectrumTexture( + props.getSpectrum("specularReflectance", Spectrum(1.0f))); + m_diffuseReflectance = new ConstantSpectrumTexture( + props.getSpectrum("diffuseReflectance", Spectrum(0.5f))); + m_specularSamplingWeight = 0.0f; + } + + SmoothPlastic(Stream *stream, InstanceManager *manager) + : BSDF(stream, manager) { + m_intIOR = stream->readFloat(); + m_extIOR = stream->readFloat(); + m_specularReflectance = static_cast(manager->getInstance(stream)); + m_diffuseReflectance = static_cast(manager->getInstance(stream)); + configure(); + } + + void configure() { + /* Verify the input parameters and fix them if necessary */ + m_specularReflectance = ensureEnergyConservation( + m_specularReflectance, "specularReflectance", 1.0f); + m_diffuseReflectance = ensureEnergyConservation( + m_diffuseReflectance, "diffuseReflectance", 1.0f); + + /* Compute weights that further steer samples towards + the specular or diffuse components */ + Float dAvg = m_diffuseReflectance->getAverage().getLuminance(), + sAvg = m_specularReflectance->getAverage().getLuminance(); + m_specularSamplingWeight = sAvg / (dAvg + sAvg); + + m_usesRayDifferentials = + m_specularReflectance->usesRayDifferentials() || + m_diffuseReflectance->usesRayDifferentials(); + + m_components.clear(); + m_components.push_back(EDeltaReflection | EFrontSide + | (m_specularReflectance->isConstant() ? 0 : ESpatiallyVarying)); + m_components.push_back(EDiffuseReflection | EFrontSide + | (m_diffuseReflectance->isConstant() ? 0 : ESpatiallyVarying)); + + BSDF::configure(); + } + + + Spectrum getDiffuseReflectance(const Intersection &its) const { + return m_diffuseReflectance->getValue(its); + } + + void serialize(Stream *stream, InstanceManager *manager) const { + BSDF::serialize(stream, manager); + + stream->writeFloat(m_intIOR); + stream->writeFloat(m_extIOR); + manager->serialize(stream, m_specularReflectance.get()); + manager->serialize(stream, m_diffuseReflectance.get()); + } + + void addChild(const std::string &name, ConfigurableObject *child) { + if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) { + if (name == "specularReflectance") + m_specularReflectance = static_cast(child); + else if (name == "diffuseReflectance") + m_diffuseReflectance = static_cast(child); + else + BSDF::addChild(name, child); + } else { + BSDF::addChild(name, child); + } + } + + /// Reflection in local coordinates + inline Vector reflect(const Vector &wi) const { + return Vector(-wi.x, -wi.y, wi.z); + } + + Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { + bool hasSpecular = (bRec.typeMask & EDeltaReflection) + && (bRec.component == -1 || bRec.component == 0); + bool hasDiffuse = (bRec.typeMask & EDiffuseReflection) + && (bRec.component == -1 || bRec.component == 1); + + if (Frame::cosTheta(bRec.wo) <= 0 || Frame::cosTheta(bRec.wi) <= 0) + return Spectrum(0.0f); + + Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); + + if (measure == EDiscrete && hasSpecular) { + /* Check if the provided direction pair matches an ideal + specular reflection; tolerate some roundoff errors */ + bool reflection = std::abs(1 - dot(reflect(bRec.wi), bRec.wo)) < Epsilon; + if (reflection) + return m_specularReflectance->getValue(bRec.its) * Fr; + } else if (measure == ESolidAngle && hasDiffuse) { + if (hasDiffuse) + return m_diffuseReflectance->getValue(bRec.its) + * (INV_PI * Frame::cosTheta(bRec.wo) * (1-Fr)); + } + + return Spectrum(0.0f); + } + + Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { + bool hasSpecular = (bRec.typeMask & EDeltaReflection) + && (bRec.component == -1 || bRec.component == 0); + bool hasDiffuse = (bRec.typeMask & EDiffuseReflection) + && (bRec.component == -1 || bRec.component == 1); + + if (Frame::cosTheta(bRec.wo) <= 0 || Frame::cosTheta(bRec.wi) <= 0) + return 0.0f; + + Float probSpecular = 1.0f; + if (hasSpecular && hasDiffuse) { + Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); + probSpecular = (Fr*m_specularSamplingWeight) / + (Fr*m_specularSamplingWeight + + (1-Fr) * (1-m_specularSamplingWeight)); + } + + if (measure == EDiscrete && hasSpecular) { + /* Check if the provided direction pair matches an ideal + specular reflection; tolerate some roundoff errors */ + if (std::abs(1 - dot(reflect(bRec.wi), bRec.wo)) < Epsilon) + return probSpecular; + } else if (measure == ESolidAngle && hasDiffuse) { + return Frame::cosTheta(bRec.wo) * INV_PI * + (hasSpecular ? (1 - probSpecular) : 1.0f); + } + + return 0.0f; + } + + Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { + bool hasSpecular = (bRec.typeMask & EDeltaReflection) + && (bRec.component == -1 || bRec.component == 0); + bool hasDiffuse = (bRec.typeMask & EDiffuseReflection) + && (bRec.component == -1 || bRec.component == 1); + + if ((!hasDiffuse && !hasSpecular) || Frame::cosTheta(bRec.wi) <= 0) + return Spectrum(0.0f); + + Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); + Float probSpecular = (Fr*m_specularSamplingWeight) / + (Fr*m_specularSamplingWeight + + (1-Fr) * (1-m_specularSamplingWeight)); + + if (hasDiffuse && hasSpecular) { + /* Importance sample wrt. the Fresnel reflectance */ + if (sample.x <= probSpecular) { + bRec.sampledComponent = 0; + bRec.sampledType = EDeltaReflection; + bRec.wo = reflect(bRec.wi); + + return m_specularReflectance->getValue(bRec.its) * + (Fr / probSpecular); + } else { + bRec.sampledComponent = 1; + bRec.sampledType = EDiffuseReflection; + bRec.wo = squareToHemispherePSA(Point2( + (sample.x - probSpecular) / (1 - probSpecular), + sample.y + )); + + return m_diffuseReflectance->getValue(bRec.its) * + ((1-Fr) / (1-probSpecular)); + } + } else if (hasSpecular) { + bRec.sampledComponent = 0; + bRec.sampledType = EDeltaReflection; + bRec.wo = reflect(bRec.wi); + return m_specularReflectance->getValue(bRec.its) * Fr; + } else { + bRec.sampledComponent = 1; + bRec.sampledType = EDiffuseReflection; + + if (Fr == 1.0f) /* Total internal reflection */ + return Spectrum(0.0f); + + bRec.wo = squareToHemispherePSA(sample); + + return m_diffuseReflectance->getValue(bRec.its) * (1-Fr); + } + } + + Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const { + bool hasSpecular = (bRec.typeMask & EDeltaReflection) + && (bRec.component == -1 || bRec.component == 0); + bool hasDiffuse = (bRec.typeMask & EDiffuseReflection) + && (bRec.component == -1 || bRec.component == 1); + + if ((!hasDiffuse && !hasSpecular) || Frame::cosTheta(bRec.wi) <= 0) + return Spectrum(0.0f); + + Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); + Float probSpecular = (Fr*m_specularSamplingWeight) / + (Fr*m_specularSamplingWeight + + (1-Fr) * (1-m_specularSamplingWeight)); + + if (hasDiffuse && hasSpecular) { + /* Importance sample wrt. the Fresnel reflectance */ + if (sample.x <= probSpecular) { + bRec.sampledComponent = 0; + bRec.sampledType = EDeltaReflection; + bRec.wo = reflect(bRec.wi); + + pdf = probSpecular; + return m_specularReflectance->getValue(bRec.its) + * Fr / probSpecular; + } else { + bRec.sampledComponent = 1; + bRec.sampledType = EDiffuseReflection; + bRec.wo = squareToHemispherePSA(Point2( + (sample.x - probSpecular) / (1 - probSpecular), + sample.y + )); + pdf = (1-probSpecular) * Frame::cosTheta(bRec.wo) * INV_PI; + + return m_diffuseReflectance->getValue(bRec.its) + * (1-Fr) / (1-probSpecular); + } + } else if (hasSpecular) { + bRec.sampledComponent = 0; + bRec.sampledType = EDeltaReflection; + bRec.wo = reflect(bRec.wi); + pdf = 1; + return m_specularReflectance->getValue(bRec.its) * Fr; + } else { + bRec.sampledComponent = 1; + bRec.sampledType = EDiffuseReflection; + + if (Fr == 1.0f) /* Total internal reflection */ + return Spectrum(0.0f); + + bRec.wo = squareToHemispherePSA(sample); + + pdf = Frame::cosTheta(bRec.wo) * INV_PI; + + return m_diffuseReflectance->getValue(bRec.its) * (1-Fr); + } + } + + Shader *createShader(Renderer *renderer) const; + + std::string toString() const { + std::ostringstream oss; + oss << "SmoothPlastic[" << endl + << " name = \"" << getName() << "\"," << endl + << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl + << " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl + << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl + << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl + << " intIOR = " << m_intIOR << "," << endl + << " extIOR = " << m_extIOR << endl + << "]"; + return oss.str(); + } + + MTS_DECLARE_CLASS() +private: + Float m_intIOR, m_extIOR; + ref m_diffuseReflectance; + ref m_specularReflectance; + Float m_specularSamplingWeight; +}; + +/** + * Smooth plastic shader -- it is really hopeless to visualize + * this material in the VPL renderer, so let's try to do at least + * something that suggests the presence of a specularly-reflecting + * dielectric coating. + */ +class SmoothPlasticShader : public Shader { +public: + SmoothPlasticShader(Renderer *renderer, const Texture *specularReflectance, + const Texture *diffuseReflectance, Float extIOR, + Float intIOR) : Shader(renderer, EBSDFShader), + m_specularReflectance(specularReflectance), + m_diffuseReflectance(diffuseReflectance), + m_extIOR(extIOR), m_intIOR(intIOR) { + m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get()); + m_diffuseReflectanceShader = renderer->registerShaderForResource(m_diffuseReflectance.get()); + m_alpha = 0.4f; + m_R0 = fresnel(1.0f, m_extIOR, m_intIOR); + } + + bool isComplete() const { + return m_specularReflectanceShader.get() != NULL && + m_diffuseReflectanceShader.get() != NULL; + } + + void putDependencies(std::vector &deps) { + deps.push_back(m_specularReflectanceShader.get()); + deps.push_back(m_diffuseReflectanceShader.get()); + } + + void cleanup(Renderer *renderer) { + renderer->unregisterShaderForResource(m_specularReflectance.get()); + renderer->unregisterShaderForResource(m_diffuseReflectance.get()); + } + + void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { + parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false)); + parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); + } + + void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { + program->setParameter(parameterIDs[0], m_alpha); + program->setParameter(parameterIDs[1], m_R0); + } + + void generateCode(std::ostringstream &oss, + const std::string &evalName, + const std::vector &depNames) const { + oss << "uniform float " << evalName << "_alpha;" << endl + << "uniform float " << evalName << "_R0;" << endl + << endl + << "float " << evalName << "_D(vec3 m) {" << endl + << " float ct = cosTheta(m);" << endl + << " if (cosTheta(m) <= 0.0)" << endl + << " return 0.0;" << endl + << " float ex = tanTheta(m) / " << evalName << "_alpha;" << endl + << " return exp(-(ex*ex)) / (pi * " << evalName << "_alpha" << endl + << " * " << evalName << "_alpha * pow(cosTheta(m), 4.0));" << endl + << "}" << endl + << endl + << "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl + << " if ((dot(wi, m) * cosTheta(wi)) <= 0 || " << endl + << " (dot(wo, m) * cosTheta(wo)) <= 0)" << endl + << " return 0.0;" << endl + << " float nDotM = cosTheta(m);" << endl + << " return min(1.0, min(" << endl + << " abs(2 * nDotM * cosTheta(wo) / dot(wo, m))," << endl + << " abs(2 * nDotM * cosTheta(wi) / dot(wi, m))));" << endl + << "}" << endl + << endl + << "float " << evalName << "_schlick(float ct) {" << endl + << " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl + << " return " << evalName << "_R0 + (1.0 - " << evalName << "_R0) * ct5;" << endl + << "}" << endl + << endl + << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " if (cosTheta(wi) <= 0 || cosTheta(wo) <= 0)" << endl + << " return vec3(0.0);" << endl + << " vec3 H = normalize(wi + wo);" << endl + << " vec3 specRef = " << depNames[0] << "(uv);" << endl + << " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl + << " float D = " << evalName << "_D(H)" << ";" << endl + << " float G = " << evalName << "_G(H, wi, wo);" << endl + << " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl + << " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl + << " diffuseRef * ((1-F) * cosTheta(wo) * 0.31831);" << endl + << "}" << endl + << endl + << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl + << " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl + << "}" << endl; + } + MTS_DECLARE_CLASS() +private: + ref m_specularReflectance; + ref m_diffuseReflectance; + ref m_specularReflectanceShader; + ref m_diffuseReflectanceShader; + Float m_alpha, m_extIOR, m_intIOR, m_R0; +}; + +Shader *SmoothPlastic::createShader(Renderer *renderer) const { + return new SmoothPlasticShader(renderer, + m_specularReflectance.get(), m_diffuseReflectance.get(), m_extIOR, m_intIOR); +} + +MTS_IMPLEMENT_CLASS(SmoothPlasticShader, false, Shader) + +MTS_IMPLEMENT_CLASS_S(SmoothPlastic, false, BSDF) +MTS_EXPORT_PLUGIN(SmoothPlastic, "Smooth plastic BRDF"); +MTS_NAMESPACE_END diff --git a/src/bsdfs/plastic.cpp b/src/bsdfs/plastic.cpp index 3f89d949..13e69775 100644 --- a/src/bsdfs/plastic.cpp +++ b/src/bsdfs/plastic.cpp @@ -30,6 +30,14 @@ MTS_NAMESPACE_BEGIN * numerically or using a known material name. \default{\texttt{polypropylene} / 1.49}} * \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified * numerically or using a known material name. \default{\texttt{air} / 1.000277}} + * \parameter{preserveColors}{\Boolean}{ + * By default, this implementation accounts for light that undergoes any number + * of internal reflections from the dielectric material boundary before exiting, which + * potentially causes the diffuse component to shift towards more saturated colors. + * While realistic, this behavior might not always be desirable. In that case, set + * this parameter to \code{true}. + * \default{\code{false}} + * } * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * factor used to modulate the specular reflection component. Note that for physical * realism, this parameter should never be touched. \default{1.0}} @@ -51,9 +59,9 @@ MTS_NAMESPACE_BEGIN * numerically, or based on a list of known materials (see * \tblref{dielectric-iors} for an overview). * - * Since it is very simple and fast, this model is often a better choice + * Since it is simple and fast, this model is often a better choice * than the \pluginref{phong}, \pluginref{ward}, and \pluginref{roughplastic} - * plugins when rendering very smooth plastic-like materials. \vspace{4mm} + * plugins when rendering smooth plastic-like materials. \vspace{4mm} * * \begin{xml}[caption=A shiny material whose diffuse reflectance is * specified using sRGB, label=lst:plastic-shiny] @@ -76,6 +84,9 @@ public: props.getSpectrum("specularReflectance", Spectrum(1.0f))); m_diffuseReflectance = new ConstantSpectrumTexture( props.getSpectrum("diffuseReflectance", Spectrum(0.5f))); + + m_preserveColors = props.getBoolean("preserveColors", false); + m_specularSamplingWeight = 0.0f; } @@ -83,6 +94,7 @@ public: : BSDF(stream, manager) { m_intIOR = stream->readFloat(); m_extIOR = stream->readFloat(); + m_preserveColors = stream->readBool(); m_specularReflectance = static_cast(manager->getInstance(stream)); m_diffuseReflectance = static_cast(manager->getInstance(stream)); configure(); @@ -95,10 +107,14 @@ public: m_diffuseReflectance = ensureEnergyConservation( m_diffuseReflectance, "diffuseReflectance", 1.0f); + /* Numerically approximate the diffuse Fresnel reflectance */ + m_fdr = fresnelDiffuseReflectance(m_extIOR / m_intIOR, false); + /* Compute weights that further steer samples towards the specular or diffuse components */ Float dAvg = m_diffuseReflectance->getAverage().getLuminance(), sAvg = m_specularReflectance->getAverage().getLuminance(); + m_specularSamplingWeight = sAvg / (dAvg + sAvg); m_usesRayDifferentials = @@ -124,6 +140,7 @@ public: stream->writeFloat(m_intIOR); stream->writeFloat(m_extIOR); + stream->writeBool(m_preserveColors); manager->serialize(stream, m_specularReflectance.get()); manager->serialize(stream, m_diffuseReflectance.get()); } @@ -164,9 +181,16 @@ public: if (reflection) return m_specularReflectance->getValue(bRec.its) * Fr; } else if (measure == ESolidAngle && hasDiffuse) { - if (hasDiffuse) - return m_diffuseReflectance->getValue(bRec.its) - * (INV_PI * Frame::cosTheta(bRec.wo) * (1-Fr)); + Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); + + if (hasDiffuse) { + Spectrum diff = m_diffuseReflectance->getValue(bRec.its); + if (m_preserveColors) + diff /= 1 - m_fdr; + else + diff /= Spectrum(1) - m_fdr*diff; + return diff * (INV_PI * Frame::cosTheta(bRec.wo) * (1-Fr) * (1-Fr2)); + } } return Spectrum(0.0f); @@ -232,9 +256,15 @@ public: (sample.x - probSpecular) / (1 - probSpecular), sample.y )); + Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); - return m_diffuseReflectance->getValue(bRec.its) * - ((1-Fr) / (1-probSpecular)); + Spectrum diff = m_diffuseReflectance->getValue(bRec.its); + if (m_preserveColors) + diff /= 1 - m_fdr; + else + diff /= Spectrum(1) - m_fdr*diff; + + return diff * ((1-Fr) * (1-Fr2) / (1-probSpecular)); } } else if (hasSpecular) { bRec.sampledComponent = 0; @@ -242,15 +272,19 @@ public: bRec.wo = reflect(bRec.wi); return m_specularReflectance->getValue(bRec.its) * Fr; } else { + bRec.wo = squareToHemispherePSA(sample); + Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); bRec.sampledComponent = 1; bRec.sampledType = EDiffuseReflection; - if (Fr == 1.0f) /* Total internal reflection */ - return Spectrum(0.0f); - - bRec.wo = squareToHemispherePSA(sample); + Spectrum diff = m_diffuseReflectance->getValue(bRec.its); + if (m_preserveColors) + diff /= 1 - m_fdr; + else + diff /= Spectrum(1) - m_fdr*diff; - return m_diffuseReflectance->getValue(bRec.its) * (1-Fr); + + return diff * (1-Fr) * (1-Fr2); } } @@ -285,10 +319,17 @@ public: (sample.x - probSpecular) / (1 - probSpecular), sample.y )); + Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); + + Spectrum diff = m_diffuseReflectance->getValue(bRec.its); + if (m_preserveColors) + diff /= 1 - m_fdr; + else + diff /= Spectrum(1) - m_fdr*diff; + pdf = (1-probSpecular) * Frame::cosTheta(bRec.wo) * INV_PI; - return m_diffuseReflectance->getValue(bRec.its) - * (1-Fr) / (1-probSpecular); + return diff * ((1-Fr) * (1-Fr2) / (1-probSpecular)); } } else if (hasSpecular) { bRec.sampledComponent = 0; @@ -299,6 +340,7 @@ public: } else { bRec.sampledComponent = 1; bRec.sampledType = EDiffuseReflection; + Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); if (Fr == 1.0f) /* Total internal reflection */ return Spectrum(0.0f); @@ -307,7 +349,13 @@ public: pdf = Frame::cosTheta(bRec.wo) * INV_PI; - return m_diffuseReflectance->getValue(bRec.its) * (1-Fr); + Spectrum diff = m_diffuseReflectance->getValue(bRec.its); + if (m_preserveColors) + diff /= 1 - m_fdr; + else + diff /= Spectrum(1) - m_fdr*diff; + + return diff * (1-Fr) * (1-Fr2); } } @@ -321,18 +369,21 @@ public: << " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl + << " preserveColors = " << m_preserveColors << "," << endl << " intIOR = " << m_intIOR << "," << endl - << " extIOR = " << m_extIOR << endl + << " extIOR = " << m_extIOR << "," << endl + << " fdr = " << m_fdr << endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: - Float m_intIOR, m_extIOR; + Float m_intIOR, m_extIOR, m_fdr; ref m_diffuseReflectance; ref m_specularReflectance; Float m_specularSamplingWeight; + bool m_preserveColors; }; /**