/* 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 /*! \plugin{varnish}{Smooth varnish layer} * * \parameters{ * \parameter{intIOR}{\Float}{Interior index of refraction \default{1.5046}} * \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}} * } * * XXX cancel out cosine factors? * XXX did I get the measure conversion terms right? * XXX allow testing interface to verify delta components */ class SmoothVarnish : public BSDF { public: SmoothVarnish(const Properties &props) : BSDF(props) { /* Specifies the internal index of refraction at the interface */ m_intIOR = props.getFloat("intIOR", 1.5046f); /* Specifies the external index of refraction at the interface */ m_extIOR = props.getFloat("extIOR", 1); /* Specifies the layer's thickness using the inverse units of sigmaT */ m_thickness = props.getFloat("thickness", 1); /* Specifies the attenuation within the varnish layer */ m_sigmaT = new ConstantSpectrumTexture( props.getSpectrum("sigmaT", Spectrum(0.0f))); } SmoothVarnish(Stream *stream, InstanceManager *manager) : BSDF(stream, manager) { m_intIOR = stream->readFloat(); m_extIOR = stream->readFloat(); m_thickness = stream->readFloat(); m_nested = static_cast(manager->getInstance(stream)); m_sigmaT = static_cast(manager->getInstance(stream)); configure(); } virtual ~SmoothVarnish() { delete[] m_type; } void configure() { if (!m_nested) Log(EError, "A child BSDF instance is required"); if (m_nested->getType() & BSDF::ETransmission) Log(EError, "Tried to put a smooth varnish layer on top of a BSDF " "with a transmission component -- this is currently not allowed!"); if (m_nested->getType() & BSDF::EDelta) Log(EError, "Tried to put a smooth varnish layer on top of a material with a " "Dirac delta distribution -- this is currently not allowed!"); if (m_type) delete[] m_type; m_componentCount = 1 + m_nested->getComponentCount(); m_type = new unsigned int[m_componentCount]; m_type[0] = EDeltaReflection | EFrontSide; m_combinedType = m_type[0]; for (int i=0; igetComponentCount(); ++i) { m_type[i+1] = m_nested->getType(i); m_combinedType |= m_type[i+1]; } m_usesRayDifferentials = m_nested->usesRayDifferentials() || m_sigmaT->usesRayDifferentials(); } void serialize(Stream *stream, InstanceManager *manager) const { BSDF::serialize(stream, manager); stream->writeFloat(m_intIOR); stream->writeFloat(m_extIOR); stream->writeFloat(m_thickness); manager->serialize(stream, m_nested.get()); manager->serialize(stream, m_sigmaT.get()); } void addChild(const std::string &name, ConfigurableObject *child) { if (child->getClass()->derivesFrom(MTS_CLASS(BSDF))) { if (m_nested != NULL) Log(EError, "Only a single nested BRDF can be added!"); m_nested = static_cast(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); } /** * \brief Refraction in local coordinates * * To be used when some of the data is already available */ inline Vector refract(const Vector &wi, Float eta, Float cosThetaT) const { return Vector(-eta*wi.x, -eta*wi.y, cosThetaT); } /// Fully complete local coordinate refraction routine inline Vector refract(const Vector &wi, Float &Fr) const { Float cosThetaI = Frame::cosTheta(wi), etaI = m_extIOR, etaT = m_intIOR; bool entering = cosThetaI > 0.0f; /* Determine the respective indices of refraction */ if (!entering) std::swap(etaI, etaT); /* Using Snell's law, calculate the squared sine of the angle between the normal and the transmitted ray */ Float eta = etaI / etaT, sinThetaTSqr = eta*eta * Frame::sinTheta2(wi); Float cosThetaT = 0; if (sinThetaTSqr >= 1.0f) { /* Total internal reflection */ Fr = 1.0f; return Vector(0.0f); } else { cosThetaT = std::sqrt(1.0f - sinThetaTSqr); /* Compute the Fresnel refletance */ Fr = fresnelDielectric(std::abs(cosThetaI), cosThetaT, etaI, etaT); if (entering) cosThetaT = -cosThetaT; return Vector(-eta*wi.x, -eta*wi.y, cosThetaT); } } Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &_sample) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll) && (bRec.component == -1 || (bRec.component > 0 && bRec.component < m_nested->getComponentCount() + 1)); if ((!sampleNested && !sampleReflection) || Frame::cosTheta(bRec.wi) < 0) return Spectrum(0.0f); Float cosThetaI = Frame::cosTheta(bRec.wi), etaI = m_extIOR, etaT = m_intIOR; /* Using Snell's law, calculate the squared sine of the angle between the normal and the transmitted ray */ Float eta = etaI / etaT, sinThetaTSqr = eta*eta * Frame::sinTheta2(bRec.wi); Float Fr, FrOut, cosThetaT = 0; if (sinThetaTSqr >= 1.0f) { /* Total internal reflection */ Fr = 1.0f; } else { cosThetaT = std::sqrt(1.0f - sinThetaTSqr); /* Compute the Fresnel refletance */ Fr = fresnelDielectric(cosThetaI, cosThetaT, etaI, etaT); cosThetaT = -cosThetaT; } Point2 sample(_sample); if (sampleNested && sampleReflection) { if (sample.x <= Fr) { bRec.sampledComponent = 0; bRec.sampledType = EDeltaReflection; bRec.wo = reflect(bRec.wi); pdf = Fr * std::abs(Frame::cosTheta(bRec.wo)); return Spectrum(Fr); } else { Vector wiBackup = bRec.wi; bRec.wi = -refract(bRec.wi, eta, cosThetaT); sample.x = (sample.x - Fr) / (1 - Fr); Spectrum result = m_nested->sample(bRec, pdf, sample); if (result.isZero()) return Spectrum(0.0f); bRec.sampledComponent++; Spectrum sigmaT = m_sigmaT->getValue(bRec.its) * m_thickness; if (!sigmaT.isZero()) result *= (-sigmaT * (1/std::abs(Frame::cosTheta(bRec.wi)) + 1/std::abs(Frame::cosTheta(bRec.wo)))).exp(); Float cosThetaWoPrime = Frame::cosTheta(bRec.wo); bRec.wi = wiBackup; bRec.wo = refract(-bRec.wo, FrOut); if (FrOut == 1) return Spectrum(0.0f); pdf *= (1 - Fr) * eta * eta; result *= (1 - Fr) * (1 - FrOut) * std::abs(cosThetaWoPrime * / Frame::cosTheta(bRec.wo)); return result; } } else if (sampleReflection) { bRec.sampledComponent = 0; bRec.sampledType = EDeltaReflection; bRec.wo = reflect(bRec.wi); pdf = std::abs(Frame::cosTheta(bRec.wo)); return Spectrum(Fr); } else { if (Fr == 1.0f) /* Total internal reflection */ return Spectrum(0.0f); Vector wiBackup = bRec.wi; bRec.wi = -refract(bRec.wi, eta, cosThetaT); sample.x = (sample.x - Fr) / (1 - Fr); Spectrum result = m_nested->sample(bRec, pdf, sample); if (result.isZero()) return Spectrum(0.0f); bRec.sampledComponent++; Spectrum sigmaT = m_sigmaT->getValue(bRec.its) * m_thickness; if (!sigmaT.isZero()) result *= (-sigmaT * (1/std::abs(Frame::cosTheta(bRec.wi)) + 1/std::abs(Frame::cosTheta(bRec.wo)))).exp(); Float cosThetaWoPrime = Frame::cosTheta(bRec.wo); bRec.wi = wiBackup; bRec.wo = refract(-bRec.wo, FrOut); if (FrOut == 1) return Spectrum(0.0f); pdf *= (1 - Fr) * eta * eta; result *= (1 - Fr) * (1 - FrOut) * std::abs(cosThetaWoPrime * / Frame::cosTheta(bRec.wo)); return result; } } Float pdfDelta(const BSDFQueryRecord &bRec) const { bool sampleSpecular = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); if (Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || !sampleSpecular) return 0.0f; bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll) && (bRec.component == -1 || (bRec.component > 0 && bRec.component < m_nested->getComponentCount() + 1)); Float pdf = std::abs(Frame::cosTheta(bRec.wo)); if (sampleNested) pdf *= fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); return pdf; } Spectrum fDelta(const BSDFQueryRecord &bRec) const { bool sampleSpecular = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); if (Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || !sampleSpecular) return Spectrum(0.0f); return Spectrum(fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR)); } Float pdf(const BSDFQueryRecord &bRec) const { bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll) && (bRec.component == -1 || (bRec.component > 0 && bRec.component < m_nested->getComponentCount() + 1)); if (Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || !sampleNested) return 0.0f; Float T12, T21; Vector wiPrime = -refract(bRec.wi, T12); Vector woPrime = -refract(bRec.wo, T21); if (T12 == 1 || T21 == 1) /* Total internal reflection */ return 0.0f; BSDFQueryRecord bRec2(bRec); if (bRec2.component != -1) bRec2.component++; bRec2.wi = wiPrime; bRec2.wo = woPrime; Float eta = m_extIOR / m_intIOR; return m_nested->pdf(bRec2) * T12 * eta * eta; } Spectrum f(const BSDFQueryRecord &bRec) const { bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll) && (bRec.component == -1 || (bRec.component > 0 && bRec.component < m_nested->getComponentCount() + 1)); if (Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || !sampleNested) return Spectrum(0.0f); Float T12, T21; Vector wiPrime = -refract(bRec.wi, T12); Vector woPrime = -refract(bRec.wo, T21); if (T12 == 1 || T21 == 1) /* Total internal reflection */ return Spectrum(0.0f); BSDFQueryRecord bRec2(bRec); if (bRec2.component != -1) bRec2.component++; bRec2.wi = wiPrime; bRec2.wo = woPrime; return m_nested->f(bRec2) * T12 * T21; } std::string toString() const { std::ostringstream oss; oss << "SmoothVarnish[" << endl << " intIOR = " << m_intIOR << "," << endl << " extIOR = " << m_extIOR << "," << endl << " sigmaT = " << indent(m_sigmaT->toString()) << "," << endl << " thickness = " << m_thickness << "," << endl << " nested = " << indent(m_nested->toString()) << endl << "]"; return oss.str(); } MTS_DECLARE_CLASS() private: Float m_intIOR, m_extIOR; ref m_nested; ref m_sigmaT; Float m_thickness; }; MTS_IMPLEMENT_CLASS_S(SmoothVarnish, false, BSDF) MTS_EXPORT_PLUGIN(SmoothVarnish, "Smooth varnish layer"); MTS_NAMESPACE_END