diff --git a/src/bsdfs/conductor.cpp b/src/bsdfs/conductor.cpp index 8ed4f6a9..724bba62 100644 --- a/src/bsdfs/conductor.cpp +++ b/src/bsdfs/conductor.cpp @@ -26,17 +26,20 @@ MTS_NAMESPACE_BEGIN * \parameters{ * \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{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}} + * \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} + * \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, @@ -50,18 +53,20 @@ MTS_NAMESPACE_BEGIN * * To faciliate the tedious task of specifying spectrally-varying index of * refraction information, Mitsuba ships with a set of measured data for a - * 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). + * 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. + * 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''). @@ -70,7 +75,8 @@ MTS_NAMESPACE_BEGIN * renderings to get the most accurate results. While it also works in RGB mode, * the computations will be much more approximate in this case. * - * \begin{xml}[caption=Material configuration for a smooth conductor with measured gold data, label=lst:conductor-gold] + * \begin{xml}[caption=Material configuration for a smooth conductor with + * measured gold data, label=lst:conductor-gold] * * * @@ -79,7 +85,8 @@ MTS_NAMESPACE_BEGIN * \end{xml} * \vspace{5mm} * It is also possible to load spectrally varying index of refraction data from - * two external files (see \secref{format-spectra} for details on the file format): + * two external files (see \secref{format-spectra} for details on the file + * format): * \begin{xml}[caption=Rendering a smooth conductor with custom data] * * @@ -121,12 +128,13 @@ MTS_NAMESPACE_BEGIN * \end{tabular} * \caption{ * \label{tbl:conductor-iors} - * This table lists all supported material names that can be passed into the - * \pluginref{conductor} plugin. 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 different measurements. + * 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} */ @@ -196,9 +204,12 @@ public: 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) + 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) * @@ -208,10 +219,15 @@ public: Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); - if (!sampleReflection || measure != EDiscrete || + + /* 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) + Frame::cosTheta(bRec.wo) <= 0 || + std::abs(1 - dot(reflect(bRec.wi), bRec.wo)) > Epsilon) return 0.0f; + return 1.0f; } diff --git a/src/bsdfs/dielectric.cpp b/src/bsdfs/dielectric.cpp index 45fcc263..23f040d6 100644 --- a/src/bsdfs/dielectric.cpp +++ b/src/bsdfs/dielectric.cpp @@ -45,14 +45,14 @@ MTS_NAMESPACE_BEGIN * * This plugin models an interface between two dielectric materials having mismatched * indices of refraction (for instance, water and air). Exterior and interior IOR values - * can each be independently specified, where ``exterior'' refers to the side that contains + * can be independently specified, where ``exterior'' refers to the side that contains * the surface normal. When no parameters are given, the plugin activates the defaults, which * describe a borosilicate glass BK7/air interface. * - * In this model, the microscopic surface structure of the surface is assumed to be perfectly + * In this model, the microscopic structure of the surface is assumed to be perfectly * smooth, resulting in a degenerate\footnote{Meaning that for any given incoming ray of light, * the model always scatters into a discrete set of directions, as opposed to a continuum.} - * BSDF described by a Dirac delta function. For a similar model that instead describes a + * BSDF described by a Dirac delta distribution. For a similar model that instead describes a * rough surface microstructure, take a look at the \pluginref{roughdielectric} plugin. * * \begin{xml}[caption=A simple air-to-water interface, label=lst:dielectric-water] @@ -125,9 +125,13 @@ MTS_NAMESPACE_BEGIN * \end{tabular} * \caption{ * \label{tbl:dielectric-iors} - * This table lists all supported material names that can be passed - * into the \pluginref{dielectric} plugin, along with the associated - * index of refraction at standard conditions. + * This table lists all supported material names along with + * along with the associated index of refraction at + * standard conditions. These can be used with the plugins + * \pluginref{dielectric},\ + * \pluginref{roughdielectric},\ + * \pluginref{plastic}, and + * \pluginref{roughplastic}. * } * \end{table} */ @@ -200,26 +204,66 @@ public: return Vector(-wi.x, -wi.y, wi.z); } - /// Refraction in local coordinates + /// Refraction in local coordinates (reuses computed information) inline Vector refract(const Vector &wi, Float eta, Float cosThetaT) const { return Vector(-eta*wi.x, -eta*wi.y, cosThetaT); } + /// Refraction in local coordinates (full version) + inline Vector refract(const Vector &wi) 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 */ + return Vector(0.0f); + } else { + cosThetaT = std::sqrt(1.0f - sinThetaTSqr); + + if (entering) + cosThetaT = -cosThetaT; + } + + return Vector(-eta*wi.x, -eta*wi.y, cosThetaT); + } + Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { bool sampleReflection = (bRec.typeMask & EDeltaReflection) && (bRec.component == -1 || bRec.component == 0); bool sampleTransmission = (bRec.typeMask & EDeltaTransmission) && (bRec.component == -1 || bRec.component == 1); - bool reflection = Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0; - - if ((reflection && !sampleReflection) || - (!reflection && !sampleTransmission) || measure != EDiscrete) + + /* 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 (measure != EDiscrete || (reflection && !sampleReflection)) return Spectrum(0.0f); - Float fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); + if (!reflection) { + /* Check if the provided direction pair matches an ideal + specular refraction; tolerate some roundoff errors */ + bool refraction = std::abs(1 - dot(refract(bRec.wi), bRec.wo)) < Epsilon; + if (!refraction || !sampleTransmission) + return Spectrum(0.0f); + } + + Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); if (reflection) { - return m_specularReflectance->getValue(bRec.its) * fr; + return m_specularReflectance->getValue(bRec.its) * Fr; } else { Float etaI = m_extIOR, etaT = m_intIOR; bool entering = Frame::cosTheta(bRec.wi) > 0.0f; @@ -229,7 +273,7 @@ public: Float factor = (bRec.quantity == ERadiance) ? (etaI*etaI) / (etaT*etaT) : 1.0f; - return m_specularTransmittance->getValue(bRec.its) * factor * (1 - fr); + return m_specularTransmittance->getValue(bRec.its) * factor * (1 - Fr); } } @@ -238,13 +282,21 @@ public: && (bRec.component == -1 || bRec.component == 0); bool sampleTransmission = (bRec.typeMask & EDeltaTransmission) && (bRec.component == -1 || bRec.component == 1); - bool reflection = Frame::cosTheta(bRec.wo) - * Frame::cosTheta(bRec.wi) > 0; - if ((reflection && !sampleReflection) || - (!reflection && !sampleTransmission) || measure != EDiscrete) + /* 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 (measure != EDiscrete || (reflection && !sampleReflection)) return 0.0f; + if (!reflection) { + /* Check if the provided direction pair matches an ideal + specular refraction; tolerate some roundoff errors */ + bool refraction = std::abs(1 - dot(refract(bRec.wi), bRec.wo)) < Epsilon; + if (!refraction || !sampleTransmission) + return 0.0f; + } + if (sampleTransmission && sampleReflection) { Float Fr = fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR); return reflection ? Fr : (1 - Fr); diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp index 17ca8463..8824d375 100644 --- a/src/bsdfs/roughdielectric.cpp +++ b/src/bsdfs/roughdielectric.cpp @@ -43,10 +43,9 @@ MTS_NAMESPACE_BEGIN * 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 transmission measurements through - * ground glass. Renderings with this distribution may - * converge slowly. - * \item \code{as}: Anisotropic microfacet distribution proposed by + * 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} * } @@ -58,12 +57,14 @@ MTS_NAMESPACE_BEGIN * \default{0.1}. * } * \parameter{alphaU, alphaV}{\Float\Or\Texture}{ - * Specifies the anisotropic rougness values along the tangent and bitangent directions. This - * parameter is only valid when \texttt{distribution=as}. + * Specifies the anisotropic rougness values along the tangent and bitangent directions. These + * parameter are only valid when \texttt{distribution=as}. * \default{0.1}. * } - * \parameter{intIOR}{\Float}{Interior index of refraction \default{1.5046}} - * \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}} + * \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified + * numerically or using a known material name. \default{\texttt{bk7} / 1.5046}} + * \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 reflectance component\default{1.0}} * \lastparameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional @@ -93,39 +94,43 @@ MTS_NAMESPACE_BEGIN * plugin is based on the paper ``Microfacet Models for Refraction through * Rough Surfaces'' by Walter et al. \cite{Walter07Microfacet}. It supports * several types of microfacet distributions and has a texturable roughness - * parameter. Exterior and interior IOR values can each be independently + * parameter. Exterior and interior IOR values can be independently * specified, where ``exterior'' refers to the side that contains the surface - * normal. When no parameters are given, the plugin activates the default - * settings, which describe a borosilicate glass BK7/air interface with a - * light amount of roughness modeled using a Beckmann distribution. + * normal. 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). When no parameters are given, + * the plugin activates the default settings, which describe a borosilicate + * glass BK7/air interface with a light amount of roughness modeled using a + * Beckmann distribution. * * When using the Ashikmin-Shirley or Phong models, a conversion method is * used to turn the specified $\alpha$ roughness value into the exponents - * of these distributions. This is done so that the different distributions - * all produce a similar appearance for the same value of $\alpha$. + * of these distributions. This is done in a way, such that the different + * distributions all produce a similar appearance for the same value of $\alpha$. * * When using this plugin, it is crucial that the scene contains * meaningful and mutally compatible index of refraction changes---see * \figref{glass-explanation} for an example. Also, please note that * the importance sampling implementation of this model is close, but - * not perfect a perfect match to the underlying scattering distribution, - * particularly for high roughness values and when the \texttt{GGX} - * model is used. Hence, such renderings may converge slowly.\vspace{1cm} + * not always a perfect a perfect match to the underlying scattering distribution, + * particularly for high roughness values and when the \texttt{ggx} + * microfacet distribution is used. Hence, such renderings may + * converge slowly.\vspace{1cm} * - * \begin{xml}[caption=Material definition for ground glass, label=lst:roughdielectric-roughglass] + * \begin{xml}[caption=A material definition for ground glass, label=lst:roughdielectric-roughglass] * * * - * - * + * + * * * \end{xml} * * \begin{xml}[caption=A texture can be attached to the roughness parameter, label=lst:roughdielectric-textured] * * - * - * + * + * * * *