ported all microfacet models to a new visible normal sampling implementation
parent
bdc43fcbd8
commit
d02d4ebedf
|
@ -78,8 +78,8 @@ def texify(texfile):
|
||||||
else:
|
else:
|
||||||
check_call(['pdflatex', texfile])
|
check_call(['pdflatex', texfile])
|
||||||
check_call(['bibtex', texfile.replace('.tex', '.aux')])
|
check_call(['bibtex', texfile.replace('.tex', '.aux')])
|
||||||
check_call(['pdflatex', texfile])
|
#check_call(['pdflatex', texfile])
|
||||||
check_call(['pdflatex', texfile])
|
#check_call(['pdflatex', texfile])
|
||||||
|
|
||||||
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
os.chdir(os.path.dirname(os.path.abspath(__file__)))
|
||||||
with open('plugins_generated.tex', 'w') as f:
|
with open('plugins_generated.tex', 'w') as f:
|
||||||
|
|
21
doc/main.bib
21
doc/main.bib
|
@ -563,3 +563,24 @@
|
||||||
year = 2008,
|
year = 2008,
|
||||||
pages = {183--190}
|
pages = {183--190}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@article{Trowbridge19975Average,
|
||||||
|
author = {T. S. Trowbridge and K. P. Reitz},
|
||||||
|
journal = {J. Opt. Soc. Am.},
|
||||||
|
number = {5},
|
||||||
|
pages = {531--536},
|
||||||
|
publisher = {OSA},
|
||||||
|
title = {Average irregularity representation of a rough surface for ray reflection},
|
||||||
|
volume = {65},
|
||||||
|
month = {May},
|
||||||
|
year = {1975}
|
||||||
|
}
|
||||||
|
|
||||||
|
@article{Heitz1014Importance,
|
||||||
|
author = {Heitz, Eric and D'Eon, Eugene},
|
||||||
|
title = {Importance Sampling Microfacet-Based BSDFs using the Distribution of Visible Normals},
|
||||||
|
journal = {Computer Graphics Forum},
|
||||||
|
publisher = {Blackwell Publishing},
|
||||||
|
year = {2014},
|
||||||
|
month = Jun
|
||||||
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -31,17 +31,20 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \parameter{distribution}{\String}{
|
* \parameter{distribution}{\String}{
|
||||||
* Specifies the type of microfacet normal distribution
|
* Specifies the type of microfacet normal distribution
|
||||||
* used to model the surface roughness.
|
* used to model the surface roughness.
|
||||||
|
* \vspace{-1mm}
|
||||||
* \begin{enumerate}[(i)]
|
* \begin{enumerate}[(i)]
|
||||||
* \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.\vspace{-1.5mm}
|
||||||
* \item \code{ggx}: New distribution proposed by
|
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||||
* the long tails observed in measurements of ground surfaces.
|
* was designed to better approximate the long tails observed in measurements
|
||||||
* Renderings with this distribution may converge slowly.
|
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
* \vspace{-1.5mm}
|
||||||
* Due to the underlying microfacet theory,
|
* \item \code{phong}: Classical Phong distribution.
|
||||||
* the use of this distribution here leads to more realistic
|
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||||
* behavior than the separately available \pluginref{phong} plugin.
|
* should be preferred, since they provide better importance sampling
|
||||||
|
* and accurate shadowing/masking computations.
|
||||||
|
* \vspace{-4mm}
|
||||||
* \end{enumerate}
|
* \end{enumerate}
|
||||||
* }
|
* }
|
||||||
* \parameter{alpha}{\Float\Or\Texture}{
|
* \parameter{alpha}{\Float\Or\Texture}{
|
||||||
|
@ -50,6 +53,11 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \emph{root mean square} (RMS) slope of the microfacets.
|
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||||
* \default{0.1}.
|
* \default{0.1}.
|
||||||
* }
|
* }
|
||||||
|
* \parameter{sampleVisible}{\Boolean}{
|
||||||
|
* Enables an improved importance sampling technique. Refer to
|
||||||
|
* pages \pageref{plg:roughconductor} and \pageref{sec:visiblenormal-sampling}
|
||||||
|
* for details. \default{\code{true}}
|
||||||
|
* }
|
||||||
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
||||||
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
||||||
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
||||||
|
@ -90,9 +98,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* loss for materials that reflect much of their energy near or below the critical
|
* loss for materials that reflect much of their energy near or below the critical
|
||||||
* angle (i.e. diffuse or very rough materials).
|
* angle (i.e. diffuse or very rough materials).
|
||||||
*
|
*
|
||||||
* The implementation here is influenced by the paper
|
* The implementation here is motivated by the paper
|
||||||
* ``Arbitrarily Layered Micro-Facet Surfaces'' by Weidlich and
|
* ``Arbitrarily Layered Micro-Facet Surfaces'' by Weidlich and
|
||||||
* Wilkie \cite{Weidlich2007Arbitrarily}.
|
* Wilkie \cite{Weidlich2007Arbitrarily}, though the implementation
|
||||||
|
* works differently.
|
||||||
*/
|
*/
|
||||||
class RoughCoating : public BSDF {
|
class RoughCoating : public BSDF {
|
||||||
public:
|
public:
|
||||||
|
@ -127,25 +136,23 @@ public:
|
||||||
m_specularReflectance = new ConstantSpectrumTexture(
|
m_specularReflectance = new ConstantSpectrumTexture(
|
||||||
props.getSpectrum("specularReflectance", Spectrum(1.0f)));
|
props.getSpectrum("specularReflectance", Spectrum(1.0f)));
|
||||||
|
|
||||||
m_distribution = MicrofacetDistribution(
|
MicrofacetDistribution distr(props);
|
||||||
props.getString("distribution", "beckmann")
|
m_type = distr.getType();
|
||||||
);
|
m_sampleVisible = distr.getSampleVisible();
|
||||||
|
|
||||||
if (m_distribution.isAnisotropic())
|
if (distr.isAnisotropic())
|
||||||
Log(EError, "The 'roughcoating' plugin currently does not support "
|
Log(EError, "The 'roughplastic' plugin currently does not support "
|
||||||
"anisotropic microfacet distributions!");
|
"anisotropic microfacet distributions!");
|
||||||
|
|
||||||
m_alpha = new ConstantFloatTexture(
|
m_alpha = new ConstantFloatTexture(distr.getAlpha());
|
||||||
props.getFloat("alpha", 0.1f));
|
|
||||||
|
|
||||||
m_specularSamplingWeight = 0.0f;
|
m_specularSamplingWeight = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoughCoating(Stream *stream, InstanceManager *manager)
|
RoughCoating(Stream *stream, InstanceManager *manager)
|
||||||
: BSDF(stream, manager) {
|
: BSDF(stream, manager) {
|
||||||
m_distribution = MicrofacetDistribution(
|
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||||
(MicrofacetDistribution::EType) stream->readUInt()
|
m_sampleVisible = stream->readBool();
|
||||||
);
|
|
||||||
m_nested = static_cast<BSDF *>(manager->getInstance(stream));
|
m_nested = static_cast<BSDF *>(manager->getInstance(stream));
|
||||||
m_sigmaA = static_cast<Texture *>(manager->getInstance(stream));
|
m_sigmaA = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
|
@ -160,7 +167,8 @@ public:
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||||
BSDF::serialize(stream, manager);
|
BSDF::serialize(stream, manager);
|
||||||
|
|
||||||
stream->writeUInt((uint32_t) m_distribution.getType());
|
stream->writeUInt((uint32_t) m_type);
|
||||||
|
stream->writeBool(m_sampleVisible);
|
||||||
manager->serialize(stream, m_nested.get());
|
manager->serialize(stream, m_nested.get());
|
||||||
manager->serialize(stream, m_sigmaA.get());
|
manager->serialize(stream, m_sigmaA.get());
|
||||||
manager->serialize(stream, m_specularReflectance.get());
|
manager->serialize(stream, m_specularReflectance.get());
|
||||||
|
@ -200,8 +208,7 @@ public:
|
||||||
if (!m_roughTransmittance.get()) {
|
if (!m_roughTransmittance.get()) {
|
||||||
/* Load precomputed data used to compute the rough
|
/* Load precomputed data used to compute the rough
|
||||||
transmittance through the dielectric interface */
|
transmittance through the dielectric interface */
|
||||||
m_roughTransmittance = new RoughTransmittance(
|
m_roughTransmittance = new RoughTransmittance(m_type);
|
||||||
m_distribution.getType());
|
|
||||||
|
|
||||||
m_roughTransmittance->checkEta(m_eta);
|
m_roughTransmittance->checkEta(m_eta);
|
||||||
m_roughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
m_roughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
||||||
|
@ -254,9 +261,13 @@ public:
|
||||||
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1)
|
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1)
|
||||||
&& measure == ESolidAngle;
|
&& measure == ESolidAngle;
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
Spectrum result(0.0f);
|
Spectrum result(0.0f);
|
||||||
if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
|
if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
|
||||||
|
@ -264,14 +275,14 @@ public:
|
||||||
const Vector H = normalize(bRec.wo+bRec.wi)
|
const Vector H = normalize(bRec.wo+bRec.wi)
|
||||||
* math::signum(Frame::cosTheta(bRec.wo));
|
* math::signum(Frame::cosTheta(bRec.wo));
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet normal distribution */
|
||||||
const Float D = m_distribution.eval(H, alphaT);
|
const Float D = distr.eval(H);
|
||||||
|
|
||||||
/* Fresnel term */
|
/* Fresnel term */
|
||||||
const Float F = fresnelDielectricExt(absDot(bRec.wi, H), m_eta);
|
const Float F = fresnelDielectricExt(absDot(bRec.wi, H), m_eta);
|
||||||
|
|
||||||
/* Smith's shadow-masking function */
|
/* Smith's shadow-masking function */
|
||||||
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);
|
const Float G = distr.G(bRec.wi, bRec.wo, H);
|
||||||
|
|
||||||
/* Calculate the specular reflection component */
|
/* Calculate the specular reflection component */
|
||||||
Float value = F * D * G /
|
Float value = F * D * G /
|
||||||
|
@ -286,8 +297,8 @@ public:
|
||||||
bRecInt.wo = refractTo(EInterior, bRec.wo);
|
bRecInt.wo = refractTo(EInterior, bRec.wo);
|
||||||
|
|
||||||
Spectrum nestedResult = m_nested->eval(bRecInt, measure) *
|
Spectrum nestedResult = m_nested->eval(bRecInt, measure) *
|
||||||
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha) *
|
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), distr.getAlpha()) *
|
||||||
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), alpha);
|
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), distr.getAlpha());
|
||||||
|
|
||||||
Spectrum sigmaA = m_sigmaA->eval(bRec.its) * m_thickness;
|
Spectrum sigmaA = m_sigmaA->eval(bRec.its) * m_thickness;
|
||||||
if (!sigmaA.isZero())
|
if (!sigmaA.isZero())
|
||||||
|
@ -319,15 +330,19 @@ public:
|
||||||
const Vector H = normalize(bRec.wo+bRec.wi)
|
const Vector H = normalize(bRec.wo+bRec.wi)
|
||||||
* math::signum(Frame::cosTheta(bRec.wo));
|
* math::signum(Frame::cosTheta(bRec.wo));
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
Float probNested, probSpecular;
|
Float probNested, probSpecular;
|
||||||
if (hasSpecular && hasNested) {
|
if (hasSpecular && hasNested) {
|
||||||
/* Find the probability of sampling the specular component */
|
/* Find the probability of sampling the specular component */
|
||||||
probSpecular = 1-m_roughTransmittance->eval(
|
probSpecular = 1-m_roughTransmittance->eval(
|
||||||
std::abs(Frame::cosTheta(bRec.wi)), alpha);
|
std::abs(Frame::cosTheta(bRec.wi)), distr.getAlpha());
|
||||||
|
|
||||||
/* Reallocate samples */
|
/* Reallocate samples */
|
||||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||||
|
@ -344,8 +359,8 @@ public:
|
||||||
/* Jacobian of the half-direction mapping */
|
/* Jacobian of the half-direction mapping */
|
||||||
const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H));
|
const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H));
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet model sampling density function */
|
||||||
const Float prob = m_distribution.pdf(H, alphaT);
|
const Float prob = distr.pdf(bRec.wi, H);
|
||||||
|
|
||||||
result = prob * dwh_dwo * probSpecular;
|
result = prob * dwh_dwo * probSpecular;
|
||||||
}
|
}
|
||||||
|
@ -377,14 +392,19 @@ public:
|
||||||
bool choseSpecular = hasSpecular;
|
bool choseSpecular = hasSpecular;
|
||||||
Point2 sample(_sample);
|
Point2 sample(_sample);
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
Float probSpecular;
|
Float probSpecular;
|
||||||
if (hasSpecular && hasNested) {
|
if (hasSpecular && hasNested) {
|
||||||
/* Find the probability of sampling the diffuse component */
|
/* Find the probability of sampling the diffuse component */
|
||||||
probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha);
|
probSpecular = 1 - m_roughTransmittance->eval(
|
||||||
|
std::abs(Frame::cosTheta(bRec.wi)), distr.getAlpha());
|
||||||
|
|
||||||
/* Reallocate samples */
|
/* Reallocate samples */
|
||||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||||
|
@ -400,8 +420,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choseSpecular) {
|
if (choseSpecular) {
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
Normal m = m_distribution.sample(sample, alphaT);
|
Normal m = distr.sample(bRec.wi, sample);
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.sampledComponent = (int) m_components.size() - 1;
|
bRec.sampledComponent = (int) m_components.size() - 1;
|
||||||
bRec.sampledType = EGlossyReflection;
|
bRec.sampledType = EGlossyReflection;
|
||||||
|
@ -464,7 +484,8 @@ public:
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RoughCoating[" << endl
|
oss << "RoughCoating[" << endl
|
||||||
<< " id = \"" << getID() << "\"," << endl
|
<< " id = \"" << getID() << "\"," << endl
|
||||||
<< " distribution = " << m_distribution.toString() << "," << endl
|
<< " distribution = " << MicrofacetDistribution::distributionName(m_type) << "," << endl
|
||||||
|
<< " sampleVisible = " << m_sampleVisible << "," << endl
|
||||||
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
|
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
|
||||||
<< " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
|
<< " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
|
||||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||||
|
@ -480,7 +501,7 @@ public:
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
private:
|
private:
|
||||||
MicrofacetDistribution m_distribution;
|
MicrofacetDistribution::EType m_type;
|
||||||
ref<RoughTransmittance> m_roughTransmittance;
|
ref<RoughTransmittance> m_roughTransmittance;
|
||||||
ref<Texture> m_sigmaA;
|
ref<Texture> m_sigmaA;
|
||||||
ref<Texture> m_alpha;
|
ref<Texture> m_alpha;
|
||||||
|
@ -489,6 +510,7 @@ private:
|
||||||
Float m_eta, m_invEta;
|
Float m_eta, m_invEta;
|
||||||
Float m_specularSamplingWeight;
|
Float m_specularSamplingWeight;
|
||||||
Float m_thickness;
|
Float m_thickness;
|
||||||
|
bool m_sampleVisible;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -31,32 +31,30 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \parameter{distribution}{\String}{
|
* \parameter{distribution}{\String}{
|
||||||
* Specifies the type of microfacet normal distribution
|
* Specifies the type of microfacet normal distribution
|
||||||
* used to model the surface roughness.
|
* used to model the surface roughness.
|
||||||
|
* \vspace{-1mm}
|
||||||
* \begin{enumerate}[(i)]
|
* \begin{enumerate}[(i)]
|
||||||
* \item \code{beckmann}: Physically-based distribution derived from
|
* \item \code{beckmann}: Physically-based distribution derived from
|
||||||
* Gaussian random surfaces. This is the default.\vspace{-1mm}
|
* Gaussian random surfaces. This is the default.\vspace{-1.5mm}
|
||||||
* \item \code{ggx}: New distribution proposed by
|
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||||
* the long tails observed in measurements of ground surfaces.
|
* was designed to better approximate the long tails observed in measurements
|
||||||
* Renderings with this distribution may converge slowly.\vspace{-1mm}
|
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
* \vspace{-1.5mm}
|
||||||
* Due to the underlying microfacet theory,
|
* \item \code{phong}: Anisotropic Phong distribution by
|
||||||
* the use of this distribution here leads to more realistic
|
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.
|
||||||
* behavior than the separately available \pluginref{phong} plugin.\vspace{-1mm}
|
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||||
* \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by
|
* should be preferred, since they provide better importance sampling
|
||||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm}
|
* and accurate shadowing/masking computations.
|
||||||
|
* \vspace{-4mm}
|
||||||
* \end{enumerate}
|
* \end{enumerate}
|
||||||
* }
|
* }
|
||||||
* \parameter{alpha}{\Float\Or\Texture}{
|
* \parameter{alpha, alphaU, alphaV}{\Float\Or\Texture}{
|
||||||
* Specifies the roughness of the unresolved surface micro-geometry.
|
* Specifies the roughness of the unresolved surface micro-geometry
|
||||||
* When the Beckmann distribution is used, this parameter is equal to the
|
* along the tangent and bitangent directions. When the Beckmann
|
||||||
* \emph{root mean square} (RMS) slope of the microfacets. This
|
* distribution is used, this parameter is equal to the
|
||||||
* parameter is only valid when \texttt{distribution=beckmann/phong/ggx}.
|
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||||
* \default{0.1}.
|
* \code{alpha} is a convenience parameter to initialize both
|
||||||
* }
|
* \code{alphaU} and \code{alphaV} to the same value. \default{0.1}.
|
||||||
* \parameter{alphaU, alphaV}{\Float\Or\Texture}{
|
|
||||||
* Specifies the anisotropic roughness values along the tangent and
|
|
||||||
* bitangent directions. These parameter are only valid when
|
|
||||||
* \texttt{distribution=as}. \default{0.1}.
|
|
||||||
* }
|
* }
|
||||||
* \parameter{material}{\String}{Name of a material preset, see
|
* \parameter{material}{\String}{Name of a material preset, see
|
||||||
* \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}}
|
* \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}}
|
||||||
|
@ -66,30 +64,36 @@ MTS_NAMESPACE_BEGIN
|
||||||
* Real-valued index of refraction of the surrounding dielectric,
|
* Real-valued index of refraction of the surrounding dielectric,
|
||||||
* or a material name of a dielectric \default{\code{air}}
|
* or a material name of a dielectric \default{\code{air}}
|
||||||
* }
|
* }
|
||||||
|
* \parameter{sampleVisible}{\Boolean}{
|
||||||
|
* Enables a sampling technique proposed by Heitz and D'Eon~\cite{Heitz1014Importance},
|
||||||
|
* which focuses computation on the visible parts of the microfacet normal
|
||||||
|
* distribution, considerably reducing variance in some cases.
|
||||||
|
* \default{\code{true}, i.e. use visible normal sampling}
|
||||||
|
* }
|
||||||
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
||||||
* factor that can be used to modulate the specular reflection component. Note
|
* factor that can be used to modulate the specular reflection component. Note
|
||||||
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
||||||
* }
|
* }
|
||||||
* \vspace{4mm}
|
* \vspace{3mm}
|
||||||
* This plugin implements a realistic microfacet scattering model for rendering
|
* This plugin implements a realistic microfacet scattering model for rendering
|
||||||
* rough conducting materials, such as metals. It can be interpreted as a fancy
|
* rough conducting materials, such as metals. It can be interpreted as a fancy
|
||||||
* version of the Cook-Torrance model and should be preferred over
|
* version of the Cook-Torrance model and should be preferred over
|
||||||
* heuristic models like \pluginref{phong} and \pluginref{ward} when possible.
|
* heuristic models like \pluginref{phong} and \pluginref{ward} if possible.
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Rough copper (Beckmann, $\alpha=0.1$)}
|
* \rendering{Rough copper (Beckmann, $\alpha=0.1$)}
|
||||||
* {bsdf_roughconductor_copper.jpg}
|
* {bsdf_roughconductor_copper.jpg}
|
||||||
* \rendering{Vertically brushed aluminium (Ashikhmin-Shirley,
|
* \rendering{Vertically brushed aluminium (Anisotropic Phong,
|
||||||
* $\alpha_u=0.05,\ \alpha_v=0.3$), see
|
* $\alpha_u=0.05,\ \alpha_v=0.3$), see
|
||||||
* \lstref{roughconductor-aluminium}}
|
* \lstref{roughconductor-aluminium}}
|
||||||
* {bsdf_roughconductor_anisotropic_aluminium.jpg}
|
* {bsdf_roughconductor_anisotropic_aluminium.jpg}
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
* Microfacet theory describes rough
|
* Microfacet theory describes rough surfaces as an arrangement of unresolved
|
||||||
* surfaces as an arrangement of unresolved and ideally specular facets, whose
|
* and ideally specular facets, whose normal directions are given by a
|
||||||
* normal directions are given by a specially chosen \emph{microfacet distribution}.
|
* specially chosen \emph{microfacet distribution}. By accounting for shadowing
|
||||||
* By accounting for shadowing and masking effects between these facets, it is
|
* and masking effects between these facets, it is possible to reproduce the
|
||||||
* possible to reproduce the important off-specular reflections peaks observed
|
* important off-specular reflections peaks observed in real-world measurements
|
||||||
* in real-world measurements of such materials.
|
* of such materials.
|
||||||
*
|
*
|
||||||
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
|
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
|
||||||
* \pluginref{conductor}. For very low values of $\alpha$, the two will
|
* \pluginref{conductor}. For very low values of $\alpha$, the two will
|
||||||
|
@ -98,7 +102,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
*
|
*
|
||||||
* The implementation is based on the paper ``Microfacet Models
|
* The implementation is based on the paper ``Microfacet Models
|
||||||
* for Refraction through Rough Surfaces'' by Walter et al.
|
* for Refraction through Rough Surfaces'' by Walter et al.
|
||||||
* \cite{Walter07Microfacet}. It supports several different types of microfacet
|
* \cite{Walter07Microfacet}. It supports three different types of microfacet
|
||||||
* distributions and has a texturable roughness parameter.
|
* distributions and has a texturable roughness parameter.
|
||||||
* To facilitate the tedious task of specifying spectrally-varying index of
|
* To facilitate the tedious task of specifying spectrally-varying index of
|
||||||
* refraction information, this plugin can access a set of measured materials
|
* refraction information, this plugin can access a set of measured materials
|
||||||
|
@ -109,41 +113,48 @@ MTS_NAMESPACE_BEGIN
|
||||||
* 100% reflecting mirror.
|
* 100% reflecting mirror.
|
||||||
*
|
*
|
||||||
* When no parameters are given, the plugin activates the default settings,
|
* When no parameters are given, the plugin activates the default settings,
|
||||||
* which describe copper with a light amount of roughness modeled using a
|
* which describe copper with a medium amount of roughness modeled using a
|
||||||
* Beckmann distribution.
|
* Beckmann distribution.
|
||||||
*
|
*
|
||||||
* To get an intuition about the effect of the surface roughness
|
* To get an intuition about the effect of the surface roughness parameter
|
||||||
* parameter $\alpha$, consider the following approximate classification:
|
* $\alpha$, consider the following approximate classification: a value of
|
||||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
* $\alpha=0.001-0.01$ corresponds to a material with slight imperfections
|
||||||
* with slight imperfections on an
|
* on an otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
|
||||||
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
||||||
* finish). Values significantly above that are probably not too realistic.
|
* finish). Values significantly above that are probably not too realistic.
|
||||||
* \vspace{4mm}
|
* \vspace{4mm}
|
||||||
* \begin{xml}[caption={A material definition for brushed aluminium}, label=lst:roughconductor-aluminium]
|
* \begin{xml}[caption={A material definition for brushed aluminium}, label=lst:roughconductor-aluminium]
|
||||||
* <bsdf type="roughconductor">
|
* <bsdf type="roughconductor">
|
||||||
* <string name="material" value="Al"/>
|
* <string name="material" value="Al"/>
|
||||||
* <string name="distribution" value="as"/>
|
* <string name="distribution" value="phong"/>
|
||||||
* <float name="alphaU" value="0.05"/>
|
* <float name="alphaU" value="0.05"/>
|
||||||
* <float name="alphaV" value="0.3"/>
|
* <float name="alphaV" value="0.3"/>
|
||||||
* </bsdf>
|
* </bsdf>
|
||||||
* \end{xml}
|
* \end{xml}
|
||||||
*
|
*
|
||||||
* \subsubsection*{Technical details}
|
* \subsubsection*{Technical details}
|
||||||
* When rendering with the Ashikhmin-Shirley or Phong microfacet
|
* All microfacet distributions allow the specification of two distinct
|
||||||
* distributions, a conversion is used to turn the specified
|
* roughness values along the tangent and bitangent directions. This can be
|
||||||
* $\alpha$ roughness value into the exponents of these distributions.
|
* used to provide a material with a ``brushed'' appearance. The alignment
|
||||||
* This is done in a way, such that the different
|
* of the anisotropy will follow the UV parameterization of the underlying
|
||||||
* distributions all produce a similar appearance for the same value of
|
* mesh. This means that such an anisotropic material cannot be applied to
|
||||||
* $\alpha$.
|
* triangle meshes that are missing texture coordinates.
|
||||||
*
|
*
|
||||||
* The Ashikhmin-Shirley microfacet distribution allows the specification
|
* \label{sec:visiblenormal-sampling}
|
||||||
* of two distinct roughness values along the tangent and bitangent
|
* Since Mitsuba 0.5.1, this plugin uses a new importance sampling technique
|
||||||
* directions. This can be used to provide a material with a ``brushed''
|
* contributed by Eric Heitz and Eugene D'Eon, which restricts the sampling
|
||||||
* appearance. The alignment of the anisotropy will follow the UV
|
* domain to the set of visible (unmasked) microfacet normals. The previous
|
||||||
* parameterization of the underlying mesh in this case. This also means that
|
* approach of sampling all normals is still available and can be enabled
|
||||||
* such an anisotropic material cannot be applied to triangle meshes that
|
* by setting \code{sampleVisible} to \code{false}.
|
||||||
* are missing texture coordinates.
|
* Note that this new method is only available for the \code{beckmann} and
|
||||||
|
* \code{ggx} microfacet distributions. When the \code{phong} distribution
|
||||||
|
* is selected, the parameter has no effect.
|
||||||
|
*
|
||||||
|
* When rendering with the Phong microfacet distribution, a conversion is
|
||||||
|
* used to turn the specified Beckmann-equivalent $\alpha$ roughness value
|
||||||
|
* into the exponent parameter of this distribution. This is done in a way,
|
||||||
|
* such that the same value $\alpha$ will produce a similar appearance across
|
||||||
|
* different microfacet distributions.
|
||||||
*
|
*
|
||||||
* When using this plugin, you should ideally compile Mitsuba with support for
|
* When using this plugin, you should ideally compile Mitsuba with support for
|
||||||
* spectral rendering to get the most accurate results. While it also works
|
* spectral rendering to get the most accurate results. While it also works
|
||||||
|
@ -178,26 +189,21 @@ public:
|
||||||
m_eta = props.getSpectrum("eta", intEta) / extEta;
|
m_eta = props.getSpectrum("eta", intEta) / extEta;
|
||||||
m_k = props.getSpectrum("k", intK) / extEta;
|
m_k = props.getSpectrum("k", intK) / extEta;
|
||||||
|
|
||||||
m_distribution = MicrofacetDistribution(
|
MicrofacetDistribution distr(props);
|
||||||
props.getString("distribution", "beckmann")
|
m_type = distr.getType();
|
||||||
);
|
m_sampleVisible = distr.getSampleVisible();
|
||||||
|
|
||||||
Float alpha = props.getFloat("alpha", 0.1f),
|
m_alphaU = new ConstantFloatTexture(distr.getAlphaU());
|
||||||
alphaU = props.getFloat("alphaU", alpha),
|
if (distr.getAlphaU() == distr.getAlphaV())
|
||||||
alphaV = props.getFloat("alphaV", alpha);
|
|
||||||
|
|
||||||
m_alphaU = new ConstantFloatTexture(alphaU);
|
|
||||||
if (alphaU == alphaV)
|
|
||||||
m_alphaV = m_alphaU;
|
m_alphaV = m_alphaU;
|
||||||
else
|
else
|
||||||
m_alphaV = new ConstantFloatTexture(alphaV);
|
m_alphaV = new ConstantFloatTexture(distr.getAlphaV());
|
||||||
}
|
}
|
||||||
|
|
||||||
RoughConductor(Stream *stream, InstanceManager *manager)
|
RoughConductor(Stream *stream, InstanceManager *manager)
|
||||||
: BSDF(stream, manager) {
|
: BSDF(stream, manager) {
|
||||||
m_distribution = MicrofacetDistribution(
|
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||||
(MicrofacetDistribution::EType) stream->readUInt()
|
m_sampleVisible = stream->readBool();
|
||||||
);
|
|
||||||
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
m_alphaV = 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));
|
||||||
|
@ -207,17 +213,23 @@ public:
|
||||||
configure();
|
configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||||
|
BSDF::serialize(stream, manager);
|
||||||
|
|
||||||
|
stream->writeUInt((uint32_t) m_type);
|
||||||
|
stream->writeBool(m_sampleVisible);
|
||||||
|
manager->serialize(stream, m_alphaU.get());
|
||||||
|
manager->serialize(stream, m_alphaV.get());
|
||||||
|
manager->serialize(stream, m_specularReflectance.get());
|
||||||
|
m_eta.serialize(stream);
|
||||||
|
m_k.serialize(stream);
|
||||||
|
}
|
||||||
|
|
||||||
void configure() {
|
void configure() {
|
||||||
unsigned int extraFlags = 0;
|
unsigned int extraFlags = 0;
|
||||||
if (m_alphaU != m_alphaV) {
|
if (m_alphaU != m_alphaV)
|
||||||
extraFlags |= EAnisotropic;
|
extraFlags |= EAnisotropic;
|
||||||
if (m_distribution.getType() !=
|
|
||||||
MicrofacetDistribution::EAshikhminShirley)
|
|
||||||
Log(EError, "Different roughness values along the tangent and "
|
|
||||||
"bitangent directions are only supported when using the "
|
|
||||||
"anisotropic Ashikhmin-Shirley microfacet distribution "
|
|
||||||
"(named \"as\")");
|
|
||||||
}
|
|
||||||
if (!m_alphaU->isConstant() || !m_alphaV->isConstant() ||
|
if (!m_alphaU->isConstant() || !m_alphaV->isConstant() ||
|
||||||
!m_specularReflectance->isConstant())
|
!m_specularReflectance->isConstant())
|
||||||
extraFlags |= ESpatiallyVarying;
|
extraFlags |= ESpatiallyVarying;
|
||||||
|
@ -254,27 +266,31 @@ public:
|
||||||
/* Calculate the reflection half-vector */
|
/* Calculate the reflection half-vector */
|
||||||
Vector H = normalize(bRec.wo+bRec.wi);
|
Vector H = normalize(bRec.wo+bRec.wi);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_distribution.transformRoughness(
|
roughness values at the current surface position. */
|
||||||
m_alphaU->eval(bRec.its).average()),
|
MicrofacetDistribution distr(
|
||||||
alphaV = m_distribution.transformRoughness(
|
m_type,
|
||||||
m_alphaV->eval(bRec.its).average());
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet normal distribution */
|
||||||
const Float D = m_distribution.eval(H, alphaU, alphaV);
|
const Float D = distr.eval(H);
|
||||||
if (D == 0)
|
if (D == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Fresnel factor */
|
/* Fresnel factor */
|
||||||
const Spectrum F = fresnelConductorExact(dot(bRec.wi, H), m_eta, m_k);
|
const Spectrum F = fresnelConductorExact(dot(bRec.wi, H), m_eta, m_k) *
|
||||||
|
m_specularReflectance->eval(bRec.its);
|
||||||
|
|
||||||
/* Smith's shadow-masking function */
|
/* Smith's shadow-masking function */
|
||||||
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV);
|
const Float G = distr.G(bRec.wi, bRec.wo, H);
|
||||||
|
|
||||||
/* Calculate the total amount of reflection */
|
/* Calculate the total amount of reflection */
|
||||||
Float value = D * G / (4.0f * Frame::cosTheta(bRec.wi));
|
Float model = D * G / (4.0f * Frame::cosTheta(bRec.wi));
|
||||||
|
|
||||||
return m_specularReflectance->eval(bRec.its) * F * value;
|
return F * model;
|
||||||
}
|
}
|
||||||
|
|
||||||
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
Float pdf(const BSDFSamplingRecord &bRec, EMeasure measure) const {
|
||||||
|
@ -288,14 +304,20 @@ public:
|
||||||
/* Calculate the reflection half-vector */
|
/* Calculate the reflection half-vector */
|
||||||
Vector H = normalize(bRec.wo+bRec.wi);
|
Vector H = normalize(bRec.wo+bRec.wi);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_distribution.transformRoughness(
|
roughness values at the current surface position. */
|
||||||
m_alphaU->eval(bRec.its).average()),
|
MicrofacetDistribution distr(
|
||||||
alphaV = m_distribution.transformRoughness(
|
m_type,
|
||||||
m_alphaV->eval(bRec.its).average());
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
return m_distribution.pdf(H, alphaU, alphaV)
|
if (m_sampleVisible)
|
||||||
/ (4 * absDot(bRec.wo, H));
|
return distr.eval(H) * distr.smithG1(bRec.wi, H)
|
||||||
|
/ (4.0f * Frame::cosTheta(bRec.wi));
|
||||||
|
else
|
||||||
|
return distr.pdf(bRec.wi, H) / (4 * absDot(bRec.wo, H));
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
|
Spectrum sample(BSDFSamplingRecord &bRec, const Point2 &sample) const {
|
||||||
|
@ -304,21 +326,23 @@ public:
|
||||||
!(bRec.typeMask & EGlossyReflection)))
|
!(bRec.typeMask & EGlossyReflection)))
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_distribution.transformRoughness(
|
roughness values at the current surface position. */
|
||||||
m_alphaU->eval(bRec.its).average()),
|
MicrofacetDistribution distr(
|
||||||
alphaV = m_distribution.transformRoughness(
|
m_type,
|
||||||
m_alphaV->eval(bRec.its).average());
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
/* Sample M, the microsurface normal */
|
/* Sample M, the microfacet normal */
|
||||||
Float microfacetPDF;
|
Float pdf;
|
||||||
const Normal m = m_distribution.sample(sample,
|
Normal m = distr.sample(bRec.wi, sample, pdf);
|
||||||
alphaU, alphaV, microfacetPDF);
|
|
||||||
|
|
||||||
if (microfacetPDF == 0)
|
if (pdf == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.eta = 1.0f;
|
bRec.eta = 1.0f;
|
||||||
bRec.sampledComponent = 0;
|
bRec.sampledComponent = 0;
|
||||||
|
@ -328,18 +352,18 @@ public:
|
||||||
if (Frame::cosTheta(bRec.wo) <= 0)
|
if (Frame::cosTheta(bRec.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
const Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||||
m_eta, m_k);
|
m_eta, m_k) * m_specularReflectance->eval(bRec.its);
|
||||||
|
|
||||||
Float numerator = m_distribution.eval(m, alphaU, alphaV)
|
Float weight;
|
||||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
if (m_sampleVisible) {
|
||||||
* dot(bRec.wi, m);
|
weight = distr.smithG1(bRec.wo, m);
|
||||||
|
} else {
|
||||||
|
weight = distr.eval(m) * distr.G(bRec.wi, bRec.wo, m)
|
||||||
|
* dot(bRec.wi, m) / (pdf * Frame::cosTheta(bRec.wi));
|
||||||
|
}
|
||||||
|
|
||||||
Float denominator = microfacetPDF
|
return F * weight;
|
||||||
* Frame::cosTheta(bRec.wi);
|
|
||||||
|
|
||||||
return m_specularReflectance->eval(bRec.its) * F
|
|
||||||
* (numerator / denominator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
|
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
|
||||||
|
@ -348,20 +372,22 @@ public:
|
||||||
!(bRec.typeMask & EGlossyReflection)))
|
!(bRec.typeMask & EGlossyReflection)))
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_distribution.transformRoughness(
|
roughness values at the current surface position. */
|
||||||
m_alphaU->eval(bRec.its).average()),
|
MicrofacetDistribution distr(
|
||||||
alphaV = m_distribution.transformRoughness(
|
m_type,
|
||||||
m_alphaV->eval(bRec.its).average());
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
/* Sample M, the microsurface normal */
|
/* Sample M, the microfacet normal */
|
||||||
const Normal m = m_distribution.sample(sample,
|
Normal m = distr.sample(bRec.wi, sample, pdf);
|
||||||
alphaU, alphaV, pdf);
|
|
||||||
|
|
||||||
if (pdf == 0)
|
if (pdf == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.eta = 1.0f;
|
bRec.eta = 1.0f;
|
||||||
bRec.sampledComponent = 0;
|
bRec.sampledComponent = 0;
|
||||||
|
@ -371,23 +397,23 @@ public:
|
||||||
if (Frame::cosTheta(bRec.wo) <= 0)
|
if (Frame::cosTheta(bRec.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
const Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||||
m_eta, m_k);
|
m_eta, m_k) * m_specularReflectance->eval(bRec.its);
|
||||||
|
|
||||||
Float numerator = m_distribution.eval(m, alphaU, alphaV)
|
Float weight;
|
||||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
if (m_sampleVisible) {
|
||||||
* dot(bRec.wi, m);
|
weight = distr.smithG1(bRec.wo, m);
|
||||||
|
} else {
|
||||||
Float denominator = pdf * Frame::cosTheta(bRec.wi);
|
weight = distr.eval(m) * distr.G(bRec.wi, bRec.wo, m)
|
||||||
|
* dot(bRec.wi, m) / (pdf * Frame::cosTheta(bRec.wi));
|
||||||
|
}
|
||||||
|
|
||||||
/* Jacobian of the half-direction mapping */
|
/* Jacobian of the half-direction mapping */
|
||||||
pdf /= 4.0f * dot(bRec.wo, m);
|
pdf /= 4.0f * dot(bRec.wo, m);
|
||||||
|
|
||||||
return m_specularReflectance->eval(bRec.its) * F
|
return F * weight;
|
||||||
* (numerator / denominator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void addChild(const std::string &name, ConfigurableObject *child) {
|
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||||
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
|
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
|
||||||
if (name == "alpha")
|
if (name == "alpha")
|
||||||
|
@ -405,17 +431,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
|
||||||
BSDF::serialize(stream, manager);
|
|
||||||
|
|
||||||
stream->writeUInt((uint32_t) m_distribution.getType());
|
|
||||||
manager->serialize(stream, m_alphaU.get());
|
|
||||||
manager->serialize(stream, m_alphaV.get());
|
|
||||||
manager->serialize(stream, m_specularReflectance.get());
|
|
||||||
m_eta.serialize(stream);
|
|
||||||
m_k.serialize(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
Float getRoughness(const Intersection &its, int component) const {
|
Float getRoughness(const Intersection &its, int component) const {
|
||||||
return 0.5f * (m_alphaU->eval(its).average()
|
return 0.5f * (m_alphaU->eval(its).average()
|
||||||
+ m_alphaV->eval(its).average());
|
+ m_alphaV->eval(its).average());
|
||||||
|
@ -425,7 +440,8 @@ public:
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RoughConductor[" << endl
|
oss << "RoughConductor[" << endl
|
||||||
<< " id = \"" << getID() << "\"," << endl
|
<< " id = \"" << getID() << "\"," << endl
|
||||||
<< " distribution = " << m_distribution.toString() << "," << endl
|
<< " distribution = " << MicrofacetDistribution::distributionName(m_type) << "," << endl
|
||||||
|
<< " sampleVisible = " << m_sampleVisible << "," << endl
|
||||||
<< " alphaU = " << indent(m_alphaU->toString()) << "," << endl
|
<< " alphaU = " << indent(m_alphaU->toString()) << "," << endl
|
||||||
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
||||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||||
|
@ -439,9 +455,10 @@ public:
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
private:
|
private:
|
||||||
MicrofacetDistribution m_distribution;
|
MicrofacetDistribution::EType m_type;
|
||||||
ref<Texture> m_specularReflectance;
|
ref<Texture> m_specularReflectance;
|
||||||
ref<Texture> m_alphaU, m_alphaV;
|
ref<Texture> m_alphaU, m_alphaV;
|
||||||
|
bool m_sampleVisible;
|
||||||
Spectrum m_eta, m_k;
|
Spectrum m_eta, m_k;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -24,12 +24,6 @@
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
/* Suggestion by Bruce Walter: sample the model using a slightly
|
|
||||||
wider density function. This in practice limits the importance
|
|
||||||
weights to values <= 4.
|
|
||||||
*/
|
|
||||||
#define ENLARGE_LOBE_TRICK 1
|
|
||||||
|
|
||||||
/*!\plugin{roughdielectric}{Rough dielectric material}
|
/*!\plugin{roughdielectric}{Rough dielectric material}
|
||||||
* \order{5}
|
* \order{5}
|
||||||
* \icon{bsdf_roughdielectric}
|
* \icon{bsdf_roughdielectric}
|
||||||
|
@ -37,42 +31,44 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \parameter{distribution}{\String}{
|
* \parameter{distribution}{\String}{
|
||||||
* Specifies the type of microfacet normal distribution
|
* Specifies the type of microfacet normal distribution
|
||||||
* used to model the surface roughness.
|
* used to model the surface roughness.
|
||||||
|
* \vspace{-1mm}
|
||||||
* \begin{enumerate}[(i)]
|
* \begin{enumerate}[(i)]
|
||||||
* \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.\vspace{-1.5mm}
|
||||||
* \item \code{ggx}: New distribution proposed by
|
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||||
* the long tails observed in measurements of ground surfaces.
|
* was designed to better approximate the long tails observed in measurements
|
||||||
* Renderings with this distribution may converge slowly.
|
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
* \vspace{-1.5mm}
|
||||||
* Due to the underlying microfacet theory,
|
* \item \code{phong}: Anisotropic Phong distribution by
|
||||||
* the use of this distribution here leads to more realistic
|
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.
|
||||||
* behavior than the separately available \pluginref{phong} plugin.
|
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||||
* \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by
|
* should be preferred, since they provide better importance sampling
|
||||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm}
|
* and accurate shadowing/masking computations.
|
||||||
|
* \vspace{-4mm}
|
||||||
* \end{enumerate}
|
* \end{enumerate}
|
||||||
* }
|
* }
|
||||||
* \parameter{alpha}{\Float\Or\Texture}{
|
* \parameter{alpha, alphaU, alphaV}{\Float\Or\Texture}{
|
||||||
* Specifies the roughness of the unresolved surface micro-geometry.
|
* Specifies the roughness of the unresolved surface micro-geometry
|
||||||
* When the Beckmann distribution is used, this parameter is equal to the
|
* along the tangent and bitangent directions. When the Beckmann
|
||||||
* \emph{root mean square} (RMS) slope of the microfacets. This
|
* distribution is used, this parameter is equal to the
|
||||||
* parameter is only valid when \texttt{distribution=beckmann/phong/ggx}.
|
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||||
* \default{0.1}.
|
* \code{alpha} is a convenience parameter to initialize both
|
||||||
* }
|
* \code{alphaU} and \code{alphaV} to the same value. \default{0.1}.
|
||||||
* \parameter{alphaU, alphaV}{\Float\Or\Texture}{
|
|
||||||
* Specifies the anisotropic roughness values along the tangent and
|
|
||||||
* bitangent directions. These parameter are only valid when
|
|
||||||
* \texttt{distribution=as}. \default{0.1}.
|
|
||||||
* }
|
* }
|
||||||
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
||||||
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
||||||
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
||||||
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
||||||
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
* \parameter{sampleVisible}{\Boolean}{
|
||||||
* factor that can be used to modulate the specular reflection component. Note
|
* Enables a sampling technique proposed by Heitz and D'Eon~\cite{Heitz1014Importance},
|
||||||
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
* which focuses computation on the visible parts of the microfacet normal
|
||||||
* \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
|
* distribution, considerably reducing variance in some cases.
|
||||||
* factor that can be used to modulate the specular transmission component. Note
|
* \default{\code{true}, i.e. use visible normal sampling}
|
||||||
|
* }
|
||||||
|
* \parameter{specular\showbreak Reflectance,\newline
|
||||||
|
* specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
|
||||||
|
* factor that can be used to modulate the specular reflection/transmission component. Note
|
||||||
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
||||||
* }\vspace{4mm}
|
* }\vspace{4mm}
|
||||||
*
|
*
|
||||||
|
@ -98,7 +94,7 @@ MTS_NAMESPACE_BEGIN
|
||||||
*
|
*
|
||||||
* The implementation is based on the paper ``Microfacet Models
|
* The implementation is based on the paper ``Microfacet Models
|
||||||
* for Refraction through Rough Surfaces'' by Walter et al.
|
* for Refraction through Rough Surfaces'' by Walter et al.
|
||||||
* \cite{Walter07Microfacet}. It supports several different types of microfacet
|
* \cite{Walter07Microfacet}. It supports three different types of microfacet
|
||||||
* distributions and has a texturable roughness parameter. Exterior and
|
* distributions and has a texturable roughness parameter. Exterior and
|
||||||
* interior IOR values can be specified independently, where ``exterior''
|
* interior IOR values can be specified independently, where ``exterior''
|
||||||
* refers to the side that contains the surface normal. Similar to the
|
* refers to the side that contains the surface normal. Similar to the
|
||||||
|
@ -109,13 +105,12 @@ MTS_NAMESPACE_BEGIN
|
||||||
* glass BK7/air interface with a light amount of roughness modeled using a
|
* glass BK7/air interface with a light amount of roughness modeled using a
|
||||||
* Beckmann distribution.
|
* Beckmann distribution.
|
||||||
*
|
*
|
||||||
* To get an intuition about the effect of the surface roughness
|
* To get an intuition about the effect of the surface roughness parameter
|
||||||
* parameter $\alpha$, consider the following approximate classification:
|
* $\alpha$, consider the following approximate classification: a value of
|
||||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
* $\alpha=0.001-0.01$ corresponds to a material with slight imperfections
|
||||||
* with slight imperfections on an
|
* on an otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
|
||||||
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
||||||
* finish).
|
* finish). Values significantly above that are probably not too realistic.
|
||||||
*
|
*
|
||||||
* Please note that when using this plugin, it is crucial that the scene contains
|
* Please note that when using this plugin, it is crucial that the scene contains
|
||||||
* meaningful and mutually compatible index of refraction changes---see
|
* meaningful and mutually compatible index of refraction changes---see
|
||||||
|
@ -127,20 +122,33 @@ MTS_NAMESPACE_BEGIN
|
||||||
* converge slowly.
|
* converge slowly.
|
||||||
*
|
*
|
||||||
* \subsubsection*{Technical details}
|
* \subsubsection*{Technical details}
|
||||||
* When rendering with the Ashikhmin-Shirley or Phong microfacet
|
* All microfacet distributions allow the specification of two distinct
|
||||||
* distributions, a conversion is used to turn the specified
|
* roughness values along the tangent and bitangent directions. This can be
|
||||||
* $\alpha$ roughness value into the exponents of these distributions.
|
* used to provide a material with a ``brushed'' appearance. The alignment
|
||||||
* This is done in a way, such that the different
|
* of the anisotropy will follow the UV parameterization of the underlying
|
||||||
* distributions all produce a similar appearance for the same value of
|
* mesh. This means that such an anisotropic material cannot be applied to
|
||||||
* $\alpha$.
|
* triangle meshes that are missing texture coordinates.
|
||||||
*
|
*
|
||||||
* The Ashikhmin-Shirley microfacet distribution allows the specification
|
* Since Mitsuba 0.5.1, this plugin uses a new importance sampling technique
|
||||||
* of two distinct roughness values along the tangent and bitangent
|
* contributed by Eric Heitz and Eugene D'Eon, which restricts the sampling
|
||||||
* directions. This can be used to provide a material with a ``brushed''
|
* domain to the set of visible (unmasked) microfacet normals. The previous
|
||||||
* appearance. The alignment of the anisotropy will follow the UV
|
* approach of sampling all normals is still available and can be enabled
|
||||||
* parameterization of the underlying mesh in this case. This also means that
|
* by setting \code{sampleVisible} to \code{false}.
|
||||||
* such an anisotropic material cannot be applied to triangle meshes that
|
* Note that this new method is only available for the \code{beckmann} and
|
||||||
* are missing texture coordinates.\newpage
|
* \code{ggx} microfacet distributions. When the \code{phong} distribution
|
||||||
|
* is selected, the parameter has no effect.
|
||||||
|
*
|
||||||
|
* When rendering with the Phong microfacet distribution, a conversion is
|
||||||
|
* used to turn the specified Beckmann-equivalent $\alpha$ roughness value
|
||||||
|
* into the exponent parameter of this distribution. This is done in a way,
|
||||||
|
* such that the same value $\alpha$ will produce a similar appearance across
|
||||||
|
* different microfacet distributions.
|
||||||
|
*
|
||||||
|
* When rendering with the Phong microfacet distribution, a conversion is
|
||||||
|
* used to turn the specified Beckmann-equivalent $\alpha$ roughness value
|
||||||
|
* into the exponents of the distribution. This is done in a way, such that
|
||||||
|
* the different distributions all produce a similar appearance for the
|
||||||
|
* same value of $\alpha$.
|
||||||
*
|
*
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Ground glass (GGX, $\alpha$=0.304,
|
* \rendering{Ground glass (GGX, $\alpha$=0.304,
|
||||||
|
@ -191,26 +199,21 @@ public:
|
||||||
m_eta = intIOR / extIOR;
|
m_eta = intIOR / extIOR;
|
||||||
m_invEta = 1 / m_eta;
|
m_invEta = 1 / m_eta;
|
||||||
|
|
||||||
m_distribution = MicrofacetDistribution(
|
MicrofacetDistribution distr(props);
|
||||||
props.getString("distribution", "beckmann")
|
m_type = distr.getType();
|
||||||
);
|
m_sampleVisible = distr.getSampleVisible();
|
||||||
|
|
||||||
Float alpha = props.getFloat("alpha", 0.1f),
|
m_alphaU = new ConstantFloatTexture(distr.getAlphaU());
|
||||||
alphaU = props.getFloat("alphaU", alpha),
|
if (distr.getAlphaU() == distr.getAlphaV())
|
||||||
alphaV = props.getFloat("alphaV", alpha);
|
|
||||||
|
|
||||||
m_alphaU = new ConstantFloatTexture(alphaU);
|
|
||||||
if (alphaU == alphaV)
|
|
||||||
m_alphaV = m_alphaU;
|
m_alphaV = m_alphaU;
|
||||||
else
|
else
|
||||||
m_alphaV = new ConstantFloatTexture(alphaV);
|
m_alphaV = new ConstantFloatTexture(distr.getAlphaV());
|
||||||
}
|
}
|
||||||
|
|
||||||
RoughDielectric(Stream *stream, InstanceManager *manager)
|
RoughDielectric(Stream *stream, InstanceManager *manager)
|
||||||
: BSDF(stream, manager) {
|
: BSDF(stream, manager) {
|
||||||
m_distribution = MicrofacetDistribution(
|
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||||
(MicrofacetDistribution::EType) stream->readUInt()
|
m_sampleVisible = stream->readBool();
|
||||||
);
|
|
||||||
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
m_alphaV = 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));
|
||||||
|
@ -221,17 +224,22 @@ public:
|
||||||
configure();
|
configure();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||||
|
BSDF::serialize(stream, manager);
|
||||||
|
|
||||||
|
stream->writeUInt((uint32_t) m_type);
|
||||||
|
stream->writeBool(m_sampleVisible);
|
||||||
|
manager->serialize(stream, m_alphaU.get());
|
||||||
|
manager->serialize(stream, m_alphaV.get());
|
||||||
|
manager->serialize(stream, m_specularReflectance.get());
|
||||||
|
manager->serialize(stream, m_specularTransmittance.get());
|
||||||
|
stream->writeFloat(m_eta);
|
||||||
|
}
|
||||||
|
|
||||||
void configure() {
|
void configure() {
|
||||||
unsigned int extraFlags = 0;
|
unsigned int extraFlags = 0;
|
||||||
if (m_alphaU != m_alphaV) {
|
if (m_alphaU != m_alphaV)
|
||||||
extraFlags |= EAnisotropic;
|
extraFlags |= EAnisotropic;
|
||||||
if (m_distribution.getType() !=
|
|
||||||
MicrofacetDistribution::EAshikhminShirley)
|
|
||||||
Log(EError, "Different roughness values along the tangent and "
|
|
||||||
"bitangent directions are only supported when using the "
|
|
||||||
"anisotropic Ashikhmin-Shirley microfacet distribution "
|
|
||||||
"(named \"as\")");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!m_alphaU->isConstant() || !m_alphaV->isConstant())
|
if (!m_alphaU->isConstant() || !m_alphaV->isConstant())
|
||||||
extraFlags |= ESpatiallyVarying;
|
extraFlags |= ESpatiallyVarying;
|
||||||
|
@ -293,14 +301,17 @@ public:
|
||||||
same hemisphere as the macrosurface normal */
|
same hemisphere as the macrosurface normal */
|
||||||
H *= math::signum(Frame::cosTheta(H));
|
H *= math::signum(Frame::cosTheta(H));
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_distribution.transformRoughness(
|
roughness values at the current surface position. */
|
||||||
m_alphaU->eval(bRec.its).average()),
|
MicrofacetDistribution distr(
|
||||||
alphaV = m_distribution.transformRoughness(
|
m_type,
|
||||||
m_alphaV->eval(bRec.its).average());
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet normal distribution */
|
||||||
const Float D = m_distribution.eval(H, alphaU, alphaV);
|
const Float D = distr.eval(H);
|
||||||
if (D == 0)
|
if (D == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
|
@ -308,7 +319,7 @@ public:
|
||||||
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||||
|
|
||||||
/* Smith's shadow-masking function */
|
/* Smith's shadow-masking function */
|
||||||
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaU, alphaV);
|
const Float G = distr.G(bRec.wi, bRec.wo, H);
|
||||||
|
|
||||||
if (reflect) {
|
if (reflect) {
|
||||||
/* Calculate the total amount of reflection */
|
/* Calculate the total amount of reflection */
|
||||||
|
@ -383,21 +394,24 @@ public:
|
||||||
same hemisphere as the macrosurface normal */
|
same hemisphere as the macrosurface normal */
|
||||||
H *= math::signum(Frame::cosTheta(H));
|
H *= math::signum(Frame::cosTheta(H));
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
roughness values at the current surface position. */
|
||||||
alphaV = m_alphaV->eval(bRec.its).average();
|
MicrofacetDistribution sampleDistr(
|
||||||
|
m_type,
|
||||||
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
#if ENLARGE_LOBE_TRICK == 1
|
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
reduce importance sampling weights. Not needed for the
|
||||||
|
Heitz and D'Eon sampling technique. */
|
||||||
|
if (!m_sampleVisible)
|
||||||
|
sampleDistr.scaleAlpha(1.2f - 0.2f * std::sqrt(
|
||||||
std::abs(Frame::cosTheta(bRec.wi))));
|
std::abs(Frame::cosTheta(bRec.wi))));
|
||||||
alphaU *= factor; alphaV *= factor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alphaU = m_distribution.transformRoughness(alphaU);
|
/* Evaluate the microfacet model sampling density function */
|
||||||
alphaV = m_distribution.transformRoughness(alphaV);
|
Float prob = sampleDistr.pdf(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, H);
|
||||||
|
|
||||||
/* Evaluate the microsurface normal sampling density */
|
|
||||||
Float prob = m_distribution.pdf(H, alphaU, alphaV);
|
|
||||||
|
|
||||||
if (hasTransmission && hasReflection) {
|
if (hasTransmission && hasReflection) {
|
||||||
Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||||
|
@ -419,44 +433,42 @@ public:
|
||||||
if (!hasReflection && !hasTransmission)
|
if (!hasReflection && !hasTransmission)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
roughness values at the current surface position. */
|
||||||
alphaV = m_alphaV->eval(bRec.its).average(),
|
MicrofacetDistribution distr(
|
||||||
sampleAlphaU = alphaU,
|
m_type,
|
||||||
sampleAlphaV = alphaV;
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
#if ENLARGE_LOBE_TRICK == 1
|
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
reduce importance sampling weights. Not needed for the
|
||||||
|
Heitz and D'Eon sampling technique. */
|
||||||
|
MicrofacetDistribution sampleDistr(distr);
|
||||||
|
if (!m_sampleVisible)
|
||||||
|
sampleDistr.scaleAlpha(1.2f - 0.2f * std::sqrt(
|
||||||
std::abs(Frame::cosTheta(bRec.wi))));
|
std::abs(Frame::cosTheta(bRec.wi))));
|
||||||
sampleAlphaU *= factor; sampleAlphaV *= factor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alphaU = m_distribution.transformRoughness(alphaU);
|
/* Sample M, the microfacet normal */
|
||||||
alphaV = m_distribution.transformRoughness(alphaV);
|
|
||||||
sampleAlphaU = m_distribution.transformRoughness(sampleAlphaU);
|
|
||||||
sampleAlphaV = m_distribution.transformRoughness(sampleAlphaV);
|
|
||||||
|
|
||||||
/* Sample M, the microsurface normal */
|
|
||||||
Float microfacetPDF;
|
Float microfacetPDF;
|
||||||
const Normal m = m_distribution.sample(sample,
|
const Normal m = sampleDistr.sample(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, sample, microfacetPDF);
|
||||||
sampleAlphaU, sampleAlphaV, microfacetPDF);
|
|
||||||
|
|
||||||
if (microfacetPDF == 0)
|
if (microfacetPDF == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
Float cosThetaT, numerator = 1.0f;
|
Float cosThetaT;
|
||||||
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
||||||
|
Spectrum weight(1.0f);
|
||||||
|
|
||||||
if (hasReflection && hasTransmission) {
|
if (hasReflection && hasTransmission) {
|
||||||
if (bRec.sampler->next1D() > F)
|
if (bRec.sampler->next1D() > F)
|
||||||
sampleReflection = false;
|
sampleReflection = false;
|
||||||
} else {
|
} else {
|
||||||
numerator = hasReflection ? F : (1-F);
|
weight = Spectrum(hasReflection ? F : (1-F));
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum result;
|
|
||||||
if (sampleReflection) {
|
if (sampleReflection) {
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.eta = 1.0f;
|
bRec.eta = 1.0f;
|
||||||
bRec.sampledComponent = 0;
|
bRec.sampledComponent = 0;
|
||||||
|
@ -466,12 +478,12 @@ public:
|
||||||
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
result = m_specularReflectance->eval(bRec.its);
|
weight *= m_specularReflectance->eval(bRec.its);
|
||||||
} else {
|
} else {
|
||||||
if (cosThetaT == 0)
|
if (cosThetaT == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Perfect specular transmission based on the microsurface normal */
|
/* Perfect specular transmission based on the microfacet normal */
|
||||||
bRec.wo = refract(bRec.wi, m, m_eta, cosThetaT);
|
bRec.wo = refract(bRec.wi, m, m_eta, cosThetaT);
|
||||||
bRec.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
bRec.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
||||||
bRec.sampledComponent = 1;
|
bRec.sampledComponent = 1;
|
||||||
|
@ -486,17 +498,16 @@ public:
|
||||||
Float factor = (bRec.mode == ERadiance)
|
Float factor = (bRec.mode == ERadiance)
|
||||||
? (cosThetaT < 0 ? m_invEta : m_eta) : 1.0f;
|
? (cosThetaT < 0 ? m_invEta : m_eta) : 1.0f;
|
||||||
|
|
||||||
result = m_specularTransmittance->eval(bRec.its) * (factor * factor);
|
weight *= m_specularTransmittance->eval(bRec.its) * (factor * factor);
|
||||||
}
|
}
|
||||||
|
|
||||||
numerator *= m_distribution.eval(m, alphaU, alphaV)
|
if (m_sampleVisible)
|
||||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
weight *= distr.smithG1(bRec.wo, m);
|
||||||
* dot(bRec.wi, m);
|
else
|
||||||
|
weight *= std::abs(distr.eval(m) * distr.G(bRec.wi, bRec.wo, m)
|
||||||
|
* dot(bRec.wi, m) / (microfacetPDF * Frame::cosTheta(bRec.wi)));
|
||||||
|
|
||||||
Float denominator = microfacetPDF
|
return weight;
|
||||||
* Frame::cosTheta(bRec.wi);
|
|
||||||
|
|
||||||
return result * std::abs(numerator / denominator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &_sample) const {
|
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &_sample) const {
|
||||||
|
@ -511,35 +522,33 @@ public:
|
||||||
if (!hasReflection && !hasTransmission)
|
if (!hasReflection && !hasTransmission)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Evaluate the roughness */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
roughness values at the current surface position. */
|
||||||
alphaV = m_alphaV->eval(bRec.its).average(),
|
MicrofacetDistribution distr(
|
||||||
sampleAlphaU = alphaU,
|
m_type,
|
||||||
sampleAlphaV = alphaV;
|
m_alphaU->eval(bRec.its).average(),
|
||||||
|
m_alphaV->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
#if ENLARGE_LOBE_TRICK == 1
|
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
reduce importance sampling weights. Not needed for the
|
||||||
|
Heitz and D'Eon sampling technique. */
|
||||||
|
MicrofacetDistribution sampleDistr(distr);
|
||||||
|
if (!m_sampleVisible)
|
||||||
|
sampleDistr.scaleAlpha(1.2f - 0.2f * std::sqrt(
|
||||||
std::abs(Frame::cosTheta(bRec.wi))));
|
std::abs(Frame::cosTheta(bRec.wi))));
|
||||||
sampleAlphaU *= factor; sampleAlphaV *= factor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
alphaU = m_distribution.transformRoughness(alphaU);
|
/* Sample M, the microfacet normal */
|
||||||
alphaV = m_distribution.transformRoughness(alphaV);
|
|
||||||
sampleAlphaU = m_distribution.transformRoughness(sampleAlphaU);
|
|
||||||
sampleAlphaV = m_distribution.transformRoughness(sampleAlphaV);
|
|
||||||
|
|
||||||
/* Sample M, the microsurface normal */
|
|
||||||
Float microfacetPDF;
|
Float microfacetPDF;
|
||||||
const Normal m = m_distribution.sample(sample,
|
const Normal m = sampleDistr.sample(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, sample, microfacetPDF);
|
||||||
sampleAlphaU, sampleAlphaV, microfacetPDF);
|
|
||||||
|
|
||||||
if (microfacetPDF == 0)
|
if (microfacetPDF == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
pdf = microfacetPDF;
|
pdf = microfacetPDF;
|
||||||
|
|
||||||
Float cosThetaT, numerator = 1.0f;
|
Float cosThetaT;
|
||||||
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
||||||
|
Spectrum weight(1.0f);
|
||||||
|
|
||||||
if (hasReflection && hasTransmission) {
|
if (hasReflection && hasTransmission) {
|
||||||
if (bRec.sampler->next1D() > F) {
|
if (bRec.sampler->next1D() > F) {
|
||||||
|
@ -549,14 +558,12 @@ public:
|
||||||
pdf *= F;
|
pdf *= F;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
numerator = hasReflection ? F : (1-F);
|
weight *= hasReflection ? F : (1-F);
|
||||||
}
|
}
|
||||||
|
|
||||||
Spectrum result;
|
|
||||||
Float dwh_dwo;
|
Float dwh_dwo;
|
||||||
|
|
||||||
if (sampleReflection) {
|
if (sampleReflection) {
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.eta = 1.0f;
|
bRec.eta = 1.0f;
|
||||||
bRec.sampledComponent = 0;
|
bRec.sampledComponent = 0;
|
||||||
|
@ -566,7 +573,7 @@ public:
|
||||||
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
result = m_specularReflectance->eval(bRec.its);
|
weight *= m_specularReflectance->eval(bRec.its);
|
||||||
|
|
||||||
/* Jacobian of the half-direction mapping */
|
/* Jacobian of the half-direction mapping */
|
||||||
dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, m));
|
dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, m));
|
||||||
|
@ -574,7 +581,7 @@ public:
|
||||||
if (cosThetaT == 0)
|
if (cosThetaT == 0)
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Perfect specular transmission based on the microsurface normal */
|
/* Perfect specular transmission based on the microfacet normal */
|
||||||
bRec.wo = refract(bRec.wi, m, m_eta, cosThetaT);
|
bRec.wo = refract(bRec.wi, m, m_eta, cosThetaT);
|
||||||
bRec.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
bRec.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
||||||
bRec.sampledComponent = 1;
|
bRec.sampledComponent = 1;
|
||||||
|
@ -589,22 +596,22 @@ public:
|
||||||
Float factor = (bRec.mode == ERadiance)
|
Float factor = (bRec.mode == ERadiance)
|
||||||
? (cosThetaT < 0 ? m_invEta : m_eta) : 1.0f;
|
? (cosThetaT < 0 ? m_invEta : m_eta) : 1.0f;
|
||||||
|
|
||||||
result = m_specularTransmittance->eval(bRec.its) * (factor * factor);
|
weight *= m_specularTransmittance->eval(bRec.its) * (factor * factor);
|
||||||
|
|
||||||
/* Jacobian of the half-direction mapping */
|
/* Jacobian of the half-direction mapping */
|
||||||
Float sqrtDenom = dot(bRec.wi, m) + bRec.eta * dot(bRec.wo, m);
|
Float sqrtDenom = dot(bRec.wi, m) + bRec.eta * dot(bRec.wo, m);
|
||||||
dwh_dwo = (bRec.eta*bRec.eta * dot(bRec.wo, m)) / (sqrtDenom*sqrtDenom);
|
dwh_dwo = (bRec.eta*bRec.eta * dot(bRec.wo, m)) / (sqrtDenom*sqrtDenom);
|
||||||
}
|
}
|
||||||
|
|
||||||
numerator *= m_distribution.eval(m, alphaU, alphaV)
|
if (m_sampleVisible)
|
||||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
weight *= distr.smithG1(bRec.wo, m);
|
||||||
* dot(bRec.wi, m);
|
else
|
||||||
|
weight *= std::abs(distr.eval(m) * distr.G(bRec.wi, bRec.wo, m)
|
||||||
Float denominator = microfacetPDF * Frame::cosTheta(bRec.wi);
|
* dot(bRec.wi, m) / (microfacetPDF * Frame::cosTheta(bRec.wi)));
|
||||||
|
|
||||||
pdf *= std::abs(dwh_dwo);
|
pdf *= std::abs(dwh_dwo);
|
||||||
|
|
||||||
return result * std::abs(numerator / denominator);
|
return weight;
|
||||||
}
|
}
|
||||||
|
|
||||||
void addChild(const std::string &name, ConfigurableObject *child) {
|
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||||
|
@ -626,17 +633,6 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
|
||||||
BSDF::serialize(stream, manager);
|
|
||||||
|
|
||||||
stream->writeUInt((uint32_t) m_distribution.getType());
|
|
||||||
manager->serialize(stream, m_alphaU.get());
|
|
||||||
manager->serialize(stream, m_alphaV.get());
|
|
||||||
manager->serialize(stream, m_specularReflectance.get());
|
|
||||||
manager->serialize(stream, m_specularTransmittance.get());
|
|
||||||
stream->writeFloat(m_eta);
|
|
||||||
}
|
|
||||||
|
|
||||||
Float getEta() const {
|
Float getEta() const {
|
||||||
return m_eta;
|
return m_eta;
|
||||||
}
|
}
|
||||||
|
@ -650,7 +646,8 @@ public:
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RoughDielectric[" << endl
|
oss << "RoughDielectric[" << endl
|
||||||
<< " id = \"" << getID() << "\"," << endl
|
<< " id = \"" << getID() << "\"," << endl
|
||||||
<< " distribution = " << m_distribution.toString() << "," << endl
|
<< " distribution = " << MicrofacetDistribution::distributionName(m_type) << "," << endl
|
||||||
|
<< " sampleVisible = " << m_sampleVisible << "," << endl
|
||||||
<< " eta = " << m_eta << "," << endl
|
<< " eta = " << m_eta << "," << endl
|
||||||
<< " alphaU = " << indent(m_alphaU->toString()) << "," << endl
|
<< " alphaU = " << indent(m_alphaU->toString()) << "," << endl
|
||||||
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
||||||
|
@ -664,11 +661,12 @@ public:
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
private:
|
private:
|
||||||
MicrofacetDistribution m_distribution;
|
MicrofacetDistribution::EType m_type;
|
||||||
ref<Texture> m_specularTransmittance;
|
ref<Texture> m_specularTransmittance;
|
||||||
ref<Texture> m_specularReflectance;
|
ref<Texture> m_specularReflectance;
|
||||||
ref<Texture> m_alphaU, m_alphaV;
|
ref<Texture> m_alphaU, m_alphaV;
|
||||||
Float m_eta, m_invEta;
|
Float m_eta, m_invEta;
|
||||||
|
bool m_sampleVisible;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Fake glass shader -- it is really hopeless to visualize
|
/* Fake glass shader -- it is really hopeless to visualize
|
||||||
|
|
|
@ -32,18 +32,20 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \parameter{distribution}{\String}{
|
* \parameter{distribution}{\String}{
|
||||||
* Specifies the type of microfacet normal distribution
|
* Specifies the type of microfacet normal distribution
|
||||||
* used to model the surface roughness.
|
* used to model the surface roughness.
|
||||||
|
* \vspace{-1mm}
|
||||||
* \begin{enumerate}[(i)]
|
* \begin{enumerate}[(i)]
|
||||||
* \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.\vspace{-1.5mm}
|
||||||
* \item \code{ggx}: New distribution proposed by
|
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||||
* the long tails observed in measurements of ground surfaces.
|
* was designed to better approximate the long tails observed in measurements
|
||||||
* Renderings with this distribution may converge slowly.
|
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
* \vspace{-1.5mm}
|
||||||
* Due to the underlying microfacet theory,
|
* \item \code{phong}: Classical Phong distribution.
|
||||||
* the use of this distribution here leads to more realistic
|
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||||
* behavior than the separately available \pluginref{phong} plugin.
|
* should be preferred, since they provide better importance sampling
|
||||||
* \vspace{-3mm}
|
* and accurate shadowing/masking computations.
|
||||||
|
* \vspace{-4mm}
|
||||||
* \end{enumerate}
|
* \end{enumerate}
|
||||||
* }
|
* }
|
||||||
* \parameter{alpha}{\Float\Or\Texture}{
|
* \parameter{alpha}{\Float\Or\Texture}{
|
||||||
|
@ -52,10 +54,16 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \emph{root mean square} (RMS) slope of the microfacets.
|
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||||
* \default{0.1}.
|
* \default{0.1}.
|
||||||
* }
|
* }
|
||||||
|
*
|
||||||
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
||||||
* numerically or using a known material name. \default{\texttt{polypropylene} / 1.49}}
|
* numerically or using a known material name. \default{\texttt{polypropylene} / 1.49}}
|
||||||
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
||||||
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
||||||
|
* \parameter{sampleVisible}{\Boolean}{
|
||||||
|
* Enables an improved importance sampling technique. Refer to
|
||||||
|
* pages \pageref{plg:roughconductor} and \pageref{sec:visiblenormal-sampling}
|
||||||
|
* for details. \default{\code{true}}
|
||||||
|
* }
|
||||||
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
||||||
* factor that can be used to modulate the specular reflection component. Note
|
* factor that can be used to modulate the specular reflection component. Note
|
||||||
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
* that for physical realism, this parameter should never be touched. \default{1.0}}
|
||||||
|
@ -75,12 +83,12 @@ MTS_NAMESPACE_BEGIN
|
||||||
* preferred over heuristic models like \pluginref{phong} and \pluginref{ward}
|
* preferred over heuristic models like \pluginref{phong} and \pluginref{ward}
|
||||||
* when possible.
|
* when possible.
|
||||||
*
|
*
|
||||||
* Microfacet theory describes rough surfaces as an arrangement of unresolved and
|
* Microfacet theory describes rough surfaces as an arrangement of
|
||||||
* ideally specular facets, whose normal directions are given by a specially
|
* unresolved and ideally specular facets, whose normal directions are given by
|
||||||
* chosen \emph{microfacet distribution}.
|
* a specially chosen \emph{microfacet distribution}. By accounting for shadowing
|
||||||
* By accounting for shadowing and masking effects between these facets, it is
|
* and masking effects between these facets, it is possible to reproduce the important
|
||||||
* possible to reproduce the important off-specular reflections peaks observed
|
* off-specular reflections peaks observed in real-world measurements of such
|
||||||
* in real-world measurements of such materials.
|
* materials.
|
||||||
*
|
*
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann}
|
* \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann}
|
||||||
|
@ -112,12 +120,10 @@ MTS_NAMESPACE_BEGIN
|
||||||
* of this parameter given in the \pluginref{plastic} plugin section
|
* of this parameter given in the \pluginref{plastic} plugin section
|
||||||
* on \pluginpage{plastic}.
|
* on \pluginpage{plastic}.
|
||||||
*
|
*
|
||||||
*
|
* To get an intuition about the effect of the surface roughness parameter
|
||||||
* To get an intuition about the effect of the surface roughness
|
* $\alpha$, consider the following approximate classification: a value of
|
||||||
* parameter $\alpha$, consider the following approximate classification:
|
* $\alpha=0.001-0.01$ corresponds to a material with slight imperfections
|
||||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
* on an otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||||
* with slight imperfections on an
|
|
||||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
|
||||||
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
|
||||||
* finish). Values significantly above that are probably not too realistic.
|
* finish). Values significantly above that are probably not too realistic.
|
||||||
*
|
*
|
||||||
|
@ -133,7 +139,6 @@ MTS_NAMESPACE_BEGIN
|
||||||
* \pluginref{roughplastic} and support for nonlinear color shifts.
|
* \pluginref{roughplastic} and support for nonlinear color shifts.
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
* \newpage
|
|
||||||
* \renderings{
|
* \renderings{
|
||||||
* \rendering{Wood material with smooth horizontal stripes}{bsdf_roughplastic_roughtex1}
|
* \rendering{Wood material with smooth horizontal stripes}{bsdf_roughplastic_roughtex1}
|
||||||
* \rendering{A material with imperfections at a much smaller scale than what
|
* \rendering{A material with imperfections at a much smaller scale than what
|
||||||
|
@ -181,11 +186,11 @@ MTS_NAMESPACE_BEGIN
|
||||||
* values of this function over a large range of parameter values. At runtime,
|
* values of this function over a large range of parameter values. At runtime,
|
||||||
* the relevant parts are extracted using tricubic interpolation.
|
* the relevant parts are extracted using tricubic interpolation.
|
||||||
*
|
*
|
||||||
* When rendering with the Phong microfacet distributions, a conversion
|
* When rendering with the Phong microfacet distribution, a conversion is
|
||||||
* is used to turn the specified $\alpha$ roughness value into the Phong
|
* used to turn the specified Beckmann-equivalent $\alpha$ roughness value
|
||||||
* exponent. This is done in a way, such that the different distributions
|
* into the exponent parameter of this distribution. This is done in a way,
|
||||||
* all produce a similar appearance for the same value of $\alpha$.
|
* such that the same value $\alpha$ will produce a similar appearance across
|
||||||
*
|
* different microfacet distributions.
|
||||||
*/
|
*/
|
||||||
class RoughPlastic : public BSDF {
|
class RoughPlastic : public BSDF {
|
||||||
public:
|
public:
|
||||||
|
@ -207,27 +212,25 @@ public:
|
||||||
|
|
||||||
m_eta = intIOR / extIOR;
|
m_eta = intIOR / extIOR;
|
||||||
|
|
||||||
m_distribution = MicrofacetDistribution(
|
m_nonlinear = props.getBoolean("nonlinear", false);
|
||||||
props.getString("distribution", "beckmann")
|
|
||||||
);
|
|
||||||
|
|
||||||
if (m_distribution.isAnisotropic())
|
MicrofacetDistribution distr(props);
|
||||||
|
m_type = distr.getType();
|
||||||
|
m_sampleVisible = distr.getSampleVisible();
|
||||||
|
|
||||||
|
if (distr.isAnisotropic())
|
||||||
Log(EError, "The 'roughplastic' plugin currently does not support "
|
Log(EError, "The 'roughplastic' plugin currently does not support "
|
||||||
"anisotropic microfacet distributions!");
|
"anisotropic microfacet distributions!");
|
||||||
|
|
||||||
m_nonlinear = props.getBoolean("nonlinear", false);
|
m_alpha = new ConstantFloatTexture(distr.getAlpha());
|
||||||
|
|
||||||
m_alpha = new ConstantFloatTexture(
|
|
||||||
props.getFloat("alpha", 0.1f));
|
|
||||||
|
|
||||||
m_specularSamplingWeight = 0.0f;
|
m_specularSamplingWeight = 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
RoughPlastic(Stream *stream, InstanceManager *manager)
|
RoughPlastic(Stream *stream, InstanceManager *manager)
|
||||||
: BSDF(stream, manager) {
|
: BSDF(stream, manager) {
|
||||||
m_distribution = MicrofacetDistribution(
|
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||||
(MicrofacetDistribution::EType) stream->readUInt()
|
m_sampleVisible = stream->readBool();
|
||||||
);
|
|
||||||
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
m_diffuseReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
m_diffuseReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
m_alpha = static_cast<Texture *>(manager->getInstance(stream));
|
m_alpha = static_cast<Texture *>(manager->getInstance(stream));
|
||||||
|
@ -240,7 +243,8 @@ public:
|
||||||
void serialize(Stream *stream, InstanceManager *manager) const {
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
||||||
BSDF::serialize(stream, manager);
|
BSDF::serialize(stream, manager);
|
||||||
|
|
||||||
stream->writeUInt((uint32_t) m_distribution.getType());
|
stream->writeUInt((uint32_t) m_type);
|
||||||
|
stream->writeBool(m_sampleVisible);
|
||||||
manager->serialize(stream, m_specularReflectance.get());
|
manager->serialize(stream, m_specularReflectance.get());
|
||||||
manager->serialize(stream, m_diffuseReflectance.get());
|
manager->serialize(stream, m_diffuseReflectance.get());
|
||||||
manager->serialize(stream, m_alpha.get());
|
manager->serialize(stream, m_alpha.get());
|
||||||
|
@ -277,8 +281,7 @@ public:
|
||||||
if (!m_externalRoughTransmittance.get()) {
|
if (!m_externalRoughTransmittance.get()) {
|
||||||
/* Load precomputed data used to compute the rough
|
/* Load precomputed data used to compute the rough
|
||||||
transmittance through the dielectric interface */
|
transmittance through the dielectric interface */
|
||||||
m_externalRoughTransmittance = new RoughTransmittance(
|
m_externalRoughTransmittance = new RoughTransmittance(m_type);
|
||||||
m_distribution.getType());
|
|
||||||
|
|
||||||
m_externalRoughTransmittance->checkEta(m_eta);
|
m_externalRoughTransmittance->checkEta(m_eta);
|
||||||
m_externalRoughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
m_externalRoughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
||||||
|
@ -332,23 +335,27 @@ public:
|
||||||
(!hasSpecular && !hasDiffuse))
|
(!hasSpecular && !hasDiffuse))
|
||||||
return Spectrum(0.0f);
|
return Spectrum(0.0f);
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
Spectrum result(0.0f);
|
Spectrum result(0.0f);
|
||||||
if (hasSpecular) {
|
if (hasSpecular) {
|
||||||
/* Calculate the reflection half-vector */
|
/* Calculate the reflection half-vector */
|
||||||
const Vector H = normalize(bRec.wo+bRec.wi);
|
const Vector H = normalize(bRec.wo+bRec.wi);
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet normal distribution */
|
||||||
const Float D = m_distribution.eval(H, alphaT);
|
const Float D = distr.eval(H);
|
||||||
|
|
||||||
/* Fresnel term */
|
/* Fresnel term */
|
||||||
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||||
|
|
||||||
/* Smith's shadow-masking function */
|
/* Smith's shadow-masking function */
|
||||||
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);
|
const Float G = distr.G(bRec.wi, bRec.wo, H);
|
||||||
|
|
||||||
/* Calculate the specular reflection component */
|
/* Calculate the specular reflection component */
|
||||||
Float value = F * D * G /
|
Float value = F * D * G /
|
||||||
|
@ -359,9 +366,9 @@ public:
|
||||||
|
|
||||||
if (hasDiffuse) {
|
if (hasDiffuse) {
|
||||||
Spectrum diff = m_diffuseReflectance->eval(bRec.its);
|
Spectrum diff = m_diffuseReflectance->eval(bRec.its);
|
||||||
Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
|
Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), distr.getAlpha());
|
||||||
Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), alpha);
|
Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), distr.getAlpha());
|
||||||
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(alpha);
|
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(distr.getAlpha());
|
||||||
|
|
||||||
if (m_nonlinear)
|
if (m_nonlinear)
|
||||||
diff /= Spectrum(1.0f) - diff * Fdr;
|
diff /= Spectrum(1.0f) - diff * Fdr;
|
||||||
|
@ -386,9 +393,13 @@ public:
|
||||||
(!hasSpecular && !hasDiffuse))
|
(!hasSpecular && !hasDiffuse))
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
/* Calculate the reflection half-vector */
|
/* Calculate the reflection half-vector */
|
||||||
const Vector H = normalize(bRec.wo+bRec.wi);
|
const Vector H = normalize(bRec.wo+bRec.wi);
|
||||||
|
@ -396,7 +407,7 @@ public:
|
||||||
Float probDiffuse, probSpecular;
|
Float probDiffuse, probSpecular;
|
||||||
if (hasSpecular && hasDiffuse) {
|
if (hasSpecular && hasDiffuse) {
|
||||||
/* Find the probability of sampling the specular component */
|
/* Find the probability of sampling the specular component */
|
||||||
probSpecular = 1-m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
|
probSpecular = 1-m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), distr.getAlpha());
|
||||||
|
|
||||||
/* Reallocate samples */
|
/* Reallocate samples */
|
||||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||||
|
@ -413,8 +424,8 @@ public:
|
||||||
/* Jacobian of the half-direction mapping */
|
/* Jacobian of the half-direction mapping */
|
||||||
const Float dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H));
|
const Float dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H));
|
||||||
|
|
||||||
/* Evaluate the microsurface normal distribution */
|
/* Evaluate the microfacet model sampling density function */
|
||||||
const Float prob = m_distribution.pdf(H, alphaT);
|
const Float prob = distr.pdf(bRec.wi, H);
|
||||||
|
|
||||||
result = prob * dwh_dwo * probSpecular;
|
result = prob * dwh_dwo * probSpecular;
|
||||||
}
|
}
|
||||||
|
@ -437,14 +448,18 @@ public:
|
||||||
bool choseSpecular = hasSpecular;
|
bool choseSpecular = hasSpecular;
|
||||||
Point2 sample(_sample);
|
Point2 sample(_sample);
|
||||||
|
|
||||||
/* Evaluate the roughness texture */
|
/* Construct the microfacet distribution matching the
|
||||||
Float alpha = m_alpha->eval(bRec.its).average();
|
roughness values at the current surface position. */
|
||||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
MicrofacetDistribution distr(
|
||||||
|
m_type,
|
||||||
|
m_alpha->eval(bRec.its).average(),
|
||||||
|
m_sampleVisible
|
||||||
|
);
|
||||||
|
|
||||||
Float probSpecular;
|
Float probSpecular;
|
||||||
if (hasSpecular && hasDiffuse) {
|
if (hasSpecular && hasDiffuse) {
|
||||||
/* Find the probability of sampling the specular component */
|
/* Find the probability of sampling the specular component */
|
||||||
probSpecular = 1 - m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
|
probSpecular = 1 - m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), distr.getAlpha());
|
||||||
|
|
||||||
/* Reallocate samples */
|
/* Reallocate samples */
|
||||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||||
|
@ -460,8 +475,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choseSpecular) {
|
if (choseSpecular) {
|
||||||
/* Perfect specular reflection based on the microsurface normal */
|
/* Perfect specular reflection based on the microfacet normal */
|
||||||
Normal m = m_distribution.sample(sample, alphaT);
|
Normal m = distr.sample(bRec.wi, sample);
|
||||||
bRec.wo = reflect(bRec.wi, m);
|
bRec.wo = reflect(bRec.wi, m);
|
||||||
bRec.sampledComponent = 0;
|
bRec.sampledComponent = 0;
|
||||||
bRec.sampledType = EGlossyReflection;
|
bRec.sampledType = EGlossyReflection;
|
||||||
|
@ -518,7 +533,8 @@ public:
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "RoughPlastic[" << endl
|
oss << "RoughPlastic[" << endl
|
||||||
<< " id = \"" << getID() << "\"," << endl
|
<< " id = \"" << getID() << "\"," << endl
|
||||||
<< " distribution = " << m_distribution.toString() << "," << endl
|
<< " distribution = " << MicrofacetDistribution::distributionName(m_type) << "," << endl
|
||||||
|
<< " sampleVisible = " << m_sampleVisible << "," << endl
|
||||||
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
|
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
|
||||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||||
<< " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl
|
<< " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl
|
||||||
|
@ -534,7 +550,7 @@ public:
|
||||||
|
|
||||||
MTS_DECLARE_CLASS()
|
MTS_DECLARE_CLASS()
|
||||||
private:
|
private:
|
||||||
MicrofacetDistribution m_distribution;
|
MicrofacetDistribution::EType m_type;
|
||||||
ref<RoughTransmittance> m_externalRoughTransmittance;
|
ref<RoughTransmittance> m_externalRoughTransmittance;
|
||||||
ref<RoughTransmittance> m_internalRoughTransmittance;
|
ref<RoughTransmittance> m_internalRoughTransmittance;
|
||||||
ref<Texture> m_diffuseReflectance;
|
ref<Texture> m_diffuseReflectance;
|
||||||
|
@ -543,6 +559,7 @@ private:
|
||||||
Float m_eta, m_invEta2;
|
Float m_eta, m_invEta2;
|
||||||
Float m_specularSamplingWeight;
|
Float m_specularSamplingWeight;
|
||||||
bool m_nonlinear;
|
bool m_nonlinear;
|
||||||
|
bool m_sampleVisible;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
/*
|
||||||
|
This file is part of Mitsuba, a physically based rendering system.
|
||||||
|
|
||||||
|
Copyright (c) 2007-2014 by Wenzel Jakob and others.
|
||||||
|
|
||||||
|
Mitsuba is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License Version 3
|
||||||
|
as published by the Free Software Foundation.
|
||||||
|
|
||||||
|
Mitsuba is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <mitsuba/core/plugin.h>
|
||||||
|
#include <mitsuba/core/statistics.h>
|
||||||
|
#include <mitsuba/core/chisquare.h>
|
||||||
|
#include <mitsuba/core/fresolver.h>
|
||||||
|
#include <mitsuba/render/testcase.h>
|
||||||
|
#include <boost/bind.hpp>
|
||||||
|
#include "../bsdfs/microfacet.h"
|
||||||
|
|
||||||
|
/* Statistical significance level of the test. Set to
|
||||||
|
1/4 percent by default -- we want there to be strong
|
||||||
|
evidence of an implementaiton error before failing
|
||||||
|
a test case */
|
||||||
|
#define SIGNIFICANCE_LEVEL 0.0025f
|
||||||
|
|
||||||
|
/* Relative bound on what is still accepted as roundoff
|
||||||
|
error -- be quite tolerant */
|
||||||
|
#if defined(SINGLE_PRECISION)
|
||||||
|
#define ERROR_REQ 1e-2f
|
||||||
|
#else
|
||||||
|
#define ERROR_REQ 1e-5
|
||||||
|
#endif
|
||||||
|
|
||||||
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
class TestChiSquare : public TestCase {
|
||||||
|
public:
|
||||||
|
MTS_BEGIN_TESTCASE()
|
||||||
|
MTS_DECLARE_TEST(test01_Microfacet)
|
||||||
|
MTS_DECLARE_TEST(test02_MicrofacetVisible)
|
||||||
|
MTS_END_TESTCASE()
|
||||||
|
|
||||||
|
class MicrofacetAdapter {
|
||||||
|
public:
|
||||||
|
MicrofacetAdapter(Sampler *sampler, const MicrofacetDistribution &distr, const Vector &wi = Vector(0.0f)) : m_sampler(sampler), m_distr(distr), m_wi(wi) { }
|
||||||
|
|
||||||
|
boost::tuple<Vector, Float, EMeasure> generateSample() {
|
||||||
|
Float pdf;
|
||||||
|
|
||||||
|
if (m_wi.lengthSquared() == 0) {
|
||||||
|
Normal m = m_distr.sampleAll(m_sampler->next2D(), pdf);
|
||||||
|
Float pdf_ref = m_distr.pdfAll(m);
|
||||||
|
|
||||||
|
SAssert(std::isfinite(pdf) && pdf > 0);
|
||||||
|
SAssert(std::isfinite(pdf_ref) && pdf_ref > 0);
|
||||||
|
SAssert(std::isfinite(m.x) && std::isfinite(m.y) && std::isfinite(m.z));
|
||||||
|
SAssert(std::abs(m.length() - 1) < 1e-4f);
|
||||||
|
SAssert(std::abs((pdf-pdf_ref)/pdf_ref) < 1e-4f);
|
||||||
|
return boost::make_tuple(m, 1.0f, ESolidAngle);
|
||||||
|
} else {
|
||||||
|
Normal m = m_distr.sampleVisible(m_wi, m_sampler->next2D());
|
||||||
|
SAssert(std::isfinite(m.x) && std::isfinite(m.y) && std::isfinite(m.z));
|
||||||
|
SAssert(std::abs(m.length() - 1) < 1e-4f);
|
||||||
|
return boost::make_tuple(m, 1.0f, ESolidAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Float pdf(const Vector &d, EMeasure measure) const {
|
||||||
|
if (measure != ESolidAngle)
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
Float pdf = m_wi.lengthSquared() == 0 ? m_distr.pdfAll(d)
|
||||||
|
: m_distr.pdfVisible(m_wi, d);
|
||||||
|
SAssert(std::isfinite(pdf) && pdf >= 0);
|
||||||
|
|
||||||
|
return pdf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
ref<Sampler> m_sampler;
|
||||||
|
MicrofacetDistribution m_distr;
|
||||||
|
Vector m_wi;
|
||||||
|
};
|
||||||
|
|
||||||
|
void test01_Microfacet() {
|
||||||
|
int thetaBins = 20;
|
||||||
|
std::vector<MicrofacetDistribution> distrs;
|
||||||
|
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EBeckmann, 0.5f));
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EBeckmann, 0.5f, 0.3f));
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EGGX, 0.5f));
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EGGX, 0.5f, 0.3f));
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EPhong, 0.5f));
|
||||||
|
distrs.push_back(MicrofacetDistribution(MicrofacetDistribution::EPhong, 0.5f, 0.3f));
|
||||||
|
|
||||||
|
|
||||||
|
ref<Sampler> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
|
||||||
|
createObject(MTS_CLASS(Sampler), Properties("independent")));
|
||||||
|
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, (int) distrs.size());
|
||||||
|
chiSqr->setLogLevel(EDebug);
|
||||||
|
|
||||||
|
for (size_t i=0; i<distrs.size(); ++i) {
|
||||||
|
Log(EInfo, "Testing %s", distrs[i].toString().c_str());
|
||||||
|
// Initialize the tables used by the chi-square test
|
||||||
|
MicrofacetAdapter adapter(sampler, distrs[i]);
|
||||||
|
chiSqr->fill(
|
||||||
|
boost::bind(&MicrofacetAdapter::generateSample, &adapter),
|
||||||
|
boost::bind(&MicrofacetAdapter::pdf, &adapter, _1, _2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
|
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||||
|
if (result == ChiSquare::EReject) {
|
||||||
|
std::string filename = formatString("failure_%i.m", (int) i);
|
||||||
|
chiSqr->dumpTables(filename);
|
||||||
|
failAndContinue(formatString("Uh oh, the chi-square test indicates a potential "
|
||||||
|
"issue. Dumped the contingency tables to '%s' for user analysis",
|
||||||
|
filename.c_str()));
|
||||||
|
} else {
|
||||||
|
//chiSqr->dumpTables(formatString("success_%i.m", (int) i));
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void test02_MicrofacetVisible() {
|
||||||
|
int thetaBins = 10;
|
||||||
|
std::vector<std::pair<MicrofacetDistribution, Vector> > distrs;
|
||||||
|
|
||||||
|
ref<Sampler> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
|
||||||
|
createObject(MTS_CLASS(Sampler), Properties("independent")));
|
||||||
|
for (int i=0; i<10; ++i) {
|
||||||
|
Vector wi = warp::squareToUniformHemisphere(sampler->next2D());
|
||||||
|
distrs.push_back(std::make_pair(MicrofacetDistribution(MicrofacetDistribution::EBeckmann, 0.3f), wi));
|
||||||
|
distrs.push_back(std::make_pair(MicrofacetDistribution(MicrofacetDistribution::EBeckmann, 0.5f, 0.3f), wi));
|
||||||
|
distrs.push_back(std::make_pair(MicrofacetDistribution(MicrofacetDistribution::EGGX, 0.1f), wi));
|
||||||
|
distrs.push_back(std::make_pair(MicrofacetDistribution(MicrofacetDistribution::EGGX, 0.2f, 0.3f), wi));
|
||||||
|
}
|
||||||
|
|
||||||
|
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, (int) distrs.size());
|
||||||
|
chiSqr->setLogLevel(EDebug);
|
||||||
|
|
||||||
|
for (size_t i=0; i<distrs.size(); ++i) {
|
||||||
|
Log(EInfo, "Testing %s (wi=%s)", distrs[i].first.toString().c_str(), distrs[i].second.toString().c_str());
|
||||||
|
// Initialize the tables used by the chi-square test
|
||||||
|
MicrofacetAdapter adapter(sampler, distrs[i].first, distrs[i].second);
|
||||||
|
chiSqr->fill(
|
||||||
|
boost::bind(&MicrofacetAdapter::generateSample, &adapter),
|
||||||
|
boost::bind(&MicrofacetAdapter::pdf, &adapter, _1, _2)
|
||||||
|
);
|
||||||
|
|
||||||
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
|
ChiSquare::ETestResult result = chiSqr->runTest(SIGNIFICANCE_LEVEL);
|
||||||
|
if (result == ChiSquare::EReject) {
|
||||||
|
std::string filename = formatString("failure_%i.m", (int) i);
|
||||||
|
chiSqr->dumpTables(filename);
|
||||||
|
failAndContinue(formatString("Uh oh, the chi-square test indicates a potential "
|
||||||
|
"issue. Dumped the contingency tables to '%s' for user analysis",
|
||||||
|
filename.c_str()));
|
||||||
|
} else {
|
||||||
|
//chiSqr->dumpTables(formatString("success_%i.m", (int) i));
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
MTS_EXPORT_TESTCASE(TestChiSquare, "Chi-square test for microfacet sampling")
|
||||||
|
MTS_NAMESPACE_END
|
Loading…
Reference in New Issue