ported all microfacet models to a new visible normal sampling implementation
parent
bdc43fcbd8
commit
d02d4ebedf
|
@ -78,8 +78,8 @@ def texify(texfile):
|
|||
else:
|
||||
check_call(['pdflatex', texfile])
|
||||
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__)))
|
||||
with open('plugins_generated.tex', 'w') as f:
|
||||
|
|
21
doc/main.bib
21
doc/main.bib
|
@ -563,3 +563,24 @@
|
|||
year = 2008,
|
||||
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}{
|
||||
* Specifies the type of microfacet normal distribution
|
||||
* used to model the surface roughness.
|
||||
* \vspace{-1mm}
|
||||
* \begin{enumerate}[(i)]
|
||||
* \item \code{beckmann}: Physically-based distribution derived from
|
||||
* Gaussian random surfaces. This is the default.
|
||||
* \item \code{ggx}: New distribution proposed by
|
||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
||||
* the long tails observed in measurements of ground surfaces.
|
||||
* Renderings with this distribution may converge slowly.
|
||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
||||
* Due to the underlying microfacet theory,
|
||||
* the use of this distribution here leads to more realistic
|
||||
* behavior than the separately available \pluginref{phong} plugin.
|
||||
* Gaussian random surfaces. This is the default.\vspace{-1.5mm}
|
||||
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||
* was designed to better approximate the long tails observed in measurements
|
||||
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||
* \vspace{-1.5mm}
|
||||
* \item \code{phong}: Classical Phong distribution.
|
||||
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||
* should be preferred, since they provide better importance sampling
|
||||
* and accurate shadowing/masking computations.
|
||||
* \vspace{-4mm}
|
||||
* \end{enumerate}
|
||||
* }
|
||||
* \parameter{alpha}{\Float\Or\Texture}{
|
||||
|
@ -50,6 +53,11 @@ MTS_NAMESPACE_BEGIN
|
|||
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||
* \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
|
||||
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
||||
* \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
|
||||
* 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
|
||||
* Wilkie \cite{Weidlich2007Arbitrarily}.
|
||||
* Wilkie \cite{Weidlich2007Arbitrarily}, though the implementation
|
||||
* works differently.
|
||||
*/
|
||||
class RoughCoating : public BSDF {
|
||||
public:
|
||||
|
@ -127,25 +136,23 @@ public:
|
|||
m_specularReflectance = new ConstantSpectrumTexture(
|
||||
props.getSpectrum("specularReflectance", Spectrum(1.0f)));
|
||||
|
||||
m_distribution = MicrofacetDistribution(
|
||||
props.getString("distribution", "beckmann")
|
||||
);
|
||||
MicrofacetDistribution distr(props);
|
||||
m_type = distr.getType();
|
||||
m_sampleVisible = distr.getSampleVisible();
|
||||
|
||||
if (m_distribution.isAnisotropic())
|
||||
Log(EError, "The 'roughcoating' plugin currently does not support "
|
||||
if (distr.isAnisotropic())
|
||||
Log(EError, "The 'roughplastic' plugin currently does not support "
|
||||
"anisotropic microfacet distributions!");
|
||||
|
||||
m_alpha = new ConstantFloatTexture(
|
||||
props.getFloat("alpha", 0.1f));
|
||||
m_alpha = new ConstantFloatTexture(distr.getAlpha());
|
||||
|
||||
m_specularSamplingWeight = 0.0f;
|
||||
}
|
||||
|
||||
RoughCoating(Stream *stream, InstanceManager *manager)
|
||||
: BSDF(stream, manager) {
|
||||
m_distribution = MicrofacetDistribution(
|
||||
(MicrofacetDistribution::EType) stream->readUInt()
|
||||
);
|
||||
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||
m_sampleVisible = stream->readBool();
|
||||
m_nested = static_cast<BSDF *>(manager->getInstance(stream));
|
||||
m_sigmaA = 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 {
|
||||
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_sigmaA.get());
|
||||
manager->serialize(stream, m_specularReflectance.get());
|
||||
|
@ -200,8 +208,7 @@ public:
|
|||
if (!m_roughTransmittance.get()) {
|
||||
/* Load precomputed data used to compute the rough
|
||||
transmittance through the dielectric interface */
|
||||
m_roughTransmittance = new RoughTransmittance(
|
||||
m_distribution.getType());
|
||||
m_roughTransmittance = new RoughTransmittance(m_type);
|
||||
|
||||
m_roughTransmittance->checkEta(m_eta);
|
||||
m_roughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
||||
|
@ -254,9 +261,13 @@ public:
|
|||
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1)
|
||||
&& measure == ESolidAngle;
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
Spectrum result(0.0f);
|
||||
if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
|
||||
|
@ -264,14 +275,14 @@ public:
|
|||
const Vector H = normalize(bRec.wo+bRec.wi)
|
||||
* math::signum(Frame::cosTheta(bRec.wo));
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float D = m_distribution.eval(H, alphaT);
|
||||
/* Evaluate the microfacet normal distribution */
|
||||
const Float D = distr.eval(H);
|
||||
|
||||
/* Fresnel term */
|
||||
const Float F = fresnelDielectricExt(absDot(bRec.wi, H), m_eta);
|
||||
|
||||
/* 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 */
|
||||
Float value = F * D * G /
|
||||
|
@ -286,8 +297,8 @@ public:
|
|||
bRecInt.wo = refractTo(EInterior, bRec.wo);
|
||||
|
||||
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.wo)), alpha);
|
||||
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), distr.getAlpha()) *
|
||||
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), distr.getAlpha());
|
||||
|
||||
Spectrum sigmaA = m_sigmaA->eval(bRec.its) * m_thickness;
|
||||
if (!sigmaA.isZero())
|
||||
|
@ -319,15 +330,19 @@ public:
|
|||
const Vector H = normalize(bRec.wo+bRec.wi)
|
||||
* math::signum(Frame::cosTheta(bRec.wo));
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
Float probNested, probSpecular;
|
||||
if (hasSpecular && hasNested) {
|
||||
/* Find the probability of sampling the specular component */
|
||||
probSpecular = 1-m_roughTransmittance->eval(
|
||||
std::abs(Frame::cosTheta(bRec.wi)), alpha);
|
||||
std::abs(Frame::cosTheta(bRec.wi)), distr.getAlpha());
|
||||
|
||||
/* Reallocate samples */
|
||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||
|
@ -344,8 +359,8 @@ public:
|
|||
/* Jacobian of the half-direction mapping */
|
||||
const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H));
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float prob = m_distribution.pdf(H, alphaT);
|
||||
/* Evaluate the microfacet model sampling density function */
|
||||
const Float prob = distr.pdf(bRec.wi, H);
|
||||
|
||||
result = prob * dwh_dwo * probSpecular;
|
||||
}
|
||||
|
@ -377,14 +392,19 @@ public:
|
|||
bool choseSpecular = hasSpecular;
|
||||
Point2 sample(_sample);
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
Float probSpecular;
|
||||
if (hasSpecular && hasNested) {
|
||||
/* 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 */
|
||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||
|
@ -400,8 +420,8 @@ public:
|
|||
}
|
||||
|
||||
if (choseSpecular) {
|
||||
/* Perfect specular reflection based on the microsurface normal */
|
||||
Normal m = m_distribution.sample(sample, alphaT);
|
||||
/* Perfect specular reflection based on the microfacet normal */
|
||||
Normal m = distr.sample(bRec.wi, sample);
|
||||
bRec.wo = reflect(bRec.wi, m);
|
||||
bRec.sampledComponent = (int) m_components.size() - 1;
|
||||
bRec.sampledType = EGlossyReflection;
|
||||
|
@ -464,7 +484,8 @@ public:
|
|||
std::ostringstream oss;
|
||||
oss << "RoughCoating[" << 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
|
||||
<< " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
|
||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||
|
@ -480,7 +501,7 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
MicrofacetDistribution m_distribution;
|
||||
MicrofacetDistribution::EType m_type;
|
||||
ref<RoughTransmittance> m_roughTransmittance;
|
||||
ref<Texture> m_sigmaA;
|
||||
ref<Texture> m_alpha;
|
||||
|
@ -489,6 +510,7 @@ private:
|
|||
Float m_eta, m_invEta;
|
||||
Float m_specularSamplingWeight;
|
||||
Float m_thickness;
|
||||
bool m_sampleVisible;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -31,32 +31,30 @@ MTS_NAMESPACE_BEGIN
|
|||
* \parameter{distribution}{\String}{
|
||||
* Specifies the type of microfacet normal distribution
|
||||
* used to model the surface roughness.
|
||||
* \vspace{-1mm}
|
||||
* \begin{enumerate}[(i)]
|
||||
* \item \code{beckmann}: Physically-based distribution derived from
|
||||
* Gaussian random surfaces. This is the default.\vspace{-1mm}
|
||||
* \item \code{ggx}: New distribution proposed by
|
||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
||||
* the long tails observed in measurements of ground surfaces.
|
||||
* Renderings with this distribution may converge slowly.\vspace{-1mm}
|
||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
||||
* Due to the underlying microfacet theory,
|
||||
* the use of this distribution here leads to more realistic
|
||||
* behavior than the separately available \pluginref{phong} plugin.\vspace{-1mm}
|
||||
* \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by
|
||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm}
|
||||
* Gaussian random surfaces. This is the default.\vspace{-1.5mm}
|
||||
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||
* was designed to better approximate the long tails observed in measurements
|
||||
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||
* \vspace{-1.5mm}
|
||||
* \item \code{phong}: Anisotropic Phong distribution by
|
||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.
|
||||
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||
* should be preferred, since they provide better importance sampling
|
||||
* and accurate shadowing/masking computations.
|
||||
* \vspace{-4mm}
|
||||
* \end{enumerate}
|
||||
* }
|
||||
* \parameter{alpha}{\Float\Or\Texture}{
|
||||
* Specifies the roughness of the unresolved surface micro-geometry.
|
||||
* When the Beckmann distribution is used, this parameter is equal to the
|
||||
* \emph{root mean square} (RMS) slope of the microfacets. This
|
||||
* parameter is only valid when \texttt{distribution=beckmann/phong/ggx}.
|
||||
* \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{alpha, alphaU, alphaV}{\Float\Or\Texture}{
|
||||
* Specifies the roughness of the unresolved surface micro-geometry
|
||||
* along the tangent and bitangent directions. When the Beckmann
|
||||
* distribution is used, this parameter is equal to the
|
||||
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||
* \code{alpha} is a convenience parameter to initialize both
|
||||
* \code{alphaU} and \code{alphaV} to the same value. \default{0.1}.
|
||||
* }
|
||||
* \parameter{material}{\String}{Name of a material preset, see
|
||||
* \tblref{conductor-iors}.\!\default{\texttt{Cu} / copper}}
|
||||
|
@ -66,30 +64,36 @@ MTS_NAMESPACE_BEGIN
|
|||
* Real-valued index of refraction of the surrounding dielectric,
|
||||
* 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
|
||||
* 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}}
|
||||
* }
|
||||
* \vspace{4mm}
|
||||
* \vspace{3mm}
|
||||
* This plugin implements a realistic microfacet scattering model for rendering
|
||||
* rough conducting materials, such as metals. It can be interpreted as a fancy
|
||||
* 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{
|
||||
* \rendering{Rough copper (Beckmann, $\alpha=0.1$)}
|
||||
* {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
|
||||
* \lstref{roughconductor-aluminium}}
|
||||
* {bsdf_roughconductor_anisotropic_aluminium.jpg}
|
||||
* }
|
||||
*
|
||||
* Microfacet theory describes rough
|
||||
* surfaces as an arrangement of unresolved and ideally specular facets, whose
|
||||
* normal directions are given by a specially chosen \emph{microfacet distribution}.
|
||||
* By accounting for shadowing and masking effects between these facets, it is
|
||||
* possible to reproduce the important off-specular reflections peaks observed
|
||||
* in real-world measurements of such materials.
|
||||
* Microfacet theory describes rough surfaces as an arrangement of unresolved
|
||||
* and ideally specular facets, whose normal directions are given by a
|
||||
* specially chosen \emph{microfacet distribution}. By accounting for shadowing
|
||||
* and masking effects between these facets, it is possible to reproduce the
|
||||
* important off-specular reflections peaks observed in real-world measurements
|
||||
* of such materials.
|
||||
*
|
||||
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
|
||||
* \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
|
||||
* 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.
|
||||
* To facilitate the tedious task of specifying spectrally-varying index of
|
||||
* refraction information, this plugin can access a set of measured materials
|
||||
|
@ -109,41 +113,48 @@ MTS_NAMESPACE_BEGIN
|
|||
* 100% reflecting mirror.
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* To get an intuition about the effect of the surface roughness
|
||||
* parameter $\alpha$, consider the following approximate classification:
|
||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
||||
* with slight imperfections on an
|
||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||
* To get an intuition about the effect of the surface roughness parameter
|
||||
* $\alpha$, consider the following approximate classification: a value of
|
||||
* $\alpha=0.001-0.01$ corresponds to a material 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
|
||||
* finish). Values significantly above that are probably not too realistic.
|
||||
* \vspace{4mm}
|
||||
* \begin{xml}[caption={A material definition for brushed aluminium}, label=lst:roughconductor-aluminium]
|
||||
* <bsdf type="roughconductor">
|
||||
* <string name="material" value="Al"/>
|
||||
* <string name="distribution" value="as"/>
|
||||
* <string name="distribution" value="phong"/>
|
||||
* <float name="alphaU" value="0.05"/>
|
||||
* <float name="alphaV" value="0.3"/>
|
||||
* </bsdf>
|
||||
* \end{xml}
|
||||
*
|
||||
* \subsubsection*{Technical details}
|
||||
* When rendering with the Ashikhmin-Shirley or Phong microfacet
|
||||
* distributions, a conversion is used to turn the specified
|
||||
* $\alpha$ roughness value into the exponents of these distributions.
|
||||
* This is done in a way, such that the different
|
||||
* distributions all produce a similar appearance for the same value of
|
||||
* $\alpha$.
|
||||
* All microfacet distributions allow the specification of two distinct
|
||||
* roughness values along the tangent and bitangent directions. This can be
|
||||
* used to provide a material with a ``brushed'' appearance. The alignment
|
||||
* of the anisotropy will follow the UV parameterization of the underlying
|
||||
* mesh. This means that such an anisotropic material cannot be applied to
|
||||
* triangle meshes that are missing texture coordinates.
|
||||
*
|
||||
* The Ashikhmin-Shirley microfacet distribution allows the specification
|
||||
* of two distinct roughness values along the tangent and bitangent
|
||||
* directions. This can be used to provide a material with a ``brushed''
|
||||
* appearance. The alignment of the anisotropy will follow the UV
|
||||
* parameterization of the underlying mesh in this case. This also means that
|
||||
* such an anisotropic material cannot be applied to triangle meshes that
|
||||
* are missing texture coordinates.
|
||||
* \label{sec:visiblenormal-sampling}
|
||||
* Since Mitsuba 0.5.1, this plugin uses a new importance sampling technique
|
||||
* contributed by Eric Heitz and Eugene D'Eon, which restricts the sampling
|
||||
* domain to the set of visible (unmasked) microfacet normals. The previous
|
||||
* approach of sampling all normals is still available and can be enabled
|
||||
* by setting \code{sampleVisible} to \code{false}.
|
||||
* 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
|
||||
* 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_k = props.getSpectrum("k", intK) / extEta;
|
||||
|
||||
m_distribution = MicrofacetDistribution(
|
||||
props.getString("distribution", "beckmann")
|
||||
);
|
||||
MicrofacetDistribution distr(props);
|
||||
m_type = distr.getType();
|
||||
m_sampleVisible = distr.getSampleVisible();
|
||||
|
||||
Float alpha = props.getFloat("alpha", 0.1f),
|
||||
alphaU = props.getFloat("alphaU", alpha),
|
||||
alphaV = props.getFloat("alphaV", alpha);
|
||||
|
||||
m_alphaU = new ConstantFloatTexture(alphaU);
|
||||
if (alphaU == alphaV)
|
||||
m_alphaU = new ConstantFloatTexture(distr.getAlphaU());
|
||||
if (distr.getAlphaU() == distr.getAlphaV())
|
||||
m_alphaV = m_alphaU;
|
||||
else
|
||||
m_alphaV = new ConstantFloatTexture(alphaV);
|
||||
m_alphaV = new ConstantFloatTexture(distr.getAlphaV());
|
||||
}
|
||||
|
||||
RoughConductor(Stream *stream, InstanceManager *manager)
|
||||
: BSDF(stream, manager) {
|
||||
m_distribution = MicrofacetDistribution(
|
||||
(MicrofacetDistribution::EType) stream->readUInt()
|
||||
);
|
||||
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||
m_sampleVisible = stream->readBool();
|
||||
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
||||
m_alphaV = static_cast<Texture *>(manager->getInstance(stream));
|
||||
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||
|
@ -207,17 +213,23 @@ public:
|
|||
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() {
|
||||
unsigned int extraFlags = 0;
|
||||
if (m_alphaU != m_alphaV) {
|
||||
if (m_alphaU != m_alphaV)
|
||||
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() ||
|
||||
!m_specularReflectance->isConstant())
|
||||
extraFlags |= ESpatiallyVarying;
|
||||
|
@ -254,27 +266,31 @@ public:
|
|||
/* Calculate the reflection half-vector */
|
||||
Vector H = normalize(bRec.wo+bRec.wi);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_distribution.transformRoughness(
|
||||
m_alphaU->eval(bRec.its).average()),
|
||||
alphaV = m_distribution.transformRoughness(
|
||||
m_alphaV->eval(bRec.its).average());
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float D = m_distribution.eval(H, alphaU, alphaV);
|
||||
/* Evaluate the microfacet normal distribution */
|
||||
const Float D = distr.eval(H);
|
||||
if (D == 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* 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 */
|
||||
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 */
|
||||
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 {
|
||||
|
@ -288,14 +304,20 @@ public:
|
|||
/* Calculate the reflection half-vector */
|
||||
Vector H = normalize(bRec.wo+bRec.wi);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_distribution.transformRoughness(
|
||||
m_alphaU->eval(bRec.its).average()),
|
||||
alphaV = m_distribution.transformRoughness(
|
||||
m_alphaV->eval(bRec.its).average());
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
return m_distribution.pdf(H, alphaU, alphaV)
|
||||
/ (4 * absDot(bRec.wo, H));
|
||||
if (m_sampleVisible)
|
||||
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 {
|
||||
|
@ -304,21 +326,23 @@ public:
|
|||
!(bRec.typeMask & EGlossyReflection)))
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_distribution.transformRoughness(
|
||||
m_alphaU->eval(bRec.its).average()),
|
||||
alphaV = m_distribution.transformRoughness(
|
||||
m_alphaV->eval(bRec.its).average());
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
/* Sample M, the microsurface normal */
|
||||
Float microfacetPDF;
|
||||
const Normal m = m_distribution.sample(sample,
|
||||
alphaU, alphaV, microfacetPDF);
|
||||
/* Sample M, the microfacet normal */
|
||||
Float pdf;
|
||||
Normal m = distr.sample(bRec.wi, sample, pdf);
|
||||
|
||||
if (microfacetPDF == 0)
|
||||
if (pdf == 0)
|
||||
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.eta = 1.0f;
|
||||
bRec.sampledComponent = 0;
|
||||
|
@ -328,18 +352,18 @@ public:
|
|||
if (Frame::cosTheta(bRec.wo) <= 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
const Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||
m_eta, m_k);
|
||||
Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||
m_eta, m_k) * m_specularReflectance->eval(bRec.its);
|
||||
|
||||
Float numerator = m_distribution.eval(m, alphaU, alphaV)
|
||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
||||
* dot(bRec.wi, m);
|
||||
Float weight;
|
||||
if (m_sampleVisible) {
|
||||
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
|
||||
* Frame::cosTheta(bRec.wi);
|
||||
|
||||
return m_specularReflectance->eval(bRec.its) * F
|
||||
* (numerator / denominator);
|
||||
return F * weight;
|
||||
}
|
||||
|
||||
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &sample) const {
|
||||
|
@ -348,20 +372,22 @@ public:
|
|||
!(bRec.typeMask & EGlossyReflection)))
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_distribution.transformRoughness(
|
||||
m_alphaU->eval(bRec.its).average()),
|
||||
alphaV = m_distribution.transformRoughness(
|
||||
m_alphaV->eval(bRec.its).average());
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
/* Sample M, the microsurface normal */
|
||||
const Normal m = m_distribution.sample(sample,
|
||||
alphaU, alphaV, pdf);
|
||||
/* Sample M, the microfacet normal */
|
||||
Normal m = distr.sample(bRec.wi, sample, pdf);
|
||||
|
||||
if (pdf == 0)
|
||||
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.eta = 1.0f;
|
||||
bRec.sampledComponent = 0;
|
||||
|
@ -371,23 +397,23 @@ public:
|
|||
if (Frame::cosTheta(bRec.wo) <= 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
const Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||
m_eta, m_k);
|
||||
Spectrum F = fresnelConductorExact(dot(bRec.wi, m),
|
||||
m_eta, m_k) * m_specularReflectance->eval(bRec.its);
|
||||
|
||||
Float numerator = m_distribution.eval(m, alphaU, alphaV)
|
||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
||||
* dot(bRec.wi, m);
|
||||
|
||||
Float denominator = pdf * Frame::cosTheta(bRec.wi);
|
||||
Float weight;
|
||||
if (m_sampleVisible) {
|
||||
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));
|
||||
}
|
||||
|
||||
/* Jacobian of the half-direction mapping */
|
||||
pdf /= 4.0f * dot(bRec.wo, m);
|
||||
|
||||
return m_specularReflectance->eval(bRec.its) * F
|
||||
* (numerator / denominator);
|
||||
return F * weight;
|
||||
}
|
||||
|
||||
|
||||
void addChild(const std::string &name, ConfigurableObject *child) {
|
||||
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
|
||||
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 {
|
||||
return 0.5f * (m_alphaU->eval(its).average()
|
||||
+ m_alphaV->eval(its).average());
|
||||
|
@ -425,7 +440,8 @@ public:
|
|||
std::ostringstream oss;
|
||||
oss << "RoughConductor[" << 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
|
||||
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||
|
@ -439,9 +455,10 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
MicrofacetDistribution m_distribution;
|
||||
MicrofacetDistribution::EType m_type;
|
||||
ref<Texture> m_specularReflectance;
|
||||
ref<Texture> m_alphaU, m_alphaV;
|
||||
bool m_sampleVisible;
|
||||
Spectrum m_eta, m_k;
|
||||
};
|
||||
|
||||
|
|
|
@ -24,12 +24,6 @@
|
|||
|
||||
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}
|
||||
* \order{5}
|
||||
* \icon{bsdf_roughdielectric}
|
||||
|
@ -37,42 +31,44 @@ MTS_NAMESPACE_BEGIN
|
|||
* \parameter{distribution}{\String}{
|
||||
* Specifies the type of microfacet normal distribution
|
||||
* used to model the surface roughness.
|
||||
* \vspace{-1mm}
|
||||
* \begin{enumerate}[(i)]
|
||||
* \item \code{beckmann}: Physically-based distribution derived from
|
||||
* Gaussian random surfaces. This is the default.
|
||||
* \item \code{ggx}: New distribution proposed by
|
||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
||||
* the long tails observed in measurements of ground surfaces.
|
||||
* Renderings with this distribution may converge slowly.
|
||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
||||
* Due to the underlying microfacet theory,
|
||||
* the use of this distribution here leads to more realistic
|
||||
* behavior than the separately available \pluginref{phong} plugin.
|
||||
* \item \code{as}: Anisotropic Phong-style microfacet distribution proposed by
|
||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.\vspace{-3mm}
|
||||
* Gaussian random surfaces. This is the default.\vspace{-1.5mm}
|
||||
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||
* was designed to better approximate the long tails observed in measurements
|
||||
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||
* \vspace{-1.5mm}
|
||||
* \item \code{phong}: Anisotropic Phong distribution by
|
||||
* Ashikhmin and Shirley \cite{Ashikhmin2005Anisotropic}.
|
||||
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||
* should be preferred, since they provide better importance sampling
|
||||
* and accurate shadowing/masking computations.
|
||||
* \vspace{-4mm}
|
||||
* \end{enumerate}
|
||||
* }
|
||||
* \parameter{alpha}{\Float\Or\Texture}{
|
||||
* Specifies the roughness of the unresolved surface micro-geometry.
|
||||
* When the Beckmann distribution is used, this parameter is equal to the
|
||||
* \emph{root mean square} (RMS) slope of the microfacets. This
|
||||
* parameter is only valid when \texttt{distribution=beckmann/phong/ggx}.
|
||||
* \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{alpha, alphaU, alphaV}{\Float\Or\Texture}{
|
||||
* Specifies the roughness of the unresolved surface micro-geometry
|
||||
* along the tangent and bitangent directions. When the Beckmann
|
||||
* distribution is used, this parameter is equal to the
|
||||
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||
* \code{alpha} is a convenience parameter to initialize both
|
||||
* \code{alphaU} and \code{alphaV} to the same value. \default{0.1}.
|
||||
* }
|
||||
* \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
|
||||
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
||||
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
|
||||
* 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}}
|
||||
* \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
|
||||
* factor that can be used to modulate the specular transmission component. Note
|
||||
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
||||
* \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,\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}}
|
||||
* }\vspace{4mm}
|
||||
*
|
||||
|
@ -98,7 +94,7 @@ MTS_NAMESPACE_BEGIN
|
|||
*
|
||||
* The implementation is based on the paper ``Microfacet Models
|
||||
* 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
|
||||
* interior IOR values can be specified independently, where ``exterior''
|
||||
* 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
|
||||
* Beckmann distribution.
|
||||
*
|
||||
* To get an intuition about the effect of the surface roughness
|
||||
* parameter $\alpha$, consider the following approximate classification:
|
||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
||||
* with slight imperfections on an
|
||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||
* To get an intuition about the effect of the surface roughness parameter
|
||||
* $\alpha$, consider the following approximate classification: a value of
|
||||
* $\alpha=0.001-0.01$ corresponds to a material 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
|
||||
* 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
|
||||
* meaningful and mutually compatible index of refraction changes---see
|
||||
|
@ -127,20 +122,33 @@ MTS_NAMESPACE_BEGIN
|
|||
* converge slowly.
|
||||
*
|
||||
* \subsubsection*{Technical details}
|
||||
* When rendering with the Ashikhmin-Shirley or Phong microfacet
|
||||
* distributions, a conversion is used to turn the specified
|
||||
* $\alpha$ roughness value into the exponents of these distributions.
|
||||
* This is done in a way, such that the different
|
||||
* distributions all produce a similar appearance for the same value of
|
||||
* $\alpha$.
|
||||
* All microfacet distributions allow the specification of two distinct
|
||||
* roughness values along the tangent and bitangent directions. This can be
|
||||
* used to provide a material with a ``brushed'' appearance. The alignment
|
||||
* of the anisotropy will follow the UV parameterization of the underlying
|
||||
* mesh. This means that such an anisotropic material cannot be applied to
|
||||
* triangle meshes that are missing texture coordinates.
|
||||
*
|
||||
* The Ashikhmin-Shirley microfacet distribution allows the specification
|
||||
* of two distinct roughness values along the tangent and bitangent
|
||||
* directions. This can be used to provide a material with a ``brushed''
|
||||
* appearance. The alignment of the anisotropy will follow the UV
|
||||
* parameterization of the underlying mesh in this case. This also means that
|
||||
* such an anisotropic material cannot be applied to triangle meshes that
|
||||
* are missing texture coordinates.\newpage
|
||||
* Since Mitsuba 0.5.1, this plugin uses a new importance sampling technique
|
||||
* contributed by Eric Heitz and Eugene D'Eon, which restricts the sampling
|
||||
* domain to the set of visible (unmasked) microfacet normals. The previous
|
||||
* approach of sampling all normals is still available and can be enabled
|
||||
* by setting \code{sampleVisible} to \code{false}.
|
||||
* 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 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{
|
||||
* \rendering{Ground glass (GGX, $\alpha$=0.304,
|
||||
|
@ -191,26 +199,21 @@ public:
|
|||
m_eta = intIOR / extIOR;
|
||||
m_invEta = 1 / m_eta;
|
||||
|
||||
m_distribution = MicrofacetDistribution(
|
||||
props.getString("distribution", "beckmann")
|
||||
);
|
||||
MicrofacetDistribution distr(props);
|
||||
m_type = distr.getType();
|
||||
m_sampleVisible = distr.getSampleVisible();
|
||||
|
||||
Float alpha = props.getFloat("alpha", 0.1f),
|
||||
alphaU = props.getFloat("alphaU", alpha),
|
||||
alphaV = props.getFloat("alphaV", alpha);
|
||||
|
||||
m_alphaU = new ConstantFloatTexture(alphaU);
|
||||
if (alphaU == alphaV)
|
||||
m_alphaU = new ConstantFloatTexture(distr.getAlphaU());
|
||||
if (distr.getAlphaU() == distr.getAlphaV())
|
||||
m_alphaV = m_alphaU;
|
||||
else
|
||||
m_alphaV = new ConstantFloatTexture(alphaV);
|
||||
m_alphaV = new ConstantFloatTexture(distr.getAlphaV());
|
||||
}
|
||||
|
||||
RoughDielectric(Stream *stream, InstanceManager *manager)
|
||||
: BSDF(stream, manager) {
|
||||
m_distribution = MicrofacetDistribution(
|
||||
(MicrofacetDistribution::EType) stream->readUInt()
|
||||
);
|
||||
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||
m_sampleVisible = stream->readBool();
|
||||
m_alphaU = static_cast<Texture *>(manager->getInstance(stream));
|
||||
m_alphaV = static_cast<Texture *>(manager->getInstance(stream));
|
||||
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||
|
@ -221,17 +224,22 @@ public:
|
|||
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() {
|
||||
unsigned int extraFlags = 0;
|
||||
if (m_alphaU != m_alphaV) {
|
||||
if (m_alphaU != m_alphaV)
|
||||
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())
|
||||
extraFlags |= ESpatiallyVarying;
|
||||
|
@ -293,14 +301,17 @@ public:
|
|||
same hemisphere as the macrosurface normal */
|
||||
H *= math::signum(Frame::cosTheta(H));
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_distribution.transformRoughness(
|
||||
m_alphaU->eval(bRec.its).average()),
|
||||
alphaV = m_distribution.transformRoughness(
|
||||
m_alphaV->eval(bRec.its).average());
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float D = m_distribution.eval(H, alphaU, alphaV);
|
||||
/* Evaluate the microfacet normal distribution */
|
||||
const Float D = distr.eval(H);
|
||||
if (D == 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
|
@ -308,7 +319,7 @@ public:
|
|||
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||
|
||||
/* 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) {
|
||||
/* Calculate the total amount of reflection */
|
||||
|
@ -383,21 +394,24 @@ public:
|
|||
same hemisphere as the macrosurface normal */
|
||||
H *= math::signum(Frame::cosTheta(H));
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
||||
alphaV = m_alphaV->eval(bRec.its).average();
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution sampleDistr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
#if ENLARGE_LOBE_TRICK == 1
|
||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
||||
std::abs(Frame::cosTheta(bRec.wi))));
|
||||
alphaU *= factor; alphaV *= factor;
|
||||
#endif
|
||||
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||
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))));
|
||||
|
||||
alphaU = m_distribution.transformRoughness(alphaU);
|
||||
alphaV = m_distribution.transformRoughness(alphaV);
|
||||
|
||||
/* Evaluate the microsurface normal sampling density */
|
||||
Float prob = m_distribution.pdf(H, alphaU, alphaV);
|
||||
/* Evaluate the microfacet model sampling density function */
|
||||
Float prob = sampleDistr.pdf(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, H);
|
||||
|
||||
if (hasTransmission && hasReflection) {
|
||||
Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||
|
@ -419,44 +433,42 @@ public:
|
|||
if (!hasReflection && !hasTransmission)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
||||
alphaV = m_alphaV->eval(bRec.its).average(),
|
||||
sampleAlphaU = alphaU,
|
||||
sampleAlphaV = alphaV;
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
#if ENLARGE_LOBE_TRICK == 1
|
||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
||||
std::abs(Frame::cosTheta(bRec.wi))));
|
||||
sampleAlphaU *= factor; sampleAlphaV *= factor;
|
||||
#endif
|
||||
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||
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))));
|
||||
|
||||
alphaU = m_distribution.transformRoughness(alphaU);
|
||||
alphaV = m_distribution.transformRoughness(alphaV);
|
||||
sampleAlphaU = m_distribution.transformRoughness(sampleAlphaU);
|
||||
sampleAlphaV = m_distribution.transformRoughness(sampleAlphaV);
|
||||
|
||||
/* Sample M, the microsurface normal */
|
||||
/* Sample M, the microfacet normal */
|
||||
Float microfacetPDF;
|
||||
const Normal m = m_distribution.sample(sample,
|
||||
sampleAlphaU, sampleAlphaV, microfacetPDF);
|
||||
|
||||
const Normal m = sampleDistr.sample(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, sample, microfacetPDF);
|
||||
if (microfacetPDF == 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
Float cosThetaT, numerator = 1.0f;
|
||||
Float cosThetaT;
|
||||
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
||||
Spectrum weight(1.0f);
|
||||
|
||||
if (hasReflection && hasTransmission) {
|
||||
if (bRec.sampler->next1D() > F)
|
||||
sampleReflection = false;
|
||||
} else {
|
||||
numerator = hasReflection ? F : (1-F);
|
||||
weight = Spectrum(hasReflection ? F : (1-F));
|
||||
}
|
||||
|
||||
Spectrum result;
|
||||
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.eta = 1.0f;
|
||||
bRec.sampledComponent = 0;
|
||||
|
@ -466,12 +478,12 @@ public:
|
|||
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
result = m_specularReflectance->eval(bRec.its);
|
||||
weight *= m_specularReflectance->eval(bRec.its);
|
||||
} else {
|
||||
if (cosThetaT == 0)
|
||||
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.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
||||
bRec.sampledComponent = 1;
|
||||
|
@ -486,17 +498,16 @@ public:
|
|||
Float factor = (bRec.mode == ERadiance)
|
||||
? (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)
|
||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
||||
* dot(bRec.wi, m);
|
||||
if (m_sampleVisible)
|
||||
weight *= distr.smithG1(bRec.wo, 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
|
||||
* Frame::cosTheta(bRec.wi);
|
||||
|
||||
return result * std::abs(numerator / denominator);
|
||||
return weight;
|
||||
}
|
||||
|
||||
Spectrum sample(BSDFSamplingRecord &bRec, Float &pdf, const Point2 &_sample) const {
|
||||
|
@ -511,35 +522,33 @@ public:
|
|||
if (!hasReflection && !hasTransmission)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* Evaluate the roughness */
|
||||
Float alphaU = m_alphaU->eval(bRec.its).average(),
|
||||
alphaV = m_alphaV->eval(bRec.its).average(),
|
||||
sampleAlphaU = alphaU,
|
||||
sampleAlphaV = alphaV;
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alphaU->eval(bRec.its).average(),
|
||||
m_alphaV->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
#if ENLARGE_LOBE_TRICK == 1
|
||||
Float factor = (1.2f - 0.2f * std::sqrt(
|
||||
std::abs(Frame::cosTheta(bRec.wi))));
|
||||
sampleAlphaU *= factor; sampleAlphaV *= factor;
|
||||
#endif
|
||||
/* Trick by Walter et al.: slightly scale the roughness values to
|
||||
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))));
|
||||
|
||||
alphaU = m_distribution.transformRoughness(alphaU);
|
||||
alphaV = m_distribution.transformRoughness(alphaV);
|
||||
sampleAlphaU = m_distribution.transformRoughness(sampleAlphaU);
|
||||
sampleAlphaV = m_distribution.transformRoughness(sampleAlphaV);
|
||||
|
||||
/* Sample M, the microsurface normal */
|
||||
/* Sample M, the microfacet normal */
|
||||
Float microfacetPDF;
|
||||
const Normal m = m_distribution.sample(sample,
|
||||
sampleAlphaU, sampleAlphaV, microfacetPDF);
|
||||
|
||||
const Normal m = sampleDistr.sample(math::signum(Frame::cosTheta(bRec.wi)) * bRec.wi, sample, microfacetPDF);
|
||||
if (microfacetPDF == 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
pdf = microfacetPDF;
|
||||
|
||||
Float cosThetaT, numerator = 1.0f;
|
||||
Float cosThetaT;
|
||||
Float F = fresnelDielectricExt(dot(bRec.wi, m), cosThetaT, m_eta);
|
||||
Spectrum weight(1.0f);
|
||||
|
||||
if (hasReflection && hasTransmission) {
|
||||
if (bRec.sampler->next1D() > F) {
|
||||
|
@ -549,14 +558,12 @@ public:
|
|||
pdf *= F;
|
||||
}
|
||||
} else {
|
||||
numerator = hasReflection ? F : (1-F);
|
||||
weight *= hasReflection ? F : (1-F);
|
||||
}
|
||||
|
||||
Spectrum result;
|
||||
Float dwh_dwo;
|
||||
|
||||
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.eta = 1.0f;
|
||||
bRec.sampledComponent = 0;
|
||||
|
@ -566,7 +573,7 @@ public:
|
|||
if (Frame::cosTheta(bRec.wi) * Frame::cosTheta(bRec.wo) <= 0)
|
||||
return Spectrum(0.0f);
|
||||
|
||||
result = m_specularReflectance->eval(bRec.its);
|
||||
weight *= m_specularReflectance->eval(bRec.its);
|
||||
|
||||
/* Jacobian of the half-direction mapping */
|
||||
dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, m));
|
||||
|
@ -574,7 +581,7 @@ public:
|
|||
if (cosThetaT == 0)
|
||||
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.eta = cosThetaT < 0 ? m_eta : m_invEta;
|
||||
bRec.sampledComponent = 1;
|
||||
|
@ -589,22 +596,22 @@ public:
|
|||
Float factor = (bRec.mode == ERadiance)
|
||||
? (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 */
|
||||
Float sqrtDenom = dot(bRec.wi, m) + bRec.eta * dot(bRec.wo, m);
|
||||
dwh_dwo = (bRec.eta*bRec.eta * dot(bRec.wo, m)) / (sqrtDenom*sqrtDenom);
|
||||
}
|
||||
|
||||
numerator *= m_distribution.eval(m, alphaU, alphaV)
|
||||
* m_distribution.G(bRec.wi, bRec.wo, m, alphaU, alphaV)
|
||||
* dot(bRec.wi, m);
|
||||
|
||||
Float denominator = microfacetPDF * Frame::cosTheta(bRec.wi);
|
||||
if (m_sampleVisible)
|
||||
weight *= distr.smithG1(bRec.wo, m);
|
||||
else
|
||||
weight *= std::abs(distr.eval(m) * distr.G(bRec.wi, bRec.wo, m)
|
||||
* dot(bRec.wi, m) / (microfacetPDF * Frame::cosTheta(bRec.wi)));
|
||||
|
||||
pdf *= std::abs(dwh_dwo);
|
||||
|
||||
return result * std::abs(numerator / denominator);
|
||||
return weight;
|
||||
}
|
||||
|
||||
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 {
|
||||
return m_eta;
|
||||
}
|
||||
|
@ -650,7 +646,8 @@ public:
|
|||
std::ostringstream oss;
|
||||
oss << "RoughDielectric[" << endl
|
||||
<< " id = \"" << getID() << "\"," << endl
|
||||
<< " distribution = " << m_distribution.toString() << "," << endl
|
||||
<< " distribution = " << MicrofacetDistribution::distributionName(m_type) << "," << endl
|
||||
<< " sampleVisible = " << m_sampleVisible << "," << endl
|
||||
<< " eta = " << m_eta << "," << endl
|
||||
<< " alphaU = " << indent(m_alphaU->toString()) << "," << endl
|
||||
<< " alphaV = " << indent(m_alphaV->toString()) << "," << endl
|
||||
|
@ -664,11 +661,12 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
MicrofacetDistribution m_distribution;
|
||||
MicrofacetDistribution::EType m_type;
|
||||
ref<Texture> m_specularTransmittance;
|
||||
ref<Texture> m_specularReflectance;
|
||||
ref<Texture> m_alphaU, m_alphaV;
|
||||
Float m_eta, m_invEta;
|
||||
bool m_sampleVisible;
|
||||
};
|
||||
|
||||
/* Fake glass shader -- it is really hopeless to visualize
|
||||
|
|
|
@ -32,18 +32,20 @@ MTS_NAMESPACE_BEGIN
|
|||
* \parameter{distribution}{\String}{
|
||||
* Specifies the type of microfacet normal distribution
|
||||
* used to model the surface roughness.
|
||||
* \vspace{-1mm}
|
||||
* \begin{enumerate}[(i)]
|
||||
* \item \code{beckmann}: Physically-based distribution derived from
|
||||
* Gaussian random surfaces. This is the default.
|
||||
* \item \code{ggx}: New distribution proposed by
|
||||
* Walter et al. \cite{Walter07Microfacet}, which is meant to better handle
|
||||
* the long tails observed in measurements of ground surfaces.
|
||||
* Renderings with this distribution may converge slowly.
|
||||
* \item \code{phong}: Classical $\cos^p\theta$ distribution.
|
||||
* Due to the underlying microfacet theory,
|
||||
* the use of this distribution here leads to more realistic
|
||||
* behavior than the separately available \pluginref{phong} plugin.
|
||||
* \vspace{-3mm}
|
||||
* Gaussian random surfaces. This is the default.\vspace{-1.5mm}
|
||||
* \item \code{ggx}: The GGX \cite{Walter07Microfacet} distribution (also known as
|
||||
* Trowbridge-Reitz \cite{Trowbridge19975Average} distribution)
|
||||
* was designed to better approximate the long tails observed in measurements
|
||||
* of ground surfaces, which are not modeled by the Beckmann distribution.
|
||||
* \vspace{-1.5mm}
|
||||
* \item \code{phong}: Classical Phong distribution.
|
||||
* In most cases, the \code{ggx} and \code{beckmann} distributions
|
||||
* should be preferred, since they provide better importance sampling
|
||||
* and accurate shadowing/masking computations.
|
||||
* \vspace{-4mm}
|
||||
* \end{enumerate}
|
||||
* }
|
||||
* \parameter{alpha}{\Float\Or\Texture}{
|
||||
|
@ -52,10 +54,16 @@ MTS_NAMESPACE_BEGIN
|
|||
* \emph{root mean square} (RMS) slope of the microfacets.
|
||||
* \default{0.1}.
|
||||
* }
|
||||
*
|
||||
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
||||
* numerically or using a known material name. \default{\texttt{polypropylene} / 1.49}}
|
||||
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
||||
* 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
|
||||
* 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}}
|
||||
|
@ -75,12 +83,12 @@ MTS_NAMESPACE_BEGIN
|
|||
* preferred over heuristic models like \pluginref{phong} and \pluginref{ward}
|
||||
* when possible.
|
||||
*
|
||||
* Microfacet theory describes rough surfaces as an arrangement of unresolved and
|
||||
* ideally specular facets, whose normal directions are given by a specially
|
||||
* chosen \emph{microfacet distribution}.
|
||||
* By accounting for shadowing and masking effects between these facets, it is
|
||||
* possible to reproduce the important off-specular reflections peaks observed
|
||||
* in real-world measurements of such materials.
|
||||
* Microfacet theory describes rough surfaces as an arrangement of
|
||||
* unresolved and ideally specular facets, whose normal directions are given by
|
||||
* a specially chosen \emph{microfacet distribution}. By accounting for shadowing
|
||||
* and masking effects between these facets, it is possible to reproduce the important
|
||||
* off-specular reflections peaks observed in real-world measurements of such
|
||||
* materials.
|
||||
*
|
||||
* \renderings{
|
||||
* \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
|
||||
* on \pluginpage{plastic}.
|
||||
*
|
||||
*
|
||||
* To get an intuition about the effect of the surface roughness
|
||||
* parameter $\alpha$, consider the following approximate classification:
|
||||
* a value of $\alpha=0.001-0.01$ corresponds to a material
|
||||
* with slight imperfections on an
|
||||
* otherwise smooth surface finish, $\alpha=0.1$ is relatively rough,
|
||||
* To get an intuition about the effect of the surface roughness parameter
|
||||
* $\alpha$, consider the following approximate classification: a value of
|
||||
* $\alpha=0.001-0.01$ corresponds to a material 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
|
||||
* 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.
|
||||
* }
|
||||
* }
|
||||
* \newpage
|
||||
* \renderings{
|
||||
* \rendering{Wood material with smooth horizontal stripes}{bsdf_roughplastic_roughtex1}
|
||||
* \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,
|
||||
* the relevant parts are extracted using tricubic interpolation.
|
||||
*
|
||||
* When rendering with the Phong microfacet distributions, a conversion
|
||||
* is used to turn the specified $\alpha$ roughness value into the Phong
|
||||
* exponent. This is done in a way, such that the different distributions
|
||||
* all produce a similar appearance for the same value of $\alpha$.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
class RoughPlastic : public BSDF {
|
||||
public:
|
||||
|
@ -207,27 +212,25 @@ public:
|
|||
|
||||
m_eta = intIOR / extIOR;
|
||||
|
||||
m_distribution = MicrofacetDistribution(
|
||||
props.getString("distribution", "beckmann")
|
||||
);
|
||||
m_nonlinear = props.getBoolean("nonlinear", false);
|
||||
|
||||
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 "
|
||||
"anisotropic microfacet distributions!");
|
||||
|
||||
m_nonlinear = props.getBoolean("nonlinear", false);
|
||||
|
||||
m_alpha = new ConstantFloatTexture(
|
||||
props.getFloat("alpha", 0.1f));
|
||||
m_alpha = new ConstantFloatTexture(distr.getAlpha());
|
||||
|
||||
m_specularSamplingWeight = 0.0f;
|
||||
}
|
||||
|
||||
RoughPlastic(Stream *stream, InstanceManager *manager)
|
||||
: BSDF(stream, manager) {
|
||||
m_distribution = MicrofacetDistribution(
|
||||
(MicrofacetDistribution::EType) stream->readUInt()
|
||||
);
|
||||
m_type = (MicrofacetDistribution::EType) stream->readUInt();
|
||||
m_sampleVisible = stream->readBool();
|
||||
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
|
||||
m_diffuseReflectance = 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 {
|
||||
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_diffuseReflectance.get());
|
||||
manager->serialize(stream, m_alpha.get());
|
||||
|
@ -277,8 +281,7 @@ public:
|
|||
if (!m_externalRoughTransmittance.get()) {
|
||||
/* Load precomputed data used to compute the rough
|
||||
transmittance through the dielectric interface */
|
||||
m_externalRoughTransmittance = new RoughTransmittance(
|
||||
m_distribution.getType());
|
||||
m_externalRoughTransmittance = new RoughTransmittance(m_type);
|
||||
|
||||
m_externalRoughTransmittance->checkEta(m_eta);
|
||||
m_externalRoughTransmittance->checkAlpha(m_alpha->getMinimum().average());
|
||||
|
@ -332,23 +335,27 @@ public:
|
|||
(!hasSpecular && !hasDiffuse))
|
||||
return Spectrum(0.0f);
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
Spectrum result(0.0f);
|
||||
if (hasSpecular) {
|
||||
/* Calculate the reflection half-vector */
|
||||
const Vector H = normalize(bRec.wo+bRec.wi);
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float D = m_distribution.eval(H, alphaT);
|
||||
/* Evaluate the microfacet normal distribution */
|
||||
const Float D = distr.eval(H);
|
||||
|
||||
/* Fresnel term */
|
||||
const Float F = fresnelDielectricExt(dot(bRec.wi, H), m_eta);
|
||||
|
||||
/* 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 */
|
||||
Float value = F * D * G /
|
||||
|
@ -359,9 +366,9 @@ public:
|
|||
|
||||
if (hasDiffuse) {
|
||||
Spectrum diff = m_diffuseReflectance->eval(bRec.its);
|
||||
Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
|
||||
Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), alpha);
|
||||
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(alpha);
|
||||
Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), distr.getAlpha());
|
||||
Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), distr.getAlpha());
|
||||
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(distr.getAlpha());
|
||||
|
||||
if (m_nonlinear)
|
||||
diff /= Spectrum(1.0f) - diff * Fdr;
|
||||
|
@ -386,9 +393,13 @@ public:
|
|||
(!hasSpecular && !hasDiffuse))
|
||||
return 0.0f;
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
/* Calculate the reflection half-vector */
|
||||
const Vector H = normalize(bRec.wo+bRec.wi);
|
||||
|
@ -396,7 +407,7 @@ public:
|
|||
Float probDiffuse, probSpecular;
|
||||
if (hasSpecular && hasDiffuse) {
|
||||
/* 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 */
|
||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||
|
@ -413,8 +424,8 @@ public:
|
|||
/* Jacobian of the half-direction mapping */
|
||||
const Float dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H));
|
||||
|
||||
/* Evaluate the microsurface normal distribution */
|
||||
const Float prob = m_distribution.pdf(H, alphaT);
|
||||
/* Evaluate the microfacet model sampling density function */
|
||||
const Float prob = distr.pdf(bRec.wi, H);
|
||||
|
||||
result = prob * dwh_dwo * probSpecular;
|
||||
}
|
||||
|
@ -437,14 +448,18 @@ public:
|
|||
bool choseSpecular = hasSpecular;
|
||||
Point2 sample(_sample);
|
||||
|
||||
/* Evaluate the roughness texture */
|
||||
Float alpha = m_alpha->eval(bRec.its).average();
|
||||
Float alphaT = m_distribution.transformRoughness(alpha);
|
||||
/* Construct the microfacet distribution matching the
|
||||
roughness values at the current surface position. */
|
||||
MicrofacetDistribution distr(
|
||||
m_type,
|
||||
m_alpha->eval(bRec.its).average(),
|
||||
m_sampleVisible
|
||||
);
|
||||
|
||||
Float probSpecular;
|
||||
if (hasSpecular && hasDiffuse) {
|
||||
/* 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 */
|
||||
probSpecular = (probSpecular*m_specularSamplingWeight) /
|
||||
|
@ -460,8 +475,8 @@ public:
|
|||
}
|
||||
|
||||
if (choseSpecular) {
|
||||
/* Perfect specular reflection based on the microsurface normal */
|
||||
Normal m = m_distribution.sample(sample, alphaT);
|
||||
/* Perfect specular reflection based on the microfacet normal */
|
||||
Normal m = distr.sample(bRec.wi, sample);
|
||||
bRec.wo = reflect(bRec.wi, m);
|
||||
bRec.sampledComponent = 0;
|
||||
bRec.sampledType = EGlossyReflection;
|
||||
|
@ -518,7 +533,8 @@ public:
|
|||
std::ostringstream oss;
|
||||
oss << "RoughPlastic[" << 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
|
||||
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
|
||||
<< " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl
|
||||
|
@ -534,7 +550,7 @@ public:
|
|||
|
||||
MTS_DECLARE_CLASS()
|
||||
private:
|
||||
MicrofacetDistribution m_distribution;
|
||||
MicrofacetDistribution::EType m_type;
|
||||
ref<RoughTransmittance> m_externalRoughTransmittance;
|
||||
ref<RoughTransmittance> m_internalRoughTransmittance;
|
||||
ref<Texture> m_diffuseReflectance;
|
||||
|
@ -543,6 +559,7 @@ private:
|
|||
Float m_eta, m_invEta2;
|
||||
Float m_specularSamplingWeight;
|
||||
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