2010-09-03 05:41:20 +08:00
|
|
|
/*
|
|
|
|
This file is part of Mitsuba, a physically based rendering system.
|
|
|
|
|
2011-04-14 21:15:59 +08:00
|
|
|
Copyright (c) 2007-2011 by Wenzel Jakob and others.
|
2010-09-03 05:41:20 +08:00
|
|
|
|
|
|
|
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
|
2011-04-14 21:15:59 +08:00
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2010-09-03 05:41:20 +08:00
|
|
|
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 <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
#include <mitsuba/render/bsdf.h>
|
2011-07-01 17:35:49 +08:00
|
|
|
#include <mitsuba/render/sampler.h>
|
2011-06-28 17:09:13 +08:00
|
|
|
#include <mitsuba/render/consttexture.h>
|
2011-07-05 02:13:36 +08:00
|
|
|
#include "microfacet.h"
|
2010-08-10 01:38:37 +08:00
|
|
|
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
|
2011-07-03 08:56:24 +08:00
|
|
|
/*! \plugin{roughdielectric}{Rough dielectric material}
|
2011-06-26 09:29:50 +08:00
|
|
|
* \parameters{
|
2011-07-01 07:48:33 +08:00
|
|
|
* \parameter{distribution}{\String}{
|
|
|
|
* Specifies the type of microfacet normal distribution
|
|
|
|
* used to model the surface roughness.
|
2011-06-26 09:29:50 +08:00
|
|
|
* \begin{enumerate}[(i)]
|
2011-07-01 17:35:49 +08:00
|
|
|
* \item \code{beckmann}: Physically-based distribution derived from
|
2011-07-03 23:10:12 +08:00
|
|
|
* Gaussian random surfaces. This is the default.
|
2011-07-01 17:35:49 +08:00
|
|
|
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
|
|
|
* The Phong exponent $p$ is obtained using a transformation that
|
2011-07-02 00:54:46 +08:00
|
|
|
* produces roughness similar to a Beckmann distribution of the same
|
2011-07-01 17:35:49 +08:00
|
|
|
* parameter. Note that due to the underlying microfacet theory,
|
|
|
|
* the use of this distribution here leads to more realistic
|
|
|
|
* behavior than the separately available \pluginref{phong} plugin.
|
2011-06-26 09:29:50 +08:00
|
|
|
* \item \code{ggx}: New distribution proposed by
|
2011-06-28 17:09:13 +08:00
|
|
|
* Walter et al. meant to better handle the long
|
|
|
|
* tails observed in transmission measurements through
|
2011-07-03 09:31:16 +08:00
|
|
|
* ground glass. Renderings with this distribution may
|
|
|
|
* converge slowly.
|
2011-06-26 09:29:50 +08:00
|
|
|
* \end{enumerate}
|
2011-06-28 17:09:13 +08:00
|
|
|
* Default: \code{beckmann}
|
2011-06-26 09:29:50 +08:00
|
|
|
* }
|
2011-07-01 17:35:49 +08:00
|
|
|
* \parameter{alpha}{\Float\Or\Texture}{Roughness value of the
|
|
|
|
* unresolved surface microgeometry. When the Beckmann
|
|
|
|
* distribution is used, this parameter specifies the
|
|
|
|
* \emph{root mean square} (RMS) slope of the microfacets.
|
|
|
|
* \default{0.1}
|
|
|
|
* }
|
2011-06-28 17:09:13 +08:00
|
|
|
* \parameter{intIOR}{\Float}{Interior index of refraction \default{1.5046}}
|
2011-07-02 00:54:46 +08:00
|
|
|
* \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}}
|
2011-06-28 17:09:13 +08:00
|
|
|
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
2011-07-02 00:54:46 +08:00
|
|
|
* factor used to modulate the reflectance component\default{1.0}}
|
2011-07-03 23:10:12 +08:00
|
|
|
* \lastparameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
|
2011-07-02 00:54:46 +08:00
|
|
|
* factor used to modulate the transmittance component\default{1.0}}
|
2011-06-26 09:29:50 +08:00
|
|
|
* }
|
|
|
|
*
|
2011-07-03 08:46:09 +08:00
|
|
|
*
|
2011-07-01 07:48:33 +08:00
|
|
|
* This plugin implements a realistic microfacet scattering model for rendering
|
2011-07-03 23:10:12 +08:00
|
|
|
* rough interfaces between dielectric materials, such as a transition from air to
|
|
|
|
* ground glass. Microfacet theory describes rough surfaces as an arrangement of
|
|
|
|
* unresolved and ideally specular facets, whose normal directions are given by
|
|
|
|
* a specially chosen \emph{microfacet distribution}. By accounting for shadowing
|
|
|
|
* and masking effects between these facets, it is possible to reproduce the
|
|
|
|
* off-specular reflections peaks observed in real-world measurements of such
|
|
|
|
* materials.
|
|
|
|
* \renderings{
|
2011-07-05 02:13:36 +08:00
|
|
|
* \medrendering{Rough glass (Beckmann, $\alpha$=0.1)}
|
|
|
|
* {bsdf_roughdielectric_beckmann_0_1.jpg}
|
|
|
|
* \medrendering{Ground glass (GGX, $\alpha$=0.304,
|
|
|
|
* \lstref{roughdielectric-roughglass})}{bsdf_roughdielectric_ggx_0_304.jpg}
|
|
|
|
* \medrendering{Textured rougness (\lstref{roughdielectric-textured})}
|
|
|
|
* {bsdf_roughdielectric_textured.jpg}
|
2011-07-03 23:10:12 +08:00
|
|
|
* }
|
2011-07-01 17:35:49 +08:00
|
|
|
*
|
2011-07-02 00:54:46 +08:00
|
|
|
* This plugin is essentially the ``roughened'' equivalent of the plugin
|
2011-07-05 02:13:36 +08:00
|
|
|
* \pluginref{dielectric}. As the roughness parameter $\alpha$ decreases, it
|
|
|
|
* will increasingly approximate the smooth model. The implementation of this
|
|
|
|
* 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
|
|
|
|
* 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 with a light amount of
|
|
|
|
* rougness modeled using a Beckmann distribution.
|
2011-07-02 23:03:57 +08:00
|
|
|
*
|
|
|
|
* When using this plugin, it is crucial that the scene contains
|
2011-07-03 23:10:12 +08:00
|
|
|
* meaningful and mutally compatible index of refraction change---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.
|
|
|
|
*
|
|
|
|
* \begin{xml}[caption=Ground glass, label=lst:roughdielectric-roughglass]
|
|
|
|
* <bsdf type="roughdielectric">
|
|
|
|
* <string name="distribution" value="ggx"/>
|
|
|
|
* <float name="alpha" value="0.304"/>
|
|
|
|
* <float name="intIOR" value="1.5046"/>
|
|
|
|
* <float name="extIOR" value="1.0"/>
|
|
|
|
* </bsdf>
|
|
|
|
* \end{xml}
|
|
|
|
*
|
|
|
|
* \begin{xml}[caption=Textured rougness, label=lst:roughdielectric-textured]
|
|
|
|
* <bsdf type="roughdielectric">
|
|
|
|
* <string name="distribution" value="beckmann"/>
|
|
|
|
* <float name="intIOR" value="1.5046"/>
|
|
|
|
* <float name="extIOR" value="1.0"/>
|
|
|
|
*
|
2011-07-05 02:13:36 +08:00
|
|
|
* <texture name="alpha" type="bitmap">
|
2011-07-03 23:10:12 +08:00
|
|
|
* <string name="filename" value="roughness.exr"/>
|
|
|
|
* </texture>
|
|
|
|
* </bsdf>
|
|
|
|
* \end{xml}
|
2010-08-10 01:38:37 +08:00
|
|
|
*/
|
2011-07-03 08:56:24 +08:00
|
|
|
class RoughDielectric : public BSDF {
|
2010-08-10 01:38:37 +08:00
|
|
|
public:
|
2011-07-03 08:56:24 +08:00
|
|
|
RoughDielectric(const Properties &props)
|
2010-08-10 01:38:37 +08:00
|
|
|
: BSDF(props) {
|
2011-07-01 07:48:33 +08:00
|
|
|
m_specularReflectance = new ConstantSpectrumTexture(
|
2011-06-28 17:09:13 +08:00
|
|
|
props.getSpectrum("specularReflectance", Spectrum(1.0f)));
|
2011-07-01 07:48:33 +08:00
|
|
|
m_specularTransmittance = new ConstantSpectrumTexture(
|
2011-07-03 08:46:09 +08:00
|
|
|
props.getSpectrum("specularTransmittance", Spectrum(1.0f)));
|
2011-06-23 23:37:31 +08:00
|
|
|
|
2011-06-28 17:09:13 +08:00
|
|
|
Float alpha;
|
2011-06-23 23:37:31 +08:00
|
|
|
if (props.hasProperty("alphaB")) {
|
|
|
|
Log(EWarn, "Deprecation warning: the 'alphaB' parameter "
|
|
|
|
"has been renamed to 'alpha'");
|
2011-06-24 05:04:21 +08:00
|
|
|
|
2011-06-28 17:09:13 +08:00
|
|
|
alpha = props.getFloat("alphaB");
|
2011-06-23 23:37:31 +08:00
|
|
|
} else {
|
2011-07-01 07:48:33 +08:00
|
|
|
alpha = props.getFloat("alpha", 0.1f);
|
2011-06-23 23:37:31 +08:00
|
|
|
}
|
|
|
|
|
2011-06-26 09:29:50 +08:00
|
|
|
m_intIOR = props.getFloat("intIOR", 1.5046f);
|
2010-08-10 01:38:37 +08:00
|
|
|
m_extIOR = props.getFloat("extIOR", 1.0f);
|
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
if (m_intIOR < 0 || m_extIOR < 0 || m_intIOR == m_extIOR)
|
|
|
|
Log(EError, "The interior and exterior indices of "
|
|
|
|
"refraction must be positive and differ!");
|
2011-06-23 23:37:31 +08:00
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
m_distribution = MicrofacetDistribution(
|
|
|
|
m_props.getString("distribution", "beckmann")
|
|
|
|
);
|
2011-06-23 23:37:31 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
m_alpha = new ConstantFloatTexture(alpha);
|
2011-06-28 17:09:13 +08:00
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
m_components.push_back(
|
|
|
|
EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler);
|
|
|
|
m_components.push_back(
|
|
|
|
EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler);
|
2010-08-10 01:38:37 +08:00
|
|
|
m_usesRayDifferentials = false;
|
|
|
|
}
|
|
|
|
|
2011-07-03 08:56:24 +08:00
|
|
|
RoughDielectric(Stream *stream, InstanceManager *manager)
|
2010-08-10 01:38:37 +08:00
|
|
|
: BSDF(stream, manager) {
|
2011-07-05 02:13:36 +08:00
|
|
|
m_distribution = MicrofacetDistribution(
|
|
|
|
(MicrofacetDistribution::EType) stream->readUInt()
|
|
|
|
);
|
2011-06-28 17:09:13 +08:00
|
|
|
m_alpha = static_cast<Texture *>(manager->getInstance(stream));
|
|
|
|
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
|
|
|
m_specularTransmittance = static_cast<Texture *>(manager->getInstance(stream));
|
2010-08-10 01:38:37 +08:00
|
|
|
m_intIOR = stream->readFloat();
|
|
|
|
m_extIOR = stream->readFloat();
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
m_components.push_back(
|
|
|
|
EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler);
|
|
|
|
m_components.push_back(
|
|
|
|
EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler);
|
2011-06-28 17:09:13 +08:00
|
|
|
m_usesRayDifferentials =
|
|
|
|
m_alpha->usesRayDifferentials() ||
|
|
|
|
m_specularReflectance->usesRayDifferentials() ||
|
|
|
|
m_specularTransmittance->usesRayDifferentials();
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
virtual ~RoughDielectric() { }
|
2011-06-25 06:24:41 +08:00
|
|
|
|
|
|
|
inline Float signum(Float value) const {
|
|
|
|
return (value < 0) ? -1.0f : 1.0f;
|
|
|
|
}
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
Spectrum eval(const BSDFQueryRecord &bRec) const {
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Determine the type of interaction */
|
|
|
|
bool reflect = Frame::cosTheta(bRec.wi)
|
|
|
|
* Frame::cosTheta(bRec.wo) > 0;
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Determine the appropriate indices of refraction */
|
2011-06-24 07:29:52 +08:00
|
|
|
Float etaI = m_extIOR, etaT = m_intIOR;
|
2010-08-10 01:38:37 +08:00
|
|
|
if (Frame::cosTheta(bRec.wi) < 0)
|
2011-06-24 07:29:52 +08:00
|
|
|
std::swap(etaI, etaT);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
Vector H;
|
|
|
|
if (reflect) {
|
|
|
|
/* Stop if this component was not requested */
|
|
|
|
if ((bRec.component != -1 && bRec.component != 0)
|
|
|
|
|| !(bRec.typeMask & EGlossyReflection))
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
|
|
|
/* Calculate the reflection half-vector (and possibly flip it
|
|
|
|
so that it lies inside the hemisphere around the normal) */
|
|
|
|
H = normalize(bRec.wo+bRec.wi)
|
|
|
|
* signum(Frame::cosTheta(bRec.wo));
|
|
|
|
} else {
|
|
|
|
/* Stop if this component was not requested */
|
|
|
|
if ((bRec.component != -1 && bRec.component != 1)
|
|
|
|
|| !(bRec.typeMask & EGlossyTransmission))
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
|
|
|
/* Calculate the transmission half-vector (and possibly flip it
|
|
|
|
when the surface normal points into the denser medium -- this
|
|
|
|
removes an assumption in the original paper) */
|
|
|
|
H = (m_extIOR > m_intIOR ? (Float) 1 : (Float) -1)
|
|
|
|
* normalize(bRec.wi*etaI + bRec.wo*etaT);
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-06-28 17:09:13 +08:00
|
|
|
/* Evaluate the roughness */
|
2011-07-05 02:13:36 +08:00
|
|
|
Float alpha = m_distribution.transform(
|
|
|
|
m_alpha->getValue(bRec.its).average());
|
2011-06-28 17:09:13 +08:00
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
/* Microsurface normal distribution */
|
2011-07-05 02:13:36 +08:00
|
|
|
const Float D = m_distribution.eval(H, alpha);
|
2011-06-23 23:37:31 +08:00
|
|
|
if (D == 0)
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Fresnel factor */
|
|
|
|
const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Smith's shadow-masking function */
|
|
|
|
const Float G = smithG1(bRec.wi, H, alpha) * smithG1(bRec.wo, H, alpha);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
if (reflect) {
|
|
|
|
/* Calculate the total amount of reflection */
|
|
|
|
Float value = F * D * G /
|
2011-07-05 02:13:36 +08:00
|
|
|
(4.0f * Frame::cosTheta(bRec.wi));
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
return m_specularReflectance->getValue(bRec.its) * value;
|
|
|
|
} else {
|
|
|
|
/* Calculate the total amount of transmission */
|
|
|
|
Float sqrtDenom = etaI * dot(bRec.wi, H) + etaT * dot(bRec.wo, H);
|
|
|
|
Float value = ((1 - F) * D * G * etaT * etaT * dot(bRec.wi, H)*dot(bRec.wo, H)) /
|
2011-07-05 02:13:36 +08:00
|
|
|
(Frame::cosTheta(bRec.wi) * sqrtDenom * sqrtDenom);
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
/* Missing term in the original paper: account for the solid angle
|
|
|
|
compression when tracing radiance -- this is necessary for
|
|
|
|
bidirectional method */
|
|
|
|
if (bRec.quantity == ERadiance)
|
|
|
|
value *= (etaI*etaI) / (etaT*etaT);
|
|
|
|
|
|
|
|
return m_specularTransmittance->getValue(bRec.its) * std::abs(value);
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
Float pdf(const BSDFQueryRecord &bRec) const {
|
|
|
|
/* Determine the type of interaction */
|
|
|
|
bool sampleReflection = ((bRec.component == -1 || bRec.component == 0)
|
|
|
|
&& (bRec.typeMask & EGlossyReflection)),
|
|
|
|
sampleTransmission = ((bRec.component == -1 || bRec.component == 1)
|
|
|
|
&& (bRec.typeMask & EGlossyTransmission)),
|
|
|
|
reflect = Frame::cosTheta(bRec.wi)
|
|
|
|
* Frame::cosTheta(bRec.wo) > 0;
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Determine the appropriate indices of refraction */
|
2011-06-24 01:41:29 +08:00
|
|
|
Float etaI = m_extIOR, etaT = m_intIOR;
|
2010-08-10 01:38:37 +08:00
|
|
|
if (Frame::cosTheta(bRec.wi) < 0)
|
2011-06-24 01:41:29 +08:00
|
|
|
std::swap(etaI, etaT);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
Vector H;
|
|
|
|
Float dwh_dwo;
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
if (reflect) {
|
|
|
|
/* Zero probability if this component was not requested */
|
|
|
|
if ((bRec.component != -1 && bRec.component != 0)
|
|
|
|
|| !(bRec.typeMask & EGlossyReflection))
|
|
|
|
return 0.0f;
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Calculate the reflection half-vector (and possibly flip it
|
|
|
|
so that it lies inside the hemisphere around the normal) */
|
|
|
|
H = normalize(bRec.wo+bRec.wi)
|
|
|
|
* signum(Frame::cosTheta(bRec.wo));
|
|
|
|
|
|
|
|
/* Jacobian of the half-direction transform */
|
|
|
|
dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H));
|
|
|
|
} else {
|
|
|
|
/* Zero probability if this component was not requested */
|
|
|
|
if ((bRec.component != -1 && bRec.component != 1)
|
|
|
|
|| !(bRec.typeMask & EGlossyTransmission))
|
|
|
|
return 0.0f;
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Calculate the transmission half-vector (and possibly flip it
|
|
|
|
when the surface normal points into the denser medium -- this
|
|
|
|
removes an assumption in the original paper) */
|
|
|
|
H = (m_extIOR > m_intIOR ? (Float) 1 : (Float) -1)
|
|
|
|
* normalize(bRec.wi*etaI + bRec.wo*etaT);
|
|
|
|
|
|
|
|
/* Jacobian of the half-direction transform. */
|
|
|
|
Float sqrtDenom = etaI * dot(bRec.wi, H) + etaT * dot(bRec.wo, H);
|
|
|
|
dwh_dwo = (etaT*etaT * dot(bRec.wo, H)) / (sqrtDenom*sqrtDenom);
|
|
|
|
}
|
2011-06-28 17:09:13 +08:00
|
|
|
|
|
|
|
/* Evaluate the roughness */
|
2011-07-05 02:13:36 +08:00
|
|
|
Float alpha = m_distribution.transform(
|
|
|
|
m_alpha->getValue(bRec.its).average());
|
2011-06-28 17:09:13 +08:00
|
|
|
|
2010-12-03 03:44:35 +08:00
|
|
|
/* Suggestion by Bruce Walter: sample using a slightly different
|
2011-06-23 23:37:31 +08:00
|
|
|
value of alpha. This in practice limits the weights to
|
2010-12-02 08:10:43 +08:00
|
|
|
values <= 4. See also \ref sample() */
|
2011-06-28 17:09:13 +08:00
|
|
|
alpha = alpha * (1.2f - 0.2f * std::sqrt(
|
2011-07-01 07:48:33 +08:00
|
|
|
std::abs(Frame::cosTheta(bRec.wi))));
|
|
|
|
|
|
|
|
/* Microsurface normal distribution */
|
2011-07-05 02:13:36 +08:00
|
|
|
Float prob = m_distribution.eval(H, alpha);
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
if (sampleTransmission && sampleReflection) {
|
2011-07-01 17:35:49 +08:00
|
|
|
/* Please see the sample() methods if the
|
|
|
|
following seems confusing */
|
|
|
|
Float F;
|
|
|
|
if (bRec.sampler) {
|
|
|
|
/* We have access to extra random numbers, hence
|
|
|
|
the exact Fresnel term with respect to the
|
|
|
|
microfacet normal is sampled */
|
|
|
|
F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR);
|
|
|
|
} else {
|
|
|
|
/* Only a simple 2D sample is given, and hence the
|
|
|
|
best we can do is to sample a clamped Fresnel term
|
|
|
|
that is taken with respect to the surface normal */
|
|
|
|
F = std::min((Float) 0.9f, std::max((Float) 0.1f,
|
|
|
|
fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR)));
|
|
|
|
}
|
|
|
|
prob *= reflect ? F : (1-F);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
return std::abs(prob * Frame::cosTheta(H) * dwh_dwo);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2010-12-02 08:10:43 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
Spectrum sample(BSDFQueryRecord &bRec, const Point2 &_sample) const {
|
|
|
|
Point2 sample(_sample);
|
2010-12-02 08:10:43 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
bool sampleReflection = ((bRec.component == -1 || bRec.component == 0)
|
|
|
|
&& (bRec.typeMask & EGlossyReflection)),
|
|
|
|
sampleTransmission = ((bRec.component == -1 || bRec.component == 1)
|
|
|
|
&& (bRec.typeMask & EGlossyTransmission)),
|
2011-07-01 17:35:49 +08:00
|
|
|
choseReflection = sampleReflection,
|
|
|
|
sampleExactFresnelTerm = false;
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
Float sampleF = 1.0f;
|
|
|
|
if (sampleReflection && sampleTransmission) {
|
2011-07-01 17:35:49 +08:00
|
|
|
if (!bRec.sampler) {
|
|
|
|
/* By default, the sample() method is given exactly two
|
|
|
|
uniformly chosen random numbers, which is a problem
|
|
|
|
when wanting to choose between the reflection and the
|
|
|
|
transmission component: by the time the microfacet normal
|
|
|
|
has been sampled, we have already "used up" both of them,
|
|
|
|
and no random bits are left. Therefore, the following
|
|
|
|
somewhat crude approach is taken here when no extra sampler
|
|
|
|
instance is provided:
|
|
|
|
1. Take the Fresnel term with respect to the surface
|
|
|
|
normal to be a good approximation to the microsurface
|
|
|
|
Fresnel term -- this will be less true for higher
|
|
|
|
rougness values. To be safe, clamp it to some
|
|
|
|
reasonable range.
|
|
|
|
2. Use this approximate term and a random number to
|
|
|
|
choose between reflection and refraction component.
|
|
|
|
3. Recycle the used random number! This is possible,
|
|
|
|
since we so far only used the knowledge that it is
|
|
|
|
smaller or larget than the purely deterministic value
|
|
|
|
from step 2.
|
|
|
|
*/
|
|
|
|
sampleF = std::min((Float) 0.9f, std::max((Float) 0.1f,
|
|
|
|
fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR)));
|
|
|
|
if (sample.x < sampleF) {
|
|
|
|
sample.x /= sampleF;
|
|
|
|
} else {
|
|
|
|
sample.x = (sample.x - sampleF) / (1 - sampleF);
|
|
|
|
choseReflection = false;
|
|
|
|
}
|
2011-07-01 07:48:33 +08:00
|
|
|
} else {
|
2011-07-01 17:35:49 +08:00
|
|
|
sampleExactFresnelTerm = true;
|
2011-07-01 07:48:33 +08:00
|
|
|
}
|
|
|
|
} else if (!sampleReflection && !sampleTransmission) {
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
}
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Evaluate the roughness */
|
2011-07-05 02:13:36 +08:00
|
|
|
Float alpha = m_distribution.transform(
|
|
|
|
m_alpha->getValue(bRec.its).average());
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Suggestion by Bruce Walter: sample using a slightly different
|
|
|
|
value of alpha. This in practice limits the weights to
|
|
|
|
values <= 4. See also \ref sample() */
|
|
|
|
Float sampleAlpha = alpha * (1.2f - 0.2f * std::sqrt(
|
|
|
|
std::abs(Frame::cosTheta(bRec.wi))));
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Sample M, the microsurface normal */
|
|
|
|
const Normal m = sampleD(sample, sampleAlpha);
|
2011-07-01 17:35:49 +08:00
|
|
|
|
|
|
|
if (sampleExactFresnelTerm) {
|
|
|
|
sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR);
|
|
|
|
if (bRec.sampler->next1D() > sampleF)
|
|
|
|
choseReflection = false;
|
|
|
|
}
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
Spectrum result;
|
|
|
|
if (choseReflection) {
|
2011-07-01 17:35:49 +08:00
|
|
|
/* Perfect specular reflection based on the microsurface normal */
|
2011-07-01 07:48:33 +08:00
|
|
|
bRec.wo = reflect(bRec.wi, m);
|
|
|
|
bRec.sampledComponent = 0;
|
|
|
|
bRec.sampledType = EGlossyReflection;
|
|
|
|
|
|
|
|
/* Side check */
|
|
|
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
|
|
|
result = m_specularReflectance->getValue(bRec.its);
|
|
|
|
} else {
|
|
|
|
/* Determine the appropriate indices of refraction */
|
|
|
|
Float etaI = m_extIOR, etaT = m_intIOR;
|
|
|
|
if (Frame::cosTheta(bRec.wi) < 0)
|
|
|
|
std::swap(etaI, etaT);
|
2011-06-23 23:37:31 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Perfect specular transmission based on the microsurface normal */
|
|
|
|
if (!refract(bRec.wi, bRec.wo, m, etaI, etaT))
|
|
|
|
return Spectrum(0.0f);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
bRec.sampledComponent = 1;
|
|
|
|
bRec.sampledType = EGlossyTransmission;
|
|
|
|
|
|
|
|
/* Side check */
|
|
|
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) >= 0)
|
|
|
|
return Spectrum(0.0f);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
result = m_specularTransmittance->getValue(bRec.its)
|
|
|
|
* ((bRec.quantity == ERadiance) ? ((etaI*etaI) / (etaT*etaT)) : (Float) 1);
|
|
|
|
}
|
2010-12-02 08:10:43 +08:00
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
Float numerator = m_distribution.eval(m, alpha)
|
|
|
|
* m_distribution.smithG1(bRec.wi, m, alpha)
|
|
|
|
* m_distribution.smithG1(bRec.wo, m, alpha)
|
2011-07-01 17:35:49 +08:00
|
|
|
* dot(bRec.wi, m);
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
Float denominator = m_distribution.eval(m, sampleAlpha)
|
2011-07-01 17:35:49 +08:00
|
|
|
* Frame::cosTheta(m)
|
2011-07-05 02:13:36 +08:00
|
|
|
* Frame::cosTheta(bRec.wi);
|
2011-07-01 17:35:49 +08:00
|
|
|
|
|
|
|
if (!sampleExactFresnelTerm) {
|
|
|
|
Float F = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR);
|
|
|
|
if (!choseReflection) {
|
|
|
|
sampleF = 1-sampleF;
|
|
|
|
F = 1-F;
|
|
|
|
}
|
|
|
|
numerator *= F;
|
|
|
|
if (sampleReflection && sampleTransmission)
|
|
|
|
denominator *= sampleF;
|
2011-07-01 07:48:33 +08:00
|
|
|
}
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
return result * std::abs(numerator / denominator);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2011-07-01 17:35:49 +08:00
|
|
|
Spectrum sample(BSDFQueryRecord &bRec, Float _pdf, const Point2 &_sample) const {
|
2011-02-03 16:15:25 +08:00
|
|
|
Point2 sample(_sample);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
bool sampleReflection = ((bRec.component == -1 || bRec.component == 0)
|
|
|
|
&& (bRec.typeMask & EGlossyReflection)),
|
|
|
|
sampleTransmission = ((bRec.component == -1 || bRec.component == 1)
|
|
|
|
&& (bRec.typeMask & EGlossyTransmission)),
|
2011-07-01 17:35:49 +08:00
|
|
|
choseReflection = sampleReflection,
|
|
|
|
sampleExactFresnelTerm = false;
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
if (sampleReflection && sampleTransmission) {
|
2011-07-01 17:35:49 +08:00
|
|
|
if (!bRec.sampler) {
|
|
|
|
/* By default, the sample() method is given exactly two
|
|
|
|
uniformly chosen random numbers, which is a problem
|
|
|
|
when wanting to choose between the reflection and the
|
|
|
|
transmission component: by the time the microfacet normal
|
|
|
|
has been sampled, we have already "used up" both of them,
|
|
|
|
and no random bits are left. Therefore, the following
|
|
|
|
somewhat crude approach is taken here when no extra sampler
|
|
|
|
instance is provided:
|
|
|
|
1. Take the Fresnel term with respect to the surface
|
|
|
|
normal to be a good approximation to the microsurface
|
|
|
|
Fresnel term -- this will be less true for higher
|
|
|
|
rougness values. To be safe, clamp it to some
|
|
|
|
reasonable range.
|
|
|
|
2. Use this approximate term and a random number to
|
|
|
|
choose between reflection and refraction component.
|
|
|
|
3. Recycle the used random number! This is possible,
|
|
|
|
since we so far only used the knowledge that it is
|
|
|
|
smaller or larget than the purely deterministic value
|
|
|
|
from step 2.
|
|
|
|
*/
|
|
|
|
Float sampleF = std::min((Float) 0.9f, std::max((Float) 0.1f,
|
|
|
|
fresnel(Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR)));
|
|
|
|
if (sample.x < sampleF) {
|
|
|
|
sample.x /= sampleF;
|
|
|
|
} else {
|
|
|
|
sample.x = (sample.x - sampleF) / (1 - sampleF);
|
|
|
|
choseReflection = false;
|
|
|
|
}
|
2011-07-01 07:48:33 +08:00
|
|
|
} else {
|
2011-07-01 17:35:49 +08:00
|
|
|
sampleExactFresnelTerm = true;
|
2011-07-01 07:48:33 +08:00
|
|
|
}
|
|
|
|
} else if (!sampleReflection && !sampleTransmission) {
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
}
|
|
|
|
|
2011-06-28 17:09:13 +08:00
|
|
|
/* Evaluate the roughness */
|
2011-07-05 02:13:36 +08:00
|
|
|
Float alpha = m_distribution.transform(
|
|
|
|
m_alpha->getValue(bRec.its).average());
|
2011-06-28 17:09:13 +08:00
|
|
|
|
2010-12-03 03:44:35 +08:00
|
|
|
/* Suggestion by Bruce Walter: sample using a slightly different
|
2011-06-23 23:37:31 +08:00
|
|
|
value of alpha. This in practice limits the weights to
|
2011-07-01 07:48:33 +08:00
|
|
|
values <= 4. See also \ref sample() */
|
|
|
|
Float sampleAlpha = alpha * (1.2f - 0.2f * std::sqrt(
|
|
|
|
std::abs(Frame::cosTheta(bRec.wi))));
|
|
|
|
|
|
|
|
/* Sample M, the microsurface normal */
|
|
|
|
const Normal m = sampleD(sample, sampleAlpha);
|
2011-07-01 17:35:49 +08:00
|
|
|
|
|
|
|
if (sampleExactFresnelTerm) {
|
|
|
|
Float sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR);
|
|
|
|
if (bRec.sampler->next1D() > sampleF)
|
|
|
|
choseReflection = false;
|
|
|
|
}
|
2011-07-01 07:48:33 +08:00
|
|
|
|
|
|
|
if (choseReflection) {
|
2011-07-01 17:35:49 +08:00
|
|
|
/* Perfect specular reflection based on the microsurface normal */
|
2011-07-01 07:48:33 +08:00
|
|
|
bRec.wo = reflect(bRec.wi, m);
|
|
|
|
bRec.sampledComponent = 0;
|
|
|
|
bRec.sampledType = EGlossyReflection;
|
|
|
|
|
|
|
|
/* Side check */
|
|
|
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
} else {
|
2011-07-01 17:35:49 +08:00
|
|
|
/* Determine the appropriate indices of refraction */
|
|
|
|
Float etaI = m_extIOR, etaT = m_intIOR;
|
|
|
|
if (Frame::cosTheta(bRec.wi) < 0)
|
|
|
|
std::swap(etaI, etaT);
|
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Perfect specular transmission based on the microsurface normal */
|
|
|
|
if (!refract(bRec.wi, bRec.wo, m, etaI, etaT))
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
|
|
|
bRec.sampledComponent = 1;
|
|
|
|
bRec.sampledType = EGlossyTransmission;
|
|
|
|
|
|
|
|
/* Side check */
|
|
|
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) >= 0)
|
|
|
|
return Spectrum(0.0f);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
2011-07-01 17:35:49 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
/* Guard against numerical imprecisions */
|
|
|
|
_pdf = pdf(bRec);
|
2010-08-10 01:38:37 +08:00
|
|
|
|
2011-07-01 07:48:33 +08:00
|
|
|
if (_pdf == 0)
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
else
|
|
|
|
return f(bRec);
|
2010-08-10 01:38:37 +08:00
|
|
|
}
|
|
|
|
|
2011-06-28 17:09:13 +08:00
|
|
|
void addChild(const std::string &name, ConfigurableObject *child) {
|
|
|
|
if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alpha") {
|
|
|
|
m_alpha = static_cast<Texture *>(child);
|
|
|
|
m_usesRayDifferentials |= m_alpha->usesRayDifferentials();
|
|
|
|
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularReflectance") {
|
|
|
|
m_specularReflectance = static_cast<Texture *>(child);
|
|
|
|
m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials();
|
|
|
|
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularTransmittance") {
|
|
|
|
m_specularTransmittance = static_cast<Texture *>(child);
|
|
|
|
m_usesRayDifferentials |= m_specularTransmittance->usesRayDifferentials();
|
|
|
|
} else {
|
|
|
|
BSDF::addChild(name, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
|
|
|
BSDF::serialize(stream, manager);
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
stream->writeUInt((uint32_t) m_distribution.getType());
|
2011-06-28 17:09:13 +08:00
|
|
|
manager->serialize(stream, m_alpha.get());
|
|
|
|
manager->serialize(stream, m_specularReflectance.get());
|
|
|
|
manager->serialize(stream, m_specularTransmittance.get());
|
2010-08-10 01:38:37 +08:00
|
|
|
stream->writeFloat(m_intIOR);
|
|
|
|
stream->writeFloat(m_extIOR);
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string toString() const {
|
|
|
|
std::ostringstream oss;
|
2011-07-03 08:56:24 +08:00
|
|
|
oss << "RoughDielectric[" << endl
|
2011-07-05 02:13:36 +08:00
|
|
|
<< " distribution = " << m_distribution.toString() << "," << endl;
|
|
|
|
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
|
2011-06-28 17:09:13 +08:00
|
|
|
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
|
|
|
<< " specularTransmittance = " << indent(m_specularTransmittance->toString()) << "," << endl
|
2011-06-24 05:04:21 +08:00
|
|
|
<< " intIOR = " << m_intIOR << "," << endl
|
2011-06-28 17:09:13 +08:00
|
|
|
<< " extIOR = " << m_extIOR << endl
|
2011-06-24 05:04:21 +08:00
|
|
|
<< "]";
|
2010-08-10 01:38:37 +08:00
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
2011-07-03 08:46:09 +08:00
|
|
|
Shader *createShader(Renderer *renderer) const;
|
|
|
|
|
2010-08-10 01:38:37 +08:00
|
|
|
MTS_DECLARE_CLASS()
|
|
|
|
private:
|
2011-07-05 02:13:36 +08:00
|
|
|
MicrofacetDistribution m_distribution;
|
2011-06-28 17:09:13 +08:00
|
|
|
ref<Texture> m_specularTransmittance;
|
|
|
|
ref<Texture> m_specularReflectance;
|
|
|
|
ref<Texture> m_alpha;
|
|
|
|
Float m_intIOR, m_extIOR;
|
2010-08-10 01:38:37 +08:00
|
|
|
};
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
/* Fake dielectric shader -- it is really hopeless to visualize
|
2011-07-03 08:46:09 +08:00
|
|
|
this material in the VPL renderer, so let's try to do at least
|
2011-07-05 02:13:36 +08:00
|
|
|
something that suggests the presence of a translucent boundary */
|
2011-07-03 08:56:24 +08:00
|
|
|
class RoughDielectricShader : public Shader {
|
2011-07-03 08:46:09 +08:00
|
|
|
public:
|
2011-07-03 08:56:24 +08:00
|
|
|
RoughDielectricShader(Renderer *renderer) :
|
2011-07-03 08:46:09 +08:00
|
|
|
Shader(renderer, EBSDFShader) {
|
|
|
|
m_flags = ETransparent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void generateCode(std::ostringstream &oss,
|
|
|
|
const std::string &evalName,
|
|
|
|
const std::vector<std::string> &depNames) const {
|
|
|
|
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
2011-07-05 02:13:36 +08:00
|
|
|
<< " return vec3(0.08) * cosTheta(wo);" << endl
|
|
|
|
<< "}" << endl
|
|
|
|
<< endl
|
|
|
|
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
|
|
|
<< " return " << evalName << "(uv, wi, wo);" << endl
|
2011-07-03 08:46:09 +08:00
|
|
|
<< "}" << endl;
|
|
|
|
}
|
|
|
|
MTS_DECLARE_CLASS()
|
|
|
|
};
|
|
|
|
|
2011-07-03 08:56:24 +08:00
|
|
|
Shader *RoughDielectric::createShader(Renderer *renderer) const {
|
|
|
|
return new RoughDielectricShader(renderer);
|
2011-07-03 08:46:09 +08:00
|
|
|
}
|
|
|
|
|
2011-07-03 08:56:24 +08:00
|
|
|
MTS_IMPLEMENT_CLASS(RoughDielectricShader, false, Shader)
|
|
|
|
MTS_IMPLEMENT_CLASS_S(RoughDielectric, false, BSDF)
|
|
|
|
MTS_EXPORT_PLUGIN(RoughDielectric, "Rough glass BSDF");
|
2010-08-10 01:38:37 +08:00
|
|
|
MTS_NAMESPACE_END
|