/* This file is part of Mitsuba, a physically based rendering system. Copyright (c) 2007-2012 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 MTS_NAMESPACE_BEGIN /*!\plugin{diffuse}{Smooth diffuse material} * \order{1} * \icon{bsdf_diffuse} * \parameters{ * \parameter{reflectance}{\Spectrum\Or\Texture}{ * Specifies the diffuse albedo of the * material \default{0.5} * } * } * * \renderings{ * \rendering{Homogeneous reflectance, see \lstref{diffuse-uniform}} * {bsdf_diffuse_plain} * \rendering{Textured reflectance, see \lstref{diffuse-textured}} * {bsdf_diffuse_textured} * } * * The smooth diffuse material (also referred to as ``Lambertian'') * represents an ideally diffuse material with a user-specified amount of * reflectance. Any received illumination is scattered so that the surface * looks the same independently of the direction of observation. * * Apart from a homogeneous reflectance value, the plugin can also accept * a nested or referenced texture map to be used as the source of reflectance * information, which is then mapped onto the shape based on its UV * parameterization. When no parameters are specified, the model uses the default * of 50% reflectance. * * Note that this material is one-sided---that is, observed from the * back side, it will be completely black. If this is undesirable, * consider using the \pluginref{twosided} BRDF adapter plugin. * \vspace{4mm} * * \begin{xml}[caption={A diffuse material, whose reflectance is specified * as an sRGB color}, label=lst:diffuse-uniform] * * * * \end{xml} * * \begin{xml}[caption=A diffuse material with a texture map, * label=lst:diffuse-textured] * * * * * * \end{xml} */ class SmoothDiffuse : public BSDF { public: SmoothDiffuse(const Properties &props) : BSDF(props) { /* For better compatibility with other models, support both 'reflectance' and 'diffuseReflectance' as parameter names */ m_reflectance = new ConstantSpectrumTexture(props.getSpectrum( props.hasProperty("reflectance") ? "reflectance" : "diffuseReflectance", Spectrum(.5f))); } SmoothDiffuse(Stream *stream, InstanceManager *manager) : BSDF(stream, manager) { m_reflectance = static_cast(manager->getInstance(stream)); configure(); } void configure() { /* Verify the input parameter and fix them if necessary */ m_reflectance = ensureEnergyConservation(m_reflectance, "reflectance", 1.0f); m_components.clear(); if (m_reflectance->getMaximum().max() > 0) m_components.push_back(EDiffuseReflection | EFrontSide | (m_reflectance->isConstant() ? 0 : ESpatiallyVarying)); m_usesRayDifferentials = m_reflectance->usesRayDifferentials(); BSDF::configure(); } Spectrum getDiffuseReflectance(const Intersection &its) const { return m_reflectance->eval(its); } Spectrum eval(const BSDFSamplingRecord &bRec, EMeasure measure) const { if (!(bRec.typeMask & EDiffuseReflection) || measure != ESolidAngle || Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0) return Spectrum(0.0f); return m_reflectance->eval(bRec.its) * (INV_PI * Frame::cosTheta(bRec.wo)); } Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const { if (!(bRec.typeMask & EDiffuseReflection) || measure != ESolidAngle || Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0) return 0.0f; return Warp::squareToCosineHemispherePdf(bRec.wo); } Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const { if (!(bRec.typeMask & EDiffuseReflection) || Frame::cosTheta(bRec.wi) <= 0) return Spectrum(0.0f); bRec.wo = Warp::squareToCosineHemisphere(sample); bRec.eta = 1.0f; bRec.sampledComponent = 0; bRec.sampledType = EDiffuseReflection; return m_reflectance->eval(bRec.its); } Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const { if (!(bRec.typeMask & EDiffuseReflection) || Frame::cosTheta(bRec.wi) <= 0) return Spectrum(0.0f); bRec.wo = Warp::squareToCosineHemisphere(sample); bRec.eta = 1.0f; bRec.sampledComponent = 0; bRec.sampledType = EDiffuseReflection; pdf = Warp::squareToCosineHemispherePdf(bRec.wo); return m_reflectance->eval(bRec.its); } void addChild(const std::string &name, ConfigurableObject *child) { if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && (name == "reflectance" || name == "diffuseReflectance")) { m_reflectance = static_cast(child); } else { BSDF::addChild(name, child); } } void serialize(Stream *stream, InstanceManager *manager) const { BSDF::serialize(stream, manager); manager->serialize(stream, m_reflectance.get()); } Float getRoughness(const Intersection &its, int component) const { return std::numeric_limits::infinity(); } std::string toString() const { std::ostringstream oss; oss << "SmoothDiffuse[" << endl << " id = \"" << getID() << "\"," << endl << " reflectance = " << indent(m_reflectance->toString()) << endl << "]"; return oss.str(); } Shader *createShader(Renderer *renderer) const; MTS_DECLARE_CLASS() private: ref m_reflectance; }; // ================ Hardware shader implementation ================ class SmoothDiffuseShader : public Shader { public: SmoothDiffuseShader(Renderer *renderer, const Texture *reflectance) : Shader(renderer, EBSDFShader), m_reflectance(reflectance) { m_reflectanceShader = renderer->registerShaderForResource(m_reflectance.get()); } bool isComplete() const { return m_reflectanceShader.get() != NULL; } void cleanup(Renderer *renderer) { renderer->unregisterShaderForResource(m_reflectance.get()); } void putDependencies(std::vector &deps) { deps.push_back(m_reflectanceShader.get()); } void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl << " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 0.0)" << endl << " return vec3(0.0);" << endl << " return " << depNames[0] << "(uv) * inv_pi * cosTheta(wo);" << endl << "}" << endl << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << " return " << evalName << "(uv, wi, wo);" << endl << "}" << endl; } MTS_DECLARE_CLASS() private: ref m_reflectance; ref m_reflectanceShader; }; Shader *SmoothDiffuse::createShader(Renderer *renderer) const { return new SmoothDiffuseShader(renderer, m_reflectance.get()); } MTS_IMPLEMENT_CLASS(SmoothDiffuseShader, false, Shader) MTS_IMPLEMENT_CLASS_S(SmoothDiffuse, false, BSDF) MTS_EXPORT_PLUGIN(SmoothDiffuse, "Smooth diffuse BRDF") MTS_NAMESPACE_END