From de4fe46aff3084b92cabdc9791a9823909cccf63 Mon Sep 17 00:00:00 2001 From: Wenzel Jakob Date: Thu, 7 Jul 2011 21:34:39 +0200 Subject: [PATCH] cleanups --- data/tests/test_bsdf.xml | 16 ++ src/bsdfs/SConscript | 2 +- src/bsdfs/microfacet.h | 5 +- src/bsdfs/roughconductor.cpp | 402 ++++++++++++++++++++++++++++++++++ src/bsdfs/roughdielectric.cpp | 38 ++-- src/bsdfs/roughmetal.cpp | 204 ----------------- src/tests/test_chisquare.cpp | 12 +- 7 files changed, 442 insertions(+), 237 deletions(-) create mode 100644 src/bsdfs/roughconductor.cpp delete mode 100644 src/bsdfs/roughmetal.cpp diff --git a/data/tests/test_bsdf.xml b/data/tests/test_bsdf.xml index 9eae929e..da2cbec3 100644 --- a/data/tests/test_bsdf.xml +++ b/data/tests/test_bsdf.xml @@ -2,6 +2,15 @@ to be tested for consistency. This is done using the testcase 'test_chisquare' --> + + + + + + + + @@ -81,4 +90,11 @@ + + + + + + diff --git a/src/bsdfs/SConscript b/src/bsdfs/SConscript index 28cf464d..dd85c439 100644 --- a/src/bsdfs/SConscript +++ b/src/bsdfs/SConscript @@ -7,7 +7,7 @@ plugins += env.SharedLibrary('diffuse', ['diffuse.cpp']) #plugins += env.SharedLibrary('plastic', ['plastic.cpp']) plugins += env.SharedLibrary('roughdielectric', ['roughdielectric.cpp']) -#plugins += env.SharedLibrary('roughconductor', ['roughconductor.cpp']) +plugins += env.SharedLibrary('roughconductor', ['roughconductor.cpp']) #plugins += env.SharedLibrary('roughdiffuse', ['roughdiffuse.cpp']) #plugins += env.SharedLibrary('roughplastic', ['roughplastic.cpp']) diff --git a/src/bsdfs/microfacet.h b/src/bsdfs/microfacet.h index 4a84a333..9d7ca181 100644 --- a/src/bsdfs/microfacet.h +++ b/src/bsdfs/microfacet.h @@ -157,7 +157,8 @@ public: return eval(m, alphaU, alphaV) * Frame::cosTheta(m); /* For the Ashikhmin-Shirley model, the sampling density - does not include the cos(theta_M) factor */ + does not include the cos(theta_M) factor, and the + normalization is slightly different than in eval(). */ const Float cosTheta = Frame::cosTheta(m); const Float ds = 1 - cosTheta * cosTheta; if (ds < 0) @@ -277,7 +278,9 @@ public: /* Approximation recommended by Bruce Walter: Use the Beckmann shadowing-masking function with specially chosen roughness value */ + cout << alpha << endl; alpha = std::sqrt(0.5f * alpha + 1) / tanTheta; + cout << " becomes " << alpha << endl; case EBeckmann: { /* Use a fast and accurate (<0.35% rel. error) rational diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp new file mode 100644 index 00000000..8fdbee7d --- /dev/null +++ b/src/bsdfs/roughconductor.cpp @@ -0,0 +1,402 @@ +/* + 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 +#include +#include "microfacet.h" + +MTS_NAMESPACE_BEGIN + +/* Suggestion by Bruce Walter: sample the model using a slightly + wider density function. This in practice limits the importance + weights to values <= 4. + + Turned off by default, since it seems to increase the variance + of the reflection component. +*/ +#define ENLARGE_LOBE_TRICK 0 + +/*! \plugin{roughconductor}{Rough conductor material} + * \parameters{ + * \parameter{distribution}{\String}{ + * Specifies the type of microfacet normal distribution + * used to model the surface roughness. + * \begin{enumerate}[(i)] + * \item \code{beckmann}: Physically-based distribution derived from + * Gaussian random surfaces. This is the default. + * \item \code{phong}: Classical $\cos^p\theta$ distribution. + * Due to the underlying microfacet theory, + * the use of this distribution here leads to more realistic + * behavior than the separately available \pluginref{phong} plugin. + * \item \code{ggx}: New distribution proposed by + * Walter et al. meant to better handle the long + * tails observed in measurements of ground surfaces. + * Renderings with this distribution may converge slowly. + * \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by + * Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm} + * \end{enumerate} + * } + * \parameter{alpha}{\Float\Or\Texture}{ + * Specifies the roughness value of the unresolved surface microgeometry. + * When the Beckmann distribution is used, this parameter is equal to the + * \emph{root mean square} (RMS) slope of the microfacets. This + * parameter is only valid when \texttt{distribution=beckmann/phong/ggx}. + * \default{0.1}. + * } + * \parameter{alphaU, alphaV}{\Float\Or\Texture}{ + * Specifies the anisotropic rougness values along the tangent and bitangent directions. These + * parameter are only valid when \texttt{distribution=as}. + * \default{0.1}. + * } + * \parameter{preset}{\String}{Name of a material preset, see + * \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}} + * \parameter{eta}{\Spectrum}{Real part of the material's index + * of refraction \default{based on the value of \texttt{preset}}} + * \parameter{k}{\Spectrum}{Imaginary part of the material's index of + * refraction, also known as absorption coefficient. + * \default{based on the value of \texttt{preset}}} + * \lastparameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional + * factor used to modulate the reflectance component\default{1.0}} + * } + */ +class RoughConductor : public BSDF { +public: + RoughConductor(const Properties &props) : BSDF(props) { + ref fResolver = Thread::getThread()->getFileResolver(); + + m_specularReflectance = new ConstantSpectrumTexture( + props.getSpectrum("specularReflectance", Spectrum(1.0f))); + + std::string preset = props.getString("preset", "Cu"); + Spectrum presetEta, presetK; + presetEta.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + preset + ".eta.spd"))); + presetK.fromContinuousSpectrum(InterpolatedSpectrum( + fResolver->resolve("data/ior/" + preset + ".k.spd"))); + + m_eta = props.getSpectrum("eta", presetEta); + m_k = props.getSpectrum("k", presetK); + + m_distribution = MicrofacetDistribution( + props.getString("distribution", "beckmann") + ); + + Float alpha = props.getFloat("alpha", 0.1f), + alphaU = props.getFloat("alphaU", alpha), + alphaV = props.getFloat("alphaV", alpha); + + m_alphaU = new ConstantFloatTexture(alphaU); + if (alphaU == alphaV) + m_alphaV = m_alphaU; + else + m_alphaV = new ConstantFloatTexture(alphaV); + + m_usesRayDifferentials = false; + } + + RoughConductor(Stream *stream, InstanceManager *manager) + : BSDF(stream, manager) { + m_distribution = MicrofacetDistribution( + (MicrofacetDistribution::EType) stream->readUInt() + ); + m_alphaU = static_cast(manager->getInstance(stream)); + m_alphaV = static_cast(manager->getInstance(stream)); + m_specularReflectance = static_cast(manager->getInstance(stream)); + m_eta = Spectrum(stream); + m_k = Spectrum(stream); + + m_usesRayDifferentials = + m_alphaU->usesRayDifferentials() || + m_alphaV->usesRayDifferentials() || + m_specularReflectance->usesRayDifferentials(); + + configure(); + } + + void configure() { + unsigned int extraFlags = 0; + if (m_alphaU != m_alphaV) { + extraFlags |= EAnisotropic; + if (m_distribution.getType() != + MicrofacetDistribution::EAshikhminShirley) + Log(EError, "Different roughness values along the tangent and " + "bitangent directions are only supported when using the " + "anisotropic Ashikhmin-Shirley microfacet distribution " + "(named \"as\")"); + } + + m_components.clear(); + m_components.push_back( + EGlossyReflection | EFrontSide | extraFlags); + + /* Verify the input parameter and fix them if necessary */ + m_specularReflectance = ensureEnergyConservation( + m_specularReflectance, "specularReflectance", 1.0f); + + BSDF::configure(); + } + + virtual ~RoughConductor() { } + + /// Helper function: reflect \c wi with respect to a given surface normal + inline Vector reflect(const Vector &wi, const Normal &m) const { + return 2 * dot(wi, m) * Vector(m) - wi; + } + + Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { + /* Stop if this component was not requested */ + if (measure != ESolidAngle || + Frame::cosTheta(bRec.wi) < 0 || + Frame::cosTheta(bRec.wo) < 0 || + ((bRec.component != -1 && bRec.component != 0) || + !(bRec.typeMask & EGlossyReflection))) + return Spectrum(0.0f); + + /* Calculate the reflection half-vector */ + Vector H = normalize(bRec.wo+bRec.wi); + + /* Evaluate the roughness */ + Float alphaU = m_distribution.transformRoughness( + m_alphaU->getValue(bRec.its).average()), + alphaV = m_distribution.transformRoughness( + m_alphaV->getValue(bRec.its).average()); + + /* Evaluate the microsurface normal distribution */ + const Float D = m_distribution.eval(H, alphaU, alphaV); + if (D == 0) + return Spectrum(0.0f); + + /* Fresnel factor */ + const Spectrum F = fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); + + /* Smith's shadow-masking function */ + const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV); + + /* Calculate the total amount of reflection */ + Float value = D * G / (4.0f * Frame::cosTheta(bRec.wi)); + + return m_specularReflectance->getValue(bRec.its) * F * value; + } + + Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { + if (measure != ESolidAngle || + Frame::cosTheta(bRec.wi) < 0 || + Frame::cosTheta(bRec.wo) < 0 || + ((bRec.component != -1 && bRec.component != 0) || + !(bRec.typeMask & EGlossyReflection))) + return 0.0f; + + /* Calculate the reflection half-vector */ + Vector H = normalize(bRec.wo+bRec.wi); + + /* Evaluate the roughness */ + Float alphaU = m_distribution.transformRoughness( + m_alphaU->getValue(bRec.its).average()), + alphaV = m_distribution.transformRoughness( + m_alphaV->getValue(bRec.its).average()); + +#if ENLARGE_LOBE_TRICK == 1 + Float factor = (1.2f - 0.2f * std::sqrt( + std::abs(Frame::cosTheta(bRec.wi)))); + alphaU *= factor; alphaV *= factor; +#endif + + return m_distribution.pdf(H, alphaU, alphaV) + / (4 * absDot(bRec.wo, H)); + } + + Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { + if (Frame::cosTheta(bRec.wi) < 0 || + ((bRec.component != -1 && bRec.component != 0) || + !(bRec.typeMask & EGlossyReflection))) + return Spectrum(0.0f); + + /* Evaluate the roughness */ + Float alphaU = m_distribution.transformRoughness( + m_alphaU->getValue(bRec.its).average()), + alphaV = m_distribution.transformRoughness( + m_alphaV->getValue(bRec.its).average()); + +#if ENLARGE_LOBE_TRICK == 1 + Float factor = (1.2f - 0.2f * std::sqrt( + std::abs(Frame::cosTheta(bRec.wi)))); + Float sampleAlphaU = alphaU * factor, + sampleAlphaV = alphaV * factor; +#else + Float sampleAlphaU = alphaU, + sampleAlphaV = alphaV; +#endif + + /* Sample M, the microsurface normal */ + const Normal m = m_distribution.sample(sample, + sampleAlphaU, sampleAlphaV); + + /* Perfect specular reflection based on the microsurface normal */ + bRec.wo = reflect(bRec.wi, m); + bRec.sampledComponent = 0; + bRec.sampledType = EGlossyReflection; + + /* Side check */ + if (Frame::cosTheta(bRec.wo) <= 0) + return Spectrum(0.0f); + + const Spectrum F = fresnelConductor(Frame::cosTheta(bRec.wi), + m_eta, m_k); + + Float numerator = m_distribution.eval(m, alphaU, alphaV) + * m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV) + * dot(bRec.wi, m); + + Float denominator = m_distribution.pdf(m, sampleAlphaU, sampleAlphaV) + * Frame::cosTheta(bRec.wi); + + return m_specularReflectance->getValue(bRec.its) * F + * (numerator / denominator); + } + + Spectrum sample(BSDFQueryRecord &bRec, Float &_pdf, const Point2 &sample) const { + if (Frame::cosTheta(bRec.wi) < 0 || + ((bRec.component != -1 && bRec.component != 0) || + !(bRec.typeMask & EGlossyReflection))) + return Spectrum(0.0f); + + /* Evaluate the roughness */ + Float alphaU = m_distribution.transformRoughness( + m_alphaU->getValue(bRec.its).average()), + alphaV = m_distribution.transformRoughness( + m_alphaV->getValue(bRec.its).average()); + +#if ENLARGE_LOBE_TRICK == 1 + Float factor = (1.2f - 0.2f * std::sqrt( + std::abs(Frame::cosTheta(bRec.wi)))); + Float sampleAlphaU = alphaU * factor, + sampleAlphaV = alphaV * factor; +#else + Float sampleAlphaU = alphaU, + sampleAlphaV = alphaV; +#endif + + /* Sample M, the microsurface normal */ + const Normal m = m_distribution.sample(sample, + sampleAlphaU, sampleAlphaV); + + /* Perfect specular reflection based on the microsurface normal */ + bRec.wo = reflect(bRec.wi, m); + bRec.sampledComponent = 0; + bRec.sampledType = EGlossyReflection; + + /* Side check */ + if (Frame::cosTheta(bRec.wo) <= 0) + return Spectrum(0.0f); + + /* Guard against numerical imprecisions */ + _pdf = pdf(bRec, ESolidAngle); + + if (_pdf == 0) + return Spectrum(0.0f); + else + return eval(bRec, ESolidAngle); + } + + void addChild(const std::string &name, ConfigurableObject *child) { + if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alpha") { + m_alphaU = m_alphaV = static_cast(child); + m_usesRayDifferentials |= m_alphaU->usesRayDifferentials(); + } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaU") { + m_alphaU = static_cast(child); + m_usesRayDifferentials |= m_alphaU->usesRayDifferentials(); + } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaV") { + m_alphaV = static_cast(child); + m_usesRayDifferentials |= m_alphaV->usesRayDifferentials(); + } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularReflectance") { + m_specularReflectance = static_cast(child); + m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials(); + } else { + BSDF::addChild(name, child); + } + } + + void serialize(Stream *stream, InstanceManager *manager) const { + BSDF::serialize(stream, manager); + + stream->writeUInt((uint32_t) m_distribution.getType()); + manager->serialize(stream, m_alphaU.get()); + manager->serialize(stream, m_alphaV.get()); + manager->serialize(stream, m_specularReflectance.get()); + m_eta.serialize(stream); + m_k.serialize(stream); + } + + std::string toString() const { + std::ostringstream oss; + oss << "RoughConductor[" << endl + << " name = \"" << getName() << "\"," << endl + << " distribution = " << m_distribution.toString() << "," << endl + << " alphaU = " << indent(m_alphaU->toString()) << "," << endl + << " alphaV = " << indent(m_alphaV->toString()) << "," << endl + << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl + << " eta = " << m_eta.toString() << "," << endl + << " k = " << m_k.toString() << endl + << "]"; + return oss.str(); + } + + Shader *createShader(Renderer *renderer) const; + + MTS_DECLARE_CLASS() +private: + MicrofacetDistribution m_distribution; + ref m_specularReflectance; + ref m_alphaU, m_alphaV; + Spectrum m_eta, m_k; +}; + +/* Fake conductor 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 translucent boundary */ +class RoughConductorShader : public Shader { +public: + RoughConductorShader(Renderer *renderer) : + Shader(renderer, EBSDFShader) { + m_flags = ETransparent; + } + + void generateCode(std::ostringstream &oss, + const std::string &evalName, + const std::vector &depNames) const { + oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " return vec3(0.08);" << endl + << "}" << endl + << endl + << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl + << " return " << evalName << "(uv, wi, wo);" << endl + << "}" << endl; + } + MTS_DECLARE_CLASS() +}; + +Shader *RoughConductor::createShader(Renderer *renderer) const { + return new RoughConductorShader(renderer); +} + +MTS_IMPLEMENT_CLASS(RoughConductorShader, false, Shader) +MTS_IMPLEMENT_CLASS_S(RoughConductor, false, BSDF) +MTS_EXPORT_PLUGIN(RoughConductor, "Rough conductor BRDF"); +MTS_NAMESPACE_END diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp index 8824d375..875c228a 100644 --- a/src/bsdfs/roughdielectric.cpp +++ b/src/bsdfs/roughdielectric.cpp @@ -24,9 +24,10 @@ MTS_NAMESPACE_BEGIN -/* Suggestion by Bruce Walter: sample using a slightly wider - density function. This in practice limits the importance - weights to values <= 4. See also \ref sample() */ +/* Suggestion by Bruce Walter: sample the model using a slightly + wider density function. This in practice limits the importance + weights to values <= 4. +*/ #define ENLARGE_LOBE_TRICK 1 /*! \plugin{roughdielectric}{Rough dielectric material} @@ -140,8 +141,7 @@ MTS_NAMESPACE_BEGIN */ class RoughDielectric : public BSDF { public: - RoughDielectric(const Properties &props) - : BSDF(props) { + RoughDielectric(const Properties &props) : BSDF(props) { m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); m_specularTransmittance = new ConstantSpectrumTexture( @@ -186,22 +186,17 @@ public: m_intIOR = stream->readFloat(); m_extIOR = stream->readFloat(); - m_components.push_back( - EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler); - m_components.push_back( - EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler); - m_usesRayDifferentials = m_alphaU->usesRayDifferentials() || m_alphaV->usesRayDifferentials() || m_specularReflectance->usesRayDifferentials() || m_specularTransmittance->usesRayDifferentials(); + configure(); } void configure() { unsigned int extraFlags = 0; - m_components.clear(); if (m_alphaU != m_alphaV) { extraFlags |= EAnisotropic; if (m_distribution.getType() != @@ -212,6 +207,7 @@ public: "(named \"as\")"); } + m_components.clear(); m_components.push_back( EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler | extraFlags); m_components.push_back( @@ -298,7 +294,7 @@ public: alphaV = m_distribution.transformRoughness( m_alphaV->getValue(bRec.its).average()); - /* Microsurface normal distribution */ + /* Evaluate the microsurface normal distribution */ const Float D = m_distribution.eval(H, alphaU, alphaV); if (D == 0) return Spectrum(0.0f); @@ -388,16 +384,13 @@ public: alphaV = m_distribution.transformRoughness( m_alphaV->getValue(bRec.its).average()); -#if defined(ENLARGE_LOBE_TRICK) - /* Suggestion by Bruce Walter: sample using a slightly wider - density function. This in practice limits the importance - weights to values <= 4. See also \ref sample() */ +#if ENLARGE_LOBE_TRICK == 1 Float factor = (1.2f - 0.2f * std::sqrt( std::abs(Frame::cosTheta(bRec.wi)))); alphaU *= factor; alphaV *= factor; #endif - /* Microsurface normal sampling density */ + /* Evaluate the microsurface normal sampling density */ Float prob = m_distribution.pdf(H, alphaU, alphaV); if (sampleTransmission && sampleReflection) { @@ -476,10 +469,7 @@ public: alphaV = m_distribution.transformRoughness( m_alphaV->getValue(bRec.its).average()); - /* Suggestion by Bruce Walter: sample using a slightly wider - density function. This in practice limits the importance - weights to values <= 4. See also \ref sample() */ -#if defined(ENLARGE_LOBE_TRICK) +#if ENLARGE_LOBE_TRICK == 1 Float factor = (1.2f - 0.2f * std::sqrt( std::abs(Frame::cosTheta(bRec.wi)))); Float sampleAlphaU = alphaU * factor, @@ -607,10 +597,7 @@ public: alphaV = m_distribution.transformRoughness( m_alphaV->getValue(bRec.its).average()); - /* Suggestion by Bruce Walter: sample using a slightly wider - density function. This in practice limits the importance - weights to values <= 4. See also \ref sample() */ -#if defined(ENLARGE_LOBE_TRICK) +#if ENLARGE_LOBE_TRICK == 1 Float factor = (1.2f - 0.2f * std::sqrt( std::abs(Frame::cosTheta(bRec.wi)))); Float sampleAlphaU = alphaU * factor, @@ -702,6 +689,7 @@ public: std::string toString() const { std::ostringstream oss; oss << "RoughDielectric[" << endl + << " name = \"" << getName() << "\"," << endl << " distribution = " << m_distribution.toString() << "," << endl << " alphaU = " << indent(m_alphaU->toString()) << "," << endl << " alphaV = " << indent(m_alphaV->toString()) << "," << endl diff --git a/src/bsdfs/roughmetal.cpp b/src/bsdfs/roughmetal.cpp deleted file mode 100644 index faebbd2e..00000000 --- a/src/bsdfs/roughmetal.cpp +++ /dev/null @@ -1,204 +0,0 @@ -/* - 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 - -MTS_NAMESPACE_BEGIN - -/** - * Rough metal BRDF model based on - * "Microfacet Models for Refraction through Rough Surfaces" - * by Bruce Walter, Stephen R. Marschner, Hongsong Li - * and Kenneth E. Torrance. - * - * This is similar to the 'microfacet' implementation, but - * the Fresnel term is now that of a conductor. - */ -class RoughMetal : public BSDF { -public: - RoughMetal(const Properties &props) - : BSDF(props) { - m_specularReflectance = new ConstantSpectrumTexture( - props.getSpectrum("specularReflectance", Spectrum(1.0f))); - m_alphaB = props.getFloat("alphaB", .1f); - m_ior = props.getSpectrum("ior", Spectrum(0.370f)); /* Gold */ - m_k = props.getSpectrum("k", Spectrum(2.820f)); - - m_componentCount = 1; - m_type = new unsigned int[m_componentCount]; - m_combinedType = m_type[0] = EGlossyReflection | EFrontSide; - m_usesRayDifferentials = false; - } - - RoughMetal(Stream *stream, InstanceManager *manager) - : BSDF(stream, manager) { - m_specularReflectance = static_cast(manager->getInstance(stream)); - m_alphaB = stream->readFloat(); - m_ior = Spectrum(stream); - m_k = Spectrum(stream); - - m_componentCount = 1; - m_type = new unsigned int[m_componentCount]; - m_combinedType = m_type[0] = EGlossyReflection | EFrontSide; - m_usesRayDifferentials = - m_specularReflectance->usesRayDifferentials(); - } - - virtual ~RoughMetal() { - delete[] m_type; - } - - /** - * Beckmann distribution function for gaussian random surfaces - * \param thetaM Tangent of the angle between M and N. - */ - Float beckmannD(const Vector &m) const { - Float ex = Frame::tanTheta(m) / m_alphaB; - return std::exp(-(ex*ex)) / (M_PI * m_alphaB*m_alphaB * - std::pow(Frame::cosTheta(m), (Float) 4.0f)); - } - - /** - * Sample microsurface normals according to - * the Beckmann distribution - */ - Normal sampleBeckmannD(Point2 sample) const { - Float thetaM = std::atan(std::sqrt(-m_alphaB*m_alphaB - * std::log(1.0f - sample.x))); - Float phiM = (2.0f * M_PI) * sample.y; - return Normal(sphericalDirection(thetaM, phiM)); - } - - /** - * Smith's shadow-masking function G1 for the Beckmann distribution - * \param m The microsurface normal - * \param v An arbitrary direction - */ - Float smithBeckmannG1(const Vector &v, const Vector &m) const { - if (dot(v, m) * Frame::cosTheta(v) <= 0) - return 0.0; - - const Float tanTheta = Frame::tanTheta(v); - - if (tanTheta == 0.0f) - return 1.0f; - - const Float a = 1.0f / (m_alphaB * tanTheta); - const Float aSqr = a * a; - - if (a >= 1.6f) - return 1.0f; - - return (3.535f * a + 2.181f * aSqr) / - (1.0f + 2.276f * a + 2.577f * aSqr); - } - - inline Vector reflect(const Vector &wi, const Normal &n) const { - return Vector(n*(2.0f*dot(n, wi))) - wi; - } - - Spectrum f(const BSDFQueryRecord &bRec) const { - if (!(bRec.typeMask & m_combinedType) - || bRec.wi.z <= 0 || bRec.wo.z <= 0) - return Spectrum(0.0f);\ - - Vector Hr = normalize(bRec.wi+bRec.wo); - - /* Fresnel factor */ - Spectrum F = fresnelConductor(dot(bRec.wi, Hr), m_ior, m_k); - - /* Microsurface normal distribution */ - Float D = beckmannD(Hr); - /* Smith's shadow-masking function for the Beckmann distribution */ - Float G = smithBeckmannG1(bRec.wi, Hr) * smithBeckmannG1(bRec.wo, Hr); - /* Calculate the total amount of specular reflection */ - Spectrum specRef = F * (D * G / - (4.0f * Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo))); - - return m_specularReflectance->getValue(bRec.its) * specRef; - } - - Float pdf(const BSDFQueryRecord &bRec) const { - if (bRec.wi.z <= 0 || bRec.wo.z <= 0) - return 0.0f; - - Vector Hr = normalize(bRec.wi + bRec.wo); - /* Jacobian of the half-direction transform. */ - Float dwhr_dwo = 1.0f / (4.0f * absDot(bRec.wo, Hr)); - return beckmannD(Hr) * Frame::cosTheta(Hr) * dwhr_dwo; - } - - Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { - if (bRec.wi.z <= 0) - return Spectrum(0.0f); - - /* Sample M, the microsurface normal */ - Normal m = sampleBeckmannD(sample); - /* Perfect specular reflection along the microsurface normal */ - bRec.wo = reflect(bRec.wi, m); - - bRec.sampledComponent = 1; - bRec.sampledType = EGlossyReflection; - - if (bRec.wo.z <= 0) - return Spectrum(0.0f); - - return f(bRec) / pdf(bRec); - } - - void serialize(Stream *stream, InstanceManager *manager) const { - BSDF::serialize(stream, manager); - - manager->serialize(stream, m_specularReflectance.get()); - stream->writeFloat(m_alphaB); - m_ior.serialize(stream); - m_k.serialize(stream); - } - - void addChild(const std::string &name, ConfigurableObject *child) { - if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularReflectance") { - m_specularReflectance = static_cast(child); - m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials(); - } else { - BSDF::addChild(name, child); - } - } - - - std::string toString() const { - std::ostringstream oss; - oss << "RoughMetal[" << endl - << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << std::endl - << " ior = " << m_ior.toString() << "," << std::endl - << " k = " << m_k.toString() << "," << std::endl - << " alphaB = " << m_alphaB << std::endl - << "]"; - return oss.str(); - } - - MTS_DECLARE_CLASS() -private: - ref m_specularReflectance; - Float m_alphaB; - Spectrum m_ior, m_k; -}; - -MTS_IMPLEMENT_CLASS_S(RoughMetal, false, BSDF) -MTS_EXPORT_PLUGIN(RoughMetal, "Rough metal BRDF"); -MTS_NAMESPACE_END diff --git a/src/tests/test_chisquare.cpp b/src/tests/test_chisquare.cpp index e037e931..f2c882a7 100644 --- a/src/tests/test_chisquare.cpp +++ b/src/tests/test_chisquare.cpp @@ -137,16 +137,16 @@ public: if (f.isZero() || pdfVal == 0 || pdfVal2 == 0) { if (!sampled.isZero()) - Log(EWarn, "Inconsistency (1): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i", - f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure); + Log(EWarn, "Inconsistency (1): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s", + f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str()); #if defined(MTS_DEBUG_FP) disableFPExceptions(); #endif return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle); } else if (sampled.isZero()) { if ((!f.isZero() && pdfVal != 0) || (!f2.isZero() && pdfVal2 != 0)) - Log(EWarn, "Inconsistency (2): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i", - f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure); + Log(EWarn, "Inconsistency (2): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s", + f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str()); #if defined(MTS_DEBUG_FP) disableFPExceptions(); #endif @@ -155,8 +155,8 @@ public: Spectrum sampled2 = f/pdfVal, evaluated = f2/pdfVal2; if (!sampled.isValid() || !sampled2.isValid() || !evaluated.isValid()) { - Log(EWarn, "Ooops: f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i", - f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure); + Log(EWarn, "Ooops: f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s", + f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str()); return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle); }