added the ashikhmin-shirley microfacet distribution

metadata
Wenzel Jakob 2011-07-07 05:39:55 +02:00
parent 84146f8168
commit ac63fa896b
4 changed files with 229 additions and 97 deletions

View File

@ -8,14 +8,36 @@
<string name="extIOR" value="air"/> <string name="extIOR" value="air"/>
</bsdf> </bsdf>
<!-- Test the rough glass model with the
Ashikhmin-Shirley microfacet distribution -->
<bsdf type="roughdielectric">
<string name="distribution" value="as"/>
<float name="alphaU" value=".3"/>
<float name="alphaV" value=".1"/>
<float name="intIOR" value="1.5"/>
<float name="extIOR" value="1.0"/>
</bsdf>
<!-- Test the rough glass model with the
Beckmann microfacet distribution -->
<bsdf type="roughdielectric">
<string name="distribution" value="beckmann"/>
<float name="alpha" value=".3"/>
<float name="intIOR" value="1.5"/>
<float name="extIOR" value="1.0"/>
</bsdf>
<!-- Test the diffuse model --> <!-- Test the diffuse model -->
<bsdf type="diffuse"/> <bsdf type="diffuse"/>
<!-- Test the diffuse transmission model --> <!-- Test the diffuse transmission model -->
<bsdf type="difftrans"/> <bsdf type="difftrans"/>
<!-- Test a simple mixture between diffuse
transmittance and reflectance -->
<bsdf type="mixture"> <bsdf type="mixture">
<string name="weights" value=".5 .5"/> <string name="weights" value=".5 .3"/>
<bsdf type="diffuse"> <bsdf type="diffuse">
<rgb name="reflectance" value=".5 0 0"/> <rgb name="reflectance" value=".5 0 0"/>

View File

