added the ashikhmin-shirley microfacet distribution
parent
84146f8168
commit
ac63fa896b
|
@ -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"/>
|
||||||
|
|
|
@ -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.}
|
||||||
|
}
|
||||||
|
|
|
@ -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 "";
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue