/* 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 MTS_NAMESPACE_BEGIN /*!\plugin{conductor}{Smooth conductor} * \order{5} * \parameters{ * \parameter{material}{\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{material}}} * \parameter{k}{\Spectrum}{Imaginary part of the material's index of * refraction, also known as absorption coefficient. * \default{based on the value of \texttt{material}}} * \lastparameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{ * Optional factor used to modulate the reflectance component * \default{1.0}} * } * \renderings{ * \rendering{Measured copper material (the default)} * {bsdf_conductor_copper.jpg} * \rendering{Measured gold material (\lstref{conductor-gold})} * {bsdf_conductor_gold.jpg} * } * This plugin implements a perfectly smooth interface to a conducting material, * such as a metal. For a similar model that instead describes a rough surface * microstructure, take a look at the seperately available * \pluginref{roughconductor} plugin. * In contrast to dielectric materials, conductors do not transmit * any light. Their index of refraction is complex-valued and tends to undergo * considerable changes throughout the visible color spectrum. * * To faciliate the tedious task of specifying spectrally-varying index of * refraction information, Mitsuba ships with a set of measured data for * several materials, where visible-spectrum information was publicly * available\footnote{ * These index of refraction values are identical to the data distributed * with PBRT. They are originally from the Luxpop database * (\url{www.luxpop.com}) and are based on data by Palik et al. * \cite{Palik1998Handbook} and measurements of atomic scattering factors * made by the Center For X-Ray Optics (CXRO) at Berkeley and the * Lawrence Livermore National Laboratory (LLNL). * }. * * Note that \tblref{conductor-iors} also includes several popular optical * coatings, which are not actually conductors. These materials can also * be used with this plugin, though note that the plugin will ignore any * refraction component that the actual material might have had. * The table also contains a few birefingent materials, which are split into * separate measurements correponding to their two indices of * refraction (named ``ordinary'' and ``extraordinary ray''). * * When using this plugin, you should ideally compile Mitsuba with support for * spectral rendering to get the most accurate results. While it also works * in RGB mode, the computations will be much more approximate in this case. * Also 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 material configuration for a smooth conductor with * measured gold data, label=lst:conductor-gold] * * * * * * \end{xml} * \vspace{5mm} * It is also possible to load spectrally varying index of refraction data from * two external files containing the real and imaginary components, * respectively (see \secref{format-spectra} for details on the file * format): * \begin{xml}[caption=Rendering a smooth conductor with custom data] * * * * * * * \end{xml} * \vspace{1.5cm} * \begin{table}[hb!] * \centering * \scriptsize * \begin{tabular}{>{\ttfamily}llp{1mm}>{\ttfamily}ll} * \toprule * \rmfamily\textbf{Preset(s)} & \textbf{Description} && * \rmfamily\textbf{Preset(s)} & \textbf{Description}\\ * \cmidrule{1-2} \cmidrule{4-5} * a-C & Amorphous carbon && Na\_palik & Sodium \\ * Ag & Silver && Nb, Nb\_palik & Niobium \\ * Al & Aluminium && Ni\_palik & Nickel \\ * AlAs, AlAs\_palik & Cubic aluminium arsenide && Rh, Rh\_palik & Rhodium \\ * AlSb, AlSb\_palik & Cubic aluminium antimonide && Se, Se\_palik & Selenium (ord. ray) \\ * Au & Gold && Se-e, Se-e\_palik & Selenium (extr. ray) \\ * Be, Be\_palik & Polycrystalline beryllium && SiC, SiC\_palik & Hexagonal silicon carbide \\ * Cr & Chromium && SnTe, SnTe\_palik & Tin telluride\\ * CsI, CsI\_palik & Cubic caesium iodide && Ta, Ta\_palik & Tantalum \\ * Cu, Cu\_palik & Copper && Te, Te\_palik & Trigonal tellurium (ord. ray) \\ * Cu2O, Cu2O\_palik & Copper (I) oxide && Te-e, Te-e\_palik & Trigonal tellurium (extr. ray) \\ * CuO, CuO\_palik & Copper (II) oxide && ThF4, ThF4\_palik & Polycryst. thorium (IV) fluoride \\ * d-C, d-C\_palik & Cubic diamond && TiC, TiC\_palik & Polycrystalline titanium carbide \\ * Hg, Hg\_palik & Mercury && TiN, TiN\_palik & Titanium nitride \\ * HgTe, HgTe\_palik & Mercury telluride && TiO2, TiO2\_palik & Tetragonal titan. dioxide (ord. ray) \\ * Ir, Ir\_palik & Iridium && TiO2-e, TiO2-e\_palik & Tetragonal titan. dioxide (extr. ray) \\ * K, K\_palik & Polycrystalline potassium && VC, VC\_palik & Vanadium carbide \\ * Li, Li\_palik & Lithium && V\_palik & Vanadium \\ * MgO, MgO\_palik & Magnesium oxide && VN, VN\_palik & Vanadium nitride \\ * Mo, Mo\_palik & Molybdenum && W & Tungsten\\ * \bottomrule * \end{tabular} * \caption{ * \label{tbl:conductor-iors} * This table lists all supported materials that can be passed into the * \pluginref{conductor} and \pluginref{roughconductor} plugins. Note that * some of them are not actually conductors---this is not a problem, * they can be used regardless (though only the reflection component and * no transmission will be simulated). In most cases, there are * multiple entries for each material, which represent measurements by * different authors. * } * \end{table} */ class SmoothConductor : public BSDF { public: SmoothConductor(const Properties &props) : BSDF(props) { ref fResolver = Thread::getThread()->getFileResolver(); m_specularReflectance = new ConstantSpectrumTexture( props.getSpectrum("specularReflectance", Spectrum(1.0f))); std::string material = props.getString("material", "Cu"); Spectrum materialEta, materialK; materialEta.fromContinuousSpectrum(InterpolatedSpectrum( fResolver->resolve("data/ior/" + material + ".eta.spd"))); materialK.fromContinuousSpectrum(InterpolatedSpectrum( fResolver->resolve("data/ior/" + material + ".k.spd"))); m_eta = props.getSpectrum("eta", materialEta); m_k = props.getSpectrum("k", materialK); m_components.push_back(EDeltaReflection | EFrontSide); m_usesRayDifferentials = false; } SmoothConductor(Stream *stream, InstanceManager *manager) : BSDF(stream, manager) { m_specularReflectance = static_cast(manager->getInstance(stream)); m_eta = Spectrum(stream); m_k = Spectrum(stream); m_components.push_back(EDeltaReflection | EFrontSide); m_usesRayDifferentials = m_specularReflectance->usesRayDifferentials(); } virtual ~SmoothConductor() { } void serialize(Stream *stream, InstanceManager *manager) const { BSDF::serialize(stream, manager); manager->serialize(stream, m_specularReflectance.get()); m_eta.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); } } void configure() { BSDF::configure(); /* Verify the input parameters and fix them if necessary */ m_specularReflectance = ensureEnergyConservation( m_specularReflectance, "specularReflectance", 1.0f); } /// 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 sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); /* Verify that the provided direction pair matches an ideal specular reflection; tolerate some roundoff errors */ if (!sampleReflection || measure != EDiscrete || Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || std::abs(1 - dot(reflect(bRec.wi), bRec.wo)) > Epsilon) return Spectrum(0.0f); return m_specularReflectance->getValue(bRec.its) * fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); } Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); /* Verify that the provided direction pair matches an ideal specular reflection; tolerate some roundoff errors */ if (!sampleReflection || measure != EDiscrete || Frame::cosTheta(bRec.wi) <= 0 || Frame::cosTheta(bRec.wo) <= 0 || std::abs(1 - dot(reflect(bRec.wi), bRec.wo)) > Epsilon) return 0.0f; return 1.0f; } Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); if (!sampleReflection || Frame::cosTheta(bRec.wi) <= 0) return Spectrum(0.0f); bRec.sampledComponent = 0; bRec.sampledType = EDeltaReflection; bRec.wo = reflect(bRec.wi); return m_specularReflectance->getValue(bRec.its) * fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); } Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &sample) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); if (!sampleReflection || Frame::cosTheta(bRec.wi) <= 0) return Spectrum(0.0f); bRec.sampledComponent = 0; bRec.sampledType = EDeltaReflection; bRec.wo = reflect(bRec.wi); pdf = 1; return m_specularReflectance->getValue(bRec.its) * fresnelConductor(Frame::cosTheta(bRec.wi), m_eta, m_k); } std::string toString() const { std::ostringstream oss; oss << "SmoothConductor[" << endl << " name = \"" << getName() << "\"," << endl << " eta = " << m_eta.toString() << "," << endl << " k = " << m_k.toString() << "," << endl << " specularReflectance = " << indent(m_specularReflectance->toString()) << endl << "]"; return oss.str(); } Shader *createShader(Renderer *renderer) const; MTS_DECLARE_CLASS() private: ref m_specularReflectance; Spectrum m_eta; Spectrum m_k; }; /* Smooth 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 specularly-reflecting conductor. The code below is an isotropic version of the shader in roughconductor.cpp, with \alpha fixed to 0.4f */ class SmoothConductorShader : public Shader { public: SmoothConductorShader(Renderer *renderer, const Texture *specularReflectance, const Spectrum &eta, const Spectrum &k) : Shader(renderer, EBSDFShader), m_specularReflectance(specularReflectance) { m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get()); /* Compute the reflectance at perpendicular incidence */ m_R0 = fresnelConductor(1.0f, eta, k); m_alpha = 0.4f; } bool isComplete() const { return m_specularReflectanceShader.get() != NULL; } void putDependencies(std::vector &deps) { deps.push_back(m_specularReflectanceShader.get()); } void cleanup(Renderer *renderer) { renderer->unregisterShaderForResource(m_specularReflectance.get()); } void resolve(const GPUProgram *program, const std::string &evalName, std::vector ¶meterIDs) const { parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); } void bind(GPUProgram *program, const std::vector ¶meterIDs, int &textureUnitOffset) const { program->setParameter(parameterIDs[0], m_R0); } void generateCode(std::ostringstream &oss, const std::string &evalName, const std::vector &depNames) const { oss << "uniform vec3 " << evalName << "_R0;" << endl << endl << "float " << evalName << "_D(vec3 m, float alpha) {" << endl << " alpha = 2 / (alpha * alpha) - 2;" << endl << " return (alpha + 2) * 0.15915 * pow(cosTheta(m), alpha);" << 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 << "vec3 " << evalName << "_schlick(vec3 wi) {" << endl << " float ct = cosTheta(wi), ctSqr = ct*ct," << endl << " ct5 = ctSqr*ctSqr*ct;" << endl << " return " << evalName << "_R0 + (vec3(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 reflectance = " << depNames[0] << "(uv);" << endl << " float D = " << evalName << "_D(H, " << m_alpha << ")" << ";" << endl << " float G = " << evalName << "_G(H, wi, wo);" << endl << " vec3 Fr = " << evalName << "_schlick(wi);" << endl << " return reflectance * Fr * (D * G / (4*cosTheta(wi)));" << endl << "}" << endl << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << " return " << evalName << "_R0 * 0.31831 * cosTheta(wo);"<< endl << "}" << endl; } MTS_DECLARE_CLASS() private: ref m_specularReflectance; ref m_specularReflectanceShader; Spectrum m_R0; Float m_alpha; }; Shader *SmoothConductor::createShader(Renderer *renderer) const { return new SmoothConductorShader(renderer, m_specularReflectance.get(), m_eta, m_k); } MTS_IMPLEMENT_CLASS(SmoothConductorShader, false, Shader) MTS_IMPLEMENT_CLASS_S(SmoothConductor, false, BSDF) MTS_EXPORT_PLUGIN(SmoothConductor, "Smooth conductor"); MTS_NAMESPACE_END