@ -28,3 +28,12 @@
year = {1998}, year = {1998},
publisher = {Academic press} publisher = {Academic press}
} }
@article{Ashikhmin2005Anisotropic,
title = {{An anisotropic phong BRDF model}},
author = {Ashikhmin, M. and Shirley, P.},
journal = {Graphics tools: The jgt editors' choice},
pages = {303},
year = {2005},
publisher = {AK Peters, Ltd.}
}

View File

@ -34,11 +34,13 @@ public:
/// Supported distribution types /// Supported distribution types
enum EType { enum EType {
/// Beckmann distribution derived from Gaussian random surfaces /// Beckmann distribution derived from Gaussian random surfaces
EBeckmann = 0, EBeckmann = 0,
/// Classical Phong distribution /// Classical Phong distribution
EPhong = 1, EPhong = 1,
/// Long-tailed distribution proposed by Walter et al. /// Long-tailed distribution proposed by Walter et al.
EGGX = 2 EGGX = 2,
/// Anisotropic distribution by Ashikhmin and Shirley
EAshikhminShirley = 3
}; };
/// Create a microfacet distribution of the specified type /// Create a microfacet distribution of the specified type
@ -47,7 +49,7 @@ public:
/** /**
* \brief Create a microfacet distribution of the specified name * \brief Create a microfacet distribution of the specified name
* (ggx/phong/beckmann) * (ggx/phong/beckmann/as)
*/ */
MicrofacetDistribution(const std::string &name) { MicrofacetDistribution(const std::string &name) {
std::string distr = boost::to_lower_copy(name); std::string distr = boost::to_lower_copy(name);
@ -58,9 +60,11 @@ public:
m_type = EPhong; m_type = EPhong;
else if (distr == "ggx") else if (distr == "ggx")
m_type = EGGX; m_type = EGGX;
else if (distr == "as")
m_type = EAshikhminShirley;
else else
SLog(EError, "Specified an invalid distribution \"%s\", must be " SLog(EError, "Specified an invalid distribution \"%s\", must be "
"\"beckmann\", \"phong\", or \"ggx\"!", distr.c_str()); "\"beckmann\", \"phong\", \"ggx\", or \"as\"!", distr.c_str());
} }
/// Return the distribution type /// Return the distribution type
@ -74,7 +78,7 @@ public:
* (For lower roughness values, please switch to the smooth BSDF variants) * (For lower roughness values, please switch to the smooth BSDF variants)
*/ */
Float transformRoughness(Float value) const { Float transformRoughness(Float value) const {
if (m_type == EPhong) if (m_type == EPhong || m_type == EAshikhminShirley)
value = 2 / (value * value) - 2; value = 2 / (value * value) - 2;
return std::max(value, (Float) 1e-4f); return std::max(value, (Float) 1e-4f);
} }
@ -83,11 +87,11 @@ public:
* \brief Implements the microfacet distribution function D * \brief Implements the microfacet distribution function D
* *
* \param m The microsurface normal * \param m The microsurface normal
* \param alphaX Surface roughness in the tangent directoin * \param alphaU Surface roughness in the tangent directoin
* \param alphaY Surface roughness in the bitangent direction * \param alphaV Surface roughness in the bitangent direction
*/ */
Float eval(const Vector &m, Float alphaX, Float alphaY) const { Float eval(const Vector &m, Float alphaU, Float alphaV) const {
Float alpha = 0.5f * (alphaX + alphaY); Float alpha = 0.5f * (alphaU + alphaV);
if (Frame::cosTheta(m) <= 0) if (Frame::cosTheta(m) <= 0)
return 0.0f; return 0.0f;
@ -120,6 +124,15 @@ public:
result = INV_PI * (root * root); result = INV_PI * (root * root);
} }
break; break;
case EAshikhminShirley: {
const Float cosTheta = Frame::cosTheta(m);
const Float exponent = (alphaU * m.x * m.x + alphaV * m.y * m.y)
/ std::max((Float) 0, 1 - cosTheta * cosTheta);
result = std::sqrt((alphaU + 1) * (alphaV + 1))
* INV_TWOPI * std::pow(cosTheta, exponent);
}
break;
default: default:
SLog(EError, "Invalid distribution function!"); SLog(EError, "Invalid distribution function!");
@ -132,17 +145,30 @@ public:
return result; return result;
} }
/// Helper routine: sample the first quadrant of the A&S distribution
void sampleFirstQuadrant(Float alphaU, Float alphaV, Float u1, Float u2,
Float &phi, Float &cosTheta) const {
if (alphaU == alphaV)
phi = M_PI * u1 * 0.5f;
else
phi = std::atan(sqrtf((alphaU + 1.0f) / (alphaV + 1.0f)) *
std::tan(M_PI * u1 * 0.5f));
const Float cosPhi = std::cos(phi), sinPhi = std::sin(phi);
cosTheta = std::pow(u2, 1.0f /
(alphaU * cosPhi * cosPhi + alphaV * sinPhi * sinPhi + 1.0f));
}
/** /**
* \brief Sample microsurface normals according to * \brief Sample microsurface normals according to
* the selected distribution * the selected distribution
* *
* \param sample A uniformly distributed 2D sample * \param sample A uniformly distributed 2D sample
* \param alphaX Surface roughness in the tangent directoin * \param alphaU Surface roughness in the tangent directoin
* \param alphaY Surface roughness in the bitangent direction * \param alphaV Surface roughness in the bitangent direction
*/ */
Normal sample(const Point2 &sample, Float alphaX, Float alphaY) const { Normal sample(const Point2 &sample, Float alphaU, Float alphaV) const {
Float alpha = 0.5f * (alphaX + alphaY); Float alpha = 0.5f * (alphaU + alphaV);
/* The azimuthal component is always selected /* The azimuthal component is always selected
uniformly regardless of the distribution */ uniformly regardless of the distribution */
@ -164,6 +190,35 @@ public:
thetaM = std::atan(alpha * std::sqrt(sample.x) / thetaM = std::atan(alpha * std::sqrt(sample.x) /
std::sqrt(1.0f - sample.x)); std::sqrt(1.0f - sample.x));
break; break;
case EAshikhminShirley: {
/* Sampling method based on code from PBRT */
Float phi, cosTheta;
if (sample.x < .25f) {
sampleFirstQuadrant(alphaU, alphaV,
4 * sample.x, sample.y, phi, cosTheta);
} else if (sample.x < 0.5f) {
sampleFirstQuadrant(alphaU, alphaV,
4 * (0.5f - sample.x), sample.y, phi, cosTheta);
phi = M_PI - phi;
} else if (sample.x < 0.75f) {
sampleFirstQuadrant(alphaU, alphaV,
4 * (sample.x - 0.5f), sample.y, phi, cosTheta);
phi += M_PI;
} else {
sampleFirstQuadrant(alphaU, alphaV,
4 * (1 - sample.x), sample.y, phi, cosTheta);
phi = 2 * M_PI - phi;
}
const Float sinTheta = std::sqrt(
std::max((Float) 0, 1 - cosTheta*cosTheta));
return Vector(
sinTheta * std::cos(phi),
sinTheta * std::sin(phi),
cosTheta
);
}
break;
default: default:
SLog(EError, "Invalid distribution function!"); SLog(EError, "Invalid distribution function!");
@ -233,16 +288,30 @@ public:
* \param m The microsurface normal * \param m The microsurface normal
* \param alpha The surface roughness * \param alpha The surface roughness
*/ */
Float G(const Vector &wi, const Vector &wo, const Vector &m, Float alphaX, Float alphaY) const { Float G(const Vector &wi, const Vector &wo, const Vector &m, Float alphaU, Float alphaV) const {
Float alpha = 0.5f * (alphaX + alphaY); Float alpha = 0.5f * (alphaU + alphaV);
return smithG1(wi, m, alpha) * smithG1(wo, m, alpha); if (EXPECT_TAKEN(m_type != EAshikhminShirley)) {
return smithG1(wi, m, alpha) * smithG1(wo, m, alpha);
} else {
/* Infinite groove shadowing/masking */
const Float nDotM = std::abs(Frame::cosTheta(m)),
nDotWo = std::abs(Frame::cosTheta(wo)),
nDotWi = std::abs(Frame::cosTheta(wi)),
woDotM = absDot(wo, m),
wiDotM = absDot(wi, m);
return std::max((Float) 0, std::min((Float) 1,
std::min(2 * nDotM * nDotWo / woDotM,
2 * nDotM * nDotWi / wiDotM)));
}
} }
std::string toString() const { std::string toString() const {
switch (m_type) { switch (m_type) {
case EBeckmann: return "beckmann"; break; case EBeckmann: return "beckmann"; break;
case EPhong: return "phong"; break; case EPhong: return "phong"; break;
case EGGX: return "ggx"; break; case EGGX: return "ggx"; break;
case EAshikhminShirley: return "as"; break;
default: default:
SLog(EError, "Invalid distribution function"); SLog(EError, "Invalid distribution function");
return ""; return "";

View File

@ -32,24 +32,29 @@ MTS_NAMESPACE_BEGIN
* \item \code{beckmann}: Physically-based distribution derived from * \item \code{beckmann}: Physically-based distribution derived from
* Gaussian random surfaces. This is the default. * Gaussian random surfaces. This is the default.
* \item \code{phong}: Classical $\cos^p\theta$ distribution. * \item \code{phong}: Classical $\cos^p\theta$ distribution.
* The Phong exponent $p$ is obtained using a transformation that * Due to the underlying microfacet theory,
* produces roughness similar to a Beckmann distribution of the same
* parameter. Note that due to the underlying microfacet theory,
* the use of this distribution here leads to more realistic * the use of this distribution here leads to more realistic
* behavior than the separately available \pluginref{phong} plugin. * behavior than the separately available \pluginref{phong} plugin.
* \item \code{ggx}: New distribution proposed by * \item \code{ggx}: New distribution proposed by
* Walter et al. meant to better handle the long * Walter et al. meant to better handle the long
* tails observed in transmission measurements through * tails observed in transmission measurements through
* ground glass. Renderings with this distribution may * ground glass. Renderings with this distribution may
* converge slowly. * converge slowly.
* \item \code{as}: Anisotropic microfacet distribution proposed by
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm}
* \end{enumerate} * \end{enumerate}
* Default: \code{beckmann}
* } * }
* \parameter{alpha}{\Float\Or\Texture}{Roughness value of the * \parameter{alpha}{\Float\Or\Texture}{
* unresolved surface microgeometry. When the Beckmann * Specifies the roughness value of the unresolved surface microgeometry.
* distribution is used, this parameter specifies the * When the Beckmann distribution is used, this parameter is equal to the
* \emph{root mean square} (RMS) slope of the microfacets. * \emph{root mean square} (RMS) slope of the microfacets. This
* \default{0.1} * parameter is only valid when \texttt{distribution=beckmann/phong/ggx}.
* \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}.
* \default{0.1}.
* } * }
* \parameter{intIOR}{\Float}{Interior index of refraction \default{1.5046}} * \parameter{intIOR}{\Float}{Interior index of refraction \default{1.5046}}
* \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}} * \parameter{extIOR}{\Float}{Exterior index of refraction \default{1.0}}
@ -59,13 +64,12 @@ MTS_NAMESPACE_BEGIN
* factor used to modulate the transmittance component\default{1.0}} * factor used to modulate the transmittance component\default{1.0}}
* } * }
* *
*
* This plugin implements a realistic microfacet scattering model for rendering * This plugin implements a realistic microfacet scattering model for rendering
* rough interfaces between dielectric materials, such as a transition from air to * rough interfaces between dielectric materials, such as a transition from air to
* ground glass. Microfacet theory describes rough surfaces as an arrangement of * ground glass. Microfacet theory describes rough surfaces as an arrangement of
* unresolved and ideally specular facets, whose normal directions are given by * unresolved and ideally specular facets, whose normal directions are given by
* a specially chosen \emph{microfacet distribution}. By accounting for shadowing * a specially chosen \emph{microfacet distribution}. By accounting for shadowing
* and masking effects between these facets, it is possible to reproduce the * and masking effects between these facets, it is possible to reproduce the important
* off-specular reflections peaks observed in real-world measurements of such * off-specular reflections peaks observed in real-world measurements of such
* materials. * materials.
* \renderings{ * \renderings{
@ -85,19 +89,24 @@ MTS_NAMESPACE_BEGIN
* several types of microfacet distributions and has a texturable roughness * 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 each be independently
* specified, where ``exterior'' refers to the side that contains the surface * specified, where ``exterior'' refers to the side that contains the surface
* normal. When no parameters are given, the plugin activates the defaults, which * normal. When no parameters are given, the plugin activates the default
* describe a borosilicate glass BK7/air interface with a light amount of * settings, which describe a borosilicate glass BK7/air interface with a
* rougness modeled using a Beckmann distribution. * 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$.
* *
* When using this plugin, it is crucial that the scene contains * When using this plugin, it is crucial that the scene contains
* meaningful and mutally compatible index of refraction change---see * meaningful and mutally compatible index of refraction changes---see
* \figref{glass-explanation} for an example. Also, please note that * \figref{glass-explanation} for an example. Also, please note that
* the importance sampling implementation of this model is close, but * the importance sampling implementation of this model is close, but
* not perfect a perfect match to the underlying scattering distribution, * not perfect a perfect match to the underlying scattering distribution,
* particularly for high roughness values and when the \texttt{GGX} * particularly for high roughness values and when the \texttt{GGX}
* model is used. Hence, such renderings may converge slowly. * model is used. Hence, such renderings may converge slowly.\vspace{1cm}
* *
* \begin{xml}[caption=Ground glass, label=lst:roughdielectric-roughglass] * \begin{xml}[caption=Material definition for ground glass, label=lst:roughdielectric-roughglass]
* <bsdf type="roughdielectric"> * <bsdf type="roughdielectric">
* <string name="distribution" value="ggx"/> * <string name="distribution" value="ggx"/>
* <float name="alpha" value="0.304"/> * <float name="alpha" value="0.304"/>
@ -106,7 +115,7 @@ MTS_NAMESPACE_BEGIN
* </bsdf> * </bsdf>
* \end{xml} * \end{xml}
* *
* \begin{xml}[caption=Textured rougness, label=lst:roughdielectric-textured] * \begin{xml}[caption=A texture can be attached to the roughness parameter, label=lst:roughdielectric-textured]
* <bsdf type="roughdielectric"> * <bsdf type="roughdielectric">
* <string name="distribution" value="beckmann"/> * <string name="distribution" value="beckmann"/>
* <float name="intIOR" value="1.5046"/> * <float name="intIOR" value="1.5046"/>
@ -139,14 +148,14 @@ public:
); );
Float alpha = props.getFloat("alpha", 0.1f), Float alpha = props.getFloat("alpha", 0.1f),
alphaX = props.getFloat("alphaX", alpha), alphaU = props.getFloat("alphaU", alpha),
alphaY = props.getFloat("alphaY", alpha); alphaV = props.getFloat("alphaV", alpha);
m_alphaX = new ConstantFloatTexture(alphaX); m_alphaU = new ConstantFloatTexture(alphaU);
if (alphaX == alphaY) if (alphaU == alphaV)
m_alphaY = m_alphaX; m_alphaV = m_alphaU;
else else
m_alphaY = new ConstantFloatTexture(alphaY); m_alphaV = new ConstantFloatTexture(alphaV);
m_usesRayDifferentials = false; m_usesRayDifferentials = false;
} }
@ -156,8 +165,8 @@ public:
m_distribution = MicrofacetDistribution( m_distribution = MicrofacetDistribution(
(MicrofacetDistribution::EType) stream->readUInt() (MicrofacetDistribution::EType) stream->readUInt()
); );
m_alphaX = static_cast<Texture *>(manager->getInstance(stream)); m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
m_alphaY = static_cast<Texture *>(manager->getInstance(stream)); m_alphaV = static_cast<Texture *>(manager->getInstance(stream));
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream)); m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
m_specularTransmittance = static_cast<Texture *>(manager->getInstance(stream)); m_specularTransmittance = static_cast<Texture *>(manager->getInstance(stream));
m_intIOR = stream->readFloat(); m_intIOR = stream->readFloat();
@ -169,8 +178,8 @@ public:
EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler); EGlossyTransmission | EFrontSide | EBackSide | ECanUseSampler);
m_usesRayDifferentials = m_usesRayDifferentials =
m_alphaX->usesRayDifferentials() || m_alphaU->usesRayDifferentials() ||
m_alphaY->usesRayDifferentials() || m_alphaV->usesRayDifferentials() ||
m_specularReflectance->usesRayDifferentials() || m_specularReflectance->usesRayDifferentials() ||
m_specularTransmittance->usesRayDifferentials(); m_specularTransmittance->usesRayDifferentials();
configure(); configure();
@ -179,7 +188,7 @@ public:
void configure() { void configure() {
unsigned int extraFlags = 0; unsigned int extraFlags = 0;
m_components.clear(); m_components.clear();
if (m_alphaX != m_alphaY) if (m_alphaU != m_alphaV)
extraFlags |= EAnisotropic; extraFlags |= EAnisotropic;
m_components.push_back( m_components.push_back(
EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler | extraFlags); EGlossyReflection | EFrontSide | EBackSide | ECanUseSampler | extraFlags);
@ -194,6 +203,29 @@ public:
return (value < 0) ? -1.0f : 1.0f; return (value < 0) ? -1.0f : 1.0f;
} }
/// Helper function: reflect \c wi with respect to a given surface normal
inline Vector reflect(const Vector &wi, const Normal &m) const {
return 2 * dot(wi, m) * Vector(m) - wi;
}
/// Helper function: refract \c wi with respect to a given surface normal
inline bool refract(const Vector &wi, Vector &wo, const Normal &m, Float etaI, Float etaT) const {
Float eta = etaI / etaT, c = dot(wi, m);
/* Using Snell's law, calculate the squared cosine of the
angle between the normal and the transmitted ray */
Float cosThetaTSqr = 1 + eta * eta * (c*c-1);
if (cosThetaTSqr < 0)
return false; // Total internal reflection
/* Compute the transmitted direction */
wo = m * (eta*c - signum(wi.z)
* std::sqrt(cosThetaTSqr)) - wi * eta;
return true;
}
Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const { Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const {
if (measure != ESolidAngle) if (measure != ESolidAngle)
return Spectrum(0.0f); return Spectrum(0.0f);
@ -232,13 +264,13 @@ public:
} }
/* Evaluate the roughness */ /* Evaluate the roughness */
Float alphaX = m_distribution.transformRoughness( Float alphaU = m_distribution.transformRoughness(
m_alphaX->getValue(bRec.its).average()), m_alphaU->getValue(bRec.its).average()),
alphaY = m_distribution.transformRoughness( alphaV = m_distribution.transformRoughness(
m_alphaY->getValue(bRec.its).average()); m_alphaV->getValue(bRec.its).average());
/* Microsurface normal distribution */ /* Microsurface normal distribution */
const Float D = m_distribution.eval(H, alphaX, alphaY); const Float D = m_distribution.eval(H, alphaU, alphaV);
if (D == 0) if (D == 0)
return Spectrum(0.0f); return Spectrum(0.0f);
@ -246,12 +278,12 @@ public:
const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR); const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR);
/* Smith's shadow-masking function */ /* Smith's shadow-masking function */
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaX, alphaY); const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV);
if (reflect) { if (reflect) {
/* Calculate the total amount of reflection */ /* Calculate the total amount of reflection */
Float value = F * D * G / Float value = F * D * G /
(4.0f * Frame::cosTheta(bRec.wi)); (4.0f * std::abs(Frame::cosTheta(bRec.wi)));
return m_specularReflectance->getValue(bRec.its) * value; return m_specularReflectance->getValue(bRec.its) * value;
} else { } else {
@ -321,20 +353,20 @@ public:
} }
/* Evaluate the roughness */ /* Evaluate the roughness */
Float alphaX = m_distribution.transformRoughness( Float alphaU = m_distribution.transformRoughness(
m_alphaX->getValue(bRec.its).average()), m_alphaU->getValue(bRec.its).average()),
alphaY = m_distribution.transformRoughness( alphaV = m_distribution.transformRoughness(
m_alphaY->getValue(bRec.its).average()); m_alphaV->getValue(bRec.its).average());
/* Suggestion by Bruce Walter: sample using a slightly wider /* Suggestion by Bruce Walter: sample using a slightly wider
density function. This in practice limits the weights to density function. This in practice limits the weights to
values <= 4. See also \ref sample() */ values <= 4. See also \ref sample() */
Float factor = (1.2f - 0.2f * std::sqrt( Float factor = (1.2f - 0.2f * std::sqrt(
std::abs(Frame::cosTheta(bRec.wi)))); std::abs(Frame::cosTheta(bRec.wi))));
alphaX *= factor; alphaY *= factor; alphaU *= factor; alphaV *= factor;
/* Microsurface normal distribution */ /* Microsurface normal distribution */
Float prob = m_distribution.eval(H, alphaX, alphaY); Float prob = m_distribution.eval(H, alphaU, alphaV);
if (sampleTransmission && sampleReflection) { if (sampleTransmission && sampleReflection) {
/* Please see the sample() methods if the /* Please see the sample() methods if the
@ -407,22 +439,22 @@ public:
} }
/* Evaluate the roughness */ /* Evaluate the roughness */
Float alphaX = m_distribution.transformRoughness( Float alphaU = m_distribution.transformRoughness(
m_alphaX->getValue(bRec.its).average()), m_alphaU->getValue(bRec.its).average()),
alphaY = m_distribution.transformRoughness( alphaV = m_distribution.transformRoughness(
m_alphaY->getValue(bRec.its).average()); m_alphaV->getValue(bRec.its).average());
/* Suggestion by Bruce Walter: sample using a slightly wider /* Suggestion by Bruce Walter: sample using a slightly wider
density function. This in practice limits the weights to density function. This in practice limits the weights to
values <= 4. See also \ref sample() */ values <= 4. See also \ref sample() */
Float factor = (1.2f - 0.2f * std::sqrt( Float factor = (1.2f - 0.2f * std::sqrt(
std::abs(Frame::cosTheta(bRec.wi)))); std::abs(Frame::cosTheta(bRec.wi))));
Float sampleAlphaX = alphaX * factor, Float sampleAlphaU = alphaU * factor,
sampleAlphaY = alphaY * factor; sampleAlphaV = alphaV * factor;
/* Sample M, the microsurface normal */ /* Sample M, the microsurface normal */
const Normal m = m_distribution.sample(sample, const Normal m = m_distribution.sample(sample,
sampleAlphaX, sampleAlphaY); sampleAlphaU, sampleAlphaV);
if (sampleExactFresnelTerm) { if (sampleExactFresnelTerm) {
sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR); sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR);
@ -463,11 +495,11 @@ public:
* ((bRec.quantity == ERadiance) ? ((etaI*etaI) / (etaT*etaT)) : (Float) 1); * ((bRec.quantity == ERadiance) ? ((etaI*etaI) / (etaT*etaT)) : (Float) 1);
} }
Float numerator = m_distribution.eval(m, alphaX, alphaY) Float numerator = m_distribution.eval(m, alphaU, alphaV)
* m_distribution.G(bRec.wi, bRec.wo, m, alphaX, alphaY) * m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
* dot(bRec.wi, m); * dot(bRec.wi, m);
Float denominator = m_distribution.eval(m, sampleAlphaX, sampleAlphaY) Float denominator = m_distribution.eval(m, sampleAlphaU, sampleAlphaV)
* Frame::cosTheta(m) * Frame::cosTheta(m)
* Frame::cosTheta(bRec.wi); * Frame::cosTheta(bRec.wi);
@ -533,21 +565,21 @@ public:
} }
/* Evaluate the roughness */ /* Evaluate the roughness */
Float alphaX = m_distribution.transformRoughness( Float alphaU = m_distribution.transformRoughness(
m_alphaX->getValue(bRec.its).average()), m_alphaU->getValue(bRec.its).average()),
alphaY = m_distribution.transformRoughness( alphaV = m_distribution.transformRoughness(
m_alphaY->getValue(bRec.its).average()); m_alphaV->getValue(bRec.its).average());
/* Suggestion by Bruce Walter: sample using a slightly different /* Suggestion by Bruce Walter: sample using a slightly different
value of alpha. This in practice limits the weights to value of alpha. This in practice limits the weights to
values <= 4. See also \ref sample() */ values <= 4. See also \ref sample() */
Float factor = (1.2f - 0.2f * std::sqrt( Float factor = (1.2f - 0.2f * std::sqrt(
std::abs(Frame::cosTheta(bRec.wi)))); std::abs(Frame::cosTheta(bRec.wi))));
Float sampleAlphaX = alphaX * factor, Float sampleAlphaU = alphaU * factor,
sampleAlphaY = alphaY * factor; sampleAlphaV = alphaV * factor;
/* Sample M, the microsurface normal */ /* Sample M, the microsurface normal */
const Normal m = m_distribution.sample(sample, sampleAlphaX, sampleAlphaY); const Normal m = m_distribution.sample(sample, sampleAlphaU, sampleAlphaV);
if (sampleExactFresnelTerm) { if (sampleExactFresnelTerm) {
Float sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR); Float sampleF = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR);
@ -593,14 +625,14 @@ public:
void addChild(const std::string &name, ConfigurableObject *child) { void addChild(const std::string &name, ConfigurableObject *child) {
if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alpha") { if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alpha") {
m_alphaX = m_alphaY = static_cast<Texture *>(child); m_alphaU = m_alphaV = static_cast<Texture *>(child);
m_usesRayDifferentials |= m_alphaX->usesRayDifferentials(); m_usesRayDifferentials |= m_alphaU->usesRayDifferentials();
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaX") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaU") {
m_alphaX = static_cast<Texture *>(child); m_alphaU = static_cast<Texture *>(child);
m_usesRayDifferentials |= m_alphaX->usesRayDifferentials(); m_usesRayDifferentials |= m_alphaU->usesRayDifferentials();
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaY") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "alphaV") {
m_alphaY = static_cast<Texture *>(child); m_alphaV = static_cast<Texture *>(child);
m_usesRayDifferentials |= m_alphaY->usesRayDifferentials(); m_usesRayDifferentials |= m_alphaV->usesRayDifferentials();
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularReflectance") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "specularReflectance") {
m_specularReflectance = static_cast<Texture *>(child); m_specularReflectance = static_cast<Texture *>(child);
m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials(); m_usesRayDifferentials |= m_specularReflectance->usesRayDifferentials();
@ -616,8 +648,8 @@ public:
BSDF::serialize(stream, manager); BSDF::serialize(stream, manager);
stream->writeUInt((uint32_t) m_distribution.getType()); stream->writeUInt((uint32_t) m_distribution.getType());
manager->serialize(stream, m_alphaX.get()); manager->serialize(stream, m_alphaU.get());
manager->serialize(stream, m_alphaY.get()); manager->serialize(stream, m_alphaV.get());
manager->serialize(stream, m_specularReflectance.get()); manager->serialize(stream, m_specularReflectance.get());
manager->serialize(stream, m_specularTransmittance.get()); manager->serialize(stream, m_specularTransmittance.get());
stream->writeFloat(m_intIOR); stream->writeFloat(m_intIOR);
@ -628,8 +660,8 @@ public:
std::ostringstream oss; std::ostringstream oss;
oss << "RoughDielectric[" << endl oss << "RoughDielectric[" << endl
<< " distribution = " << m_distribution.toString() << "," << endl << " distribution = " << m_distribution.toString() << "," << endl
<< " alphaX = " << indent(m_alphaX->toString()) << "," << endl << " alphaU = " << indent(m_alphaU->toString()) << "," << endl
<< " alphaY = " << indent(m_alphaY->toString()) << "," << endl << " alphaV = " << indent(m_alphaV->toString()) << "," << endl
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
<< " specularTransmittance = " << indent(m_specularTransmittance->toString()) << "," << endl << " specularTransmittance = " << indent(m_specularTransmittance->toString()) << "," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
@ -645,7 +677,7 @@ private:
MicrofacetDistribution m_distribution; MicrofacetDistribution m_distribution;
ref<Texture> m_specularTransmittance; ref<Texture> m_specularTransmittance;
ref<Texture> m_specularReflectance; ref<Texture> m_specularReflectance;
ref<Texture> m_alphaX, m_alphaY; ref<Texture> m_alphaU, m_alphaV;
Float m_intIOR, m_extIOR; Float m_intIOR, m_extIOR;
}; };
@ -663,7 +695,7 @@ public:
const std::string &evalName, const std::string &evalName,
const std::vector<std::string> &depNames) const { const std::vector<std::string> &depNames) const {
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " return vec3(0.08) * cosTheta(wo);" << endl << " return vec3(0.08);" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl