merged the microfacet changes into the main branch

metadata
Wenzel Jakob 2011-09-17 22:44:54 -04:00
commit 1538b2afed
46 changed files with 7783 additions and 696 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 165 KiB

BIN
doc/images/bsdf_sssbrdf.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 182 KiB

File diff suppressed because one or more lines are too long

View File

@ -1,14 +1,20 @@
% Cite a figure/listing % Cite a figure/listing
\renewcommand{\lstlistingname}{Listing} \renewcommand{\lstlistingname}{Listing}
\newcommand{\figref}[1]{\mbox{Figure~\ref{fig:#1}}} \newcommand{\figref}[1]{\mbox{\hyperref[fig:#1]{Figure~\ref*{fig:#1}}}}
\newcommand{\secref}[1]{\mbox{Section~\ref{sec:#1}}} \newcommand{\figpage}[1]{\hyperref[fig:#1]{page \pageref*{fig:#1}}}
\newcommand{\lstref}[1]{\mbox{Listing~\ref{lst:#1}}} \newcommand{\subfigref}[2]{\mbox{\hyperref[fig:#1]{Figure~\ref*{fig:#1}#2}}}
\newcommand{\tblref}[1]{\mbox{Table~\ref{tbl:#1}}} \newcommand{\secref}[1]{\mbox{\hyperref[sec:#1]{Section~\ref*{sec:#1}}}}
\newcommand{\secpage}[1]{\hyperref[sec:#1]{page \pageref*{sec:#1}}}
\newcommand{\lstref}[1]{\mbox{\hyperref[lst:#1]{Listing~\ref{lst:#1}}}}
\newcommand{\lstpage}[1]{\hyperref[lst:#1]{page \pageref*{lst:#1}}}
\newcommand{\tblref}[1]{\mbox{\hyperref[tbl:#1]{Table~\ref{tbl:#1}}}}
\newcommand{\tblpage}[1]{\hyperref[tbl:#1]{page \pageref*{tbl:#1}}}
\newcommand{\code}[1]{\texttt{#1}} \newcommand{\code}[1]{\texttt{#1}}
% Macros that are used in the plugin documentation % Macros that are used in the plugin documentation
\newcommand{\plugin}[2]{\newpage\subsubsection{#2 (\texttt{#1})}\label{plg:#1}} \newcommand{\plugin}[2]{\newpage\subsubsection{#2 (\texttt{#1})}\label{plg:#1}}
\newcommand{\pluginref}[1]{\texttt{\hyperref[plg:#1]{#1}}} \newcommand{\pluginref}[1]{\texttt{\hyperref[plg:#1]{#1}}}
\newcommand{\pluginpage}[1]{\hyperref[plg:#1]{page \pageref*{plg:#1}}}
\newcommand{\order}[1]{} % Ignore \newcommand{\order}[1]{} % Ignore
\newcommand{\Transform}{\texttt{transform}} \newcommand{\Transform}{\texttt{transform}}

View File

@ -1,91 +0,0 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 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/>.
*/
#if !defined(__SPLINE_H)
#define __SPLINE_H
#include <mitsuba/core/serialization.h>
MTS_NAMESPACE_BEGIN
/**
* \brief Simple natural cubic spline interpolation
*
* \ingroup libcore
*/
class MTS_EXPORT_CORE CubicSpline : public SerializableObject {
public:
/**
* \brief Initialize a cubic spline with the given set of
* points (must be in ascending order, with no duplicates)
*/
inline CubicSpline(std::vector<Float> &x, std::vector<Float> &y)
: m_x(x), m_y(y) { }
/**
* \brief Initialize an empty cubic spline and reserve memory
* for the specified number of points
*/
inline CubicSpline(size_t nPoints) {
m_x.reserve(nPoints);
m_y.reserve(nPoints);
m_deriv.reserve(nPoints);
}
/**
* \brief Unserialize a cubic spline from a
* binary data stream
*/
CubicSpline(Stream *stream, InstanceManager *manager);
/**
* \brief Append a point at the end -- must be called with
* increasing values of \a x
*/
inline void append(Float x, Float y) {
m_x.push_back(x);
m_y.push_back(y);
}
/// Return the number of control points
inline size_t getSize() const { return m_x.size(); }
/// Clear the internal representation
void clear();
/// Compute the derivatives -- must be called prior to \ref eval
void build();
/// Evaluate the spline at \c x
Float eval(Float x) const;
/// Serialize this spline to a binary data stream
void serialize(Stream *stream, InstanceManager *manager) const;
/// Return a human-readable representation
std::string toString() const;
MTS_DECLARE_CLASS()
private:
std::vector<Float> m_x, m_y;
std::vector<Float> m_deriv; /// 2nd derivatives
};
MTS_NAMESPACE_END
#endif /* __SPLINE_H */

View File

@ -56,6 +56,10 @@ public:
return m_value; return m_value;
} }
inline Spectrum getMinimum() const {
return m_value;
}
inline bool isConstant() const { inline bool isConstant() const {
return true; return true;
} }
@ -105,6 +109,10 @@ public:
return Spectrum(m_value); return Spectrum(m_value);
} }
inline Spectrum getMinimum() const {
return Spectrum(m_value);
}
inline bool isConstant() const { inline bool isConstant() const {
return true; return true;
} }
@ -150,8 +158,13 @@ public:
} }
inline Spectrum getMaximum() const { inline Spectrum getMaximum() const {
SLog(EError, "SpectrumAdditionTexture::getMaximum() -- information unavailable!"); // Return a conservative estimate
return Spectrum(0.0f); return m_a->getMaximum() + m_b->getMaximum();
}
inline Spectrum getMinimum() const {
// Return a conservative estimate
return m_a->getMinimum() + m_b->getMinimum();
} }
inline bool isConstant() const { inline bool isConstant() const {
@ -202,8 +215,13 @@ public:
} }
inline Spectrum getMaximum() const { inline Spectrum getMaximum() const {
SLog(EError, "SpectrumSubtractionTexture::getMaximum() -- information unavailable!"); // Return a conservative estimate
return Spectrum(0.0f); return m_a->getMaximum() - m_b->getMinimum();
}
inline Spectrum getMinimum() const {
// Return a conservative estimate
return m_a->getMinimum() - m_b->getMaximum();
} }
inline bool isConstant() const { inline bool isConstant() const {
@ -255,9 +273,15 @@ public:
} }
inline Spectrum getMaximum() const { inline Spectrum getMaximum() const {
// Return a conservative estimate
return m_a->getMaximum() * m_b->getMaximum(); return m_a->getMaximum() * m_b->getMaximum();
} }
inline Spectrum getMinimum() const {
// Return a conservative estimate
return m_a->getMinimum() * m_b->getMinimum();
}
inline bool isConstant() const { inline bool isConstant() const {
return m_a->isConstant() && m_b->isConstant(); return m_a->isConstant() && m_b->isConstant();
} }

View File

@ -88,7 +88,13 @@ public:
} }
/// Get the component-wise maximum at the zero level /// Get the component-wise maximum at the zero level
Spectrum getMaximum() const; inline const Spectrum &getMinimum() const { return m_minimum; }
/// Get the component-wise minimum
inline const Spectrum &getMaximum() const { return m_maximum; }
/// Get the component-wise average
inline const Spectrum &getAverage() const { return m_average; }
/// Return a bitmap representation of the full-resolution image /// Return a bitmap representation of the full-resolution image
Bitmap *getBitmap() const; Bitmap *getBitmap() const;
@ -130,6 +136,9 @@ private:
EWrapMode m_wrapMode; EWrapMode m_wrapMode;
Float *m_weightLut; Float *m_weightLut;
Float m_maxAnisotropy; Float m_maxAnisotropy;
Spectrum m_minimum;
Spectrum m_maximum;
Spectrum m_average;
}; };
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -38,6 +38,9 @@ public:
/// Return the component-wise average value of the texture over its domain /// Return the component-wise average value of the texture over its domain
virtual Spectrum getAverage() const = 0; virtual Spectrum getAverage() const = 0;
/// Return the component-wise minimum of the texture over its domain
virtual Spectrum getMinimum() const = 0;
/// Return the component-wise maximum of the texture over its domain /// Return the component-wise maximum of the texture over its domain
virtual Spectrum getMaximum() const = 0; virtual Spectrum getMaximum() const = 0;

View File

@ -24,7 +24,7 @@ plugins += env.SharedLibrary('phong', ['phong.cpp'])
plugins += env.SharedLibrary('difftrans', ['difftrans.cpp']) plugins += env.SharedLibrary('difftrans', ['difftrans.cpp'])
plugins += env.SharedLibrary('hk', ['hk.cpp']) plugins += env.SharedLibrary('hk', ['hk.cpp'])
plugins += env.SharedLibrary('dipolebrdf', ['dipolebrdf.cpp']) plugins += env.SharedLibrary('dipolebrdf', ['dipolebrdf.cpp'])
plugins += env.SharedLibrary('sssbrdf', ['sssbrdf.cpp']) plugins += env.SharedLibrary('rmbrdf', ['rmbrdf.cpp'])
# The Irawan-Marschner plugin uses a Boost::Spirit parser, which makes it # The Irawan-Marschner plugin uses a Boost::Spirit parser, which makes it
# pretty heavy stuff to compile. Go easy on the compiler flags: # pretty heavy stuff to compile. Go easy on the compiler flags:

View File

@ -181,7 +181,7 @@ public:
Log(EError, "Only a single nested BRDF can be added!"); Log(EError, "Only a single nested BRDF can be added!");
m_nested = static_cast<BSDF *>(child); m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") {
m_sigmaA = static_cast<Texture *>(m_sigmaA); m_sigmaA = static_cast<Texture *>(child);
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }

View File

@ -33,9 +33,9 @@ MTS_NAMESPACE_BEGIN
* \parameter{k}{\Spectrum}{Imaginary part of the material's index of * \parameter{k}{\Spectrum}{Imaginary part of the material's index of
* refraction, also known as absorption coefficient. * refraction, also known as absorption coefficient.
* \default{based on the value of \texttt{material}}} * \default{based on the value of \texttt{material}}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{ * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* Optional factor used to modulate the reflectance component * factor that can be used to modulate the specular reflection component. Note
* \default{1.0}} * that for physical realism, this parameter should never be touched. \default{1.0}}
* } * }
* \renderings{ * \renderings{
* \rendering{Measured copper material (the default), rendered using 30 * \rendering{Measured copper material (the default), rendered using 30
@ -76,7 +76,7 @@ MTS_NAMESPACE_BEGIN
* *
* 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
* in RGB mode, the computations will be much more approximate in this case. * in RGB mode, the computations will be more approximate in nature.
* Also note that this material is one-sided---that is, observed from the * Also note that this material is one-sided---that is, observed from the
* back side, it will be completely black. If this is undesirable, * back side, it will be completely black. If this is undesirable,
* consider using the \pluginref{twosided} BRDF adapter plugin.\vspace{4mm} * consider using the \pluginref{twosided} BRDF adapter plugin.\vspace{4mm}
@ -369,7 +369,7 @@ public:
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " return " << evalName << "_R0 * 0.31831 * cosTheta(wo);"<< endl << " return " << evalName << "_R0 * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl; << "}" << endl;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()

View File

@ -31,9 +31,11 @@ MTS_NAMESPACE_BEGIN
* \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{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the reflectance component\default{1.0}} * 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 * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the transmittance component\default{1.0}} * factor that can be used to modulate the specular transmission component. Note
* that for physical realism, this parameter should never be touched. \default{1.0}}
* } * }
* *
* \renderings{ * \renderings{
@ -72,8 +74,7 @@ MTS_NAMESPACE_BEGIN
* In many cases, we will want to additionally describe the \emph{medium} within a * In many cases, we will want to additionally describe the \emph{medium} within a
* dielectric material. This requires the use of a rendering technique that is * dielectric material. This requires the use of a rendering technique that is
* aware of media (e.g. the volumetric path tracer). An example of how one might * aware of media (e.g. the volumetric path tracer). An example of how one might
* describe a slightly absorbing piece of glass is given on the next page: * describe a slightly absorbing piece of glass is shown below:
* \newpage
* \begin{xml}[caption=A glass material with absorption (based on the * \begin{xml}[caption=A glass material with absorption (based on the
* Beer-Lambert law). This material can only be used by an integrator * Beer-Lambert law). This material can only be used by an integrator
* that is aware of participating media., label=lst:dielectric-glass] * that is aware of participating media., label=lst:dielectric-glass]

View File

@ -171,7 +171,7 @@ public:
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (cosTheta(wi) * cosTheta(wo) >= 0.0)" << endl << " if (cosTheta(wi) * cosTheta(wo) >= 0.0)" << endl
<< " return vec3(0.0);" << endl << " return vec3(0.0);" << endl
<< " return " << depNames[0] << "(uv) * 0.31831 * abs(cosTheta(wo));" << endl << " return " << depNames[0] << "(uv) * inv_pi * abs(cosTheta(wo));" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl

View File

@ -203,7 +203,7 @@ public:
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 0.0)" << endl << " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 0.0)" << endl
<< " return vec3(0.0);" << endl << " return vec3(0.0);" << endl
<< " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo);" << endl << " return " << depNames[0] << "(uv) * inv_pi * cosTheta(wo);" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl

View File

@ -291,7 +291,7 @@ public:
<< " reducedAlbedo[i] = reducedSigmaT[i] > 0.0 ? reducedSigmaS[i]/reducedSigmaT[i] : 0.0;" << endl << " reducedAlbedo[i] = reducedSigmaT[i] > 0.0 ? reducedSigmaS[i]/reducedSigmaT[i] : 0.0;" << endl
<< " vec3 rootExp = sqrt((1.0 - reducedAlbedo) * 3.0);" << endl << " vec3 rootExp = sqrt((1.0 - reducedAlbedo) * 3.0);" << endl
<< " return (reducedAlbedo * 0.5) * (1+exp(-rootExp*(4.0/3.0 * " << endl << " return (reducedAlbedo * 0.5) * (1+exp(-rootExp*(4.0/3.0 * " << endl
<< " " << evalName << "_A" << "))) * exp(-rootExp) * 0.31831 * cosThetaO;" << endl << " " << evalName << "_A" << "))) * exp(-rootExp) * inv_pi * cosThetaO;" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl

View File

@ -19,9 +19,7 @@
#if !defined(__MICROFACET_H) #if !defined(__MICROFACET_H)
#define __MICROFACET_H #define __MICROFACET_H
#include <mitsuba/core/quad.h>
#include <mitsuba/core/timer.h> #include <mitsuba/core/timer.h>
#include <mitsuba/core/spline.h>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/bind.hpp> #include <boost/bind.hpp>
@ -499,53 +497,6 @@ public:
} }
} }
/**
* \brief Compute a spline representation for the overall Fresnel
* transmittance through a rough interface
*
* This function essentially computes the integral of
* 1 - \int_{S^2} f(w_i, w_o) * dw_o
* for incident directions 'wi' with a range of different inclinations
* (where f denotes a Cook-Torrance style reflectance model). It returns
* a cubic spline interpolation parameterized by the cosine of the angle
* between 'wi' and the (macro-) surface normal.
*
* \remark This only works for isotropic microfacet distributions
*/
CubicSpline *computeRoughTransmittance(Float extIOR, Float intIOR, Float alpha, size_t resolution) const {
if (isAnisotropic())
SLog(EError, "MicrofacetDistribution::computeRoughTransmission(): only "
"supports isotropic distributions!");
NDIntegrator integrator(1, 2, 5000, 0, 1e-5f);
CubicSpline *spline = new CubicSpline(resolution);
size_t nEvals, nEvalsTotal = 0;
ref<Timer> timer = new Timer();
Float stepSize = (1.0f-2*Epsilon)/(resolution-1);
for (size_t i=0; i<resolution; ++i) {
Float z = stepSize * i + Epsilon;
Vector wi(std::sqrt(std::max((Float) 0, 1-z*z)), 0, z);
Float min[2] = {0, 0}, max[2] = {1, 1},
integral = 0, error = 0;
integrator.integrateVectorized(
boost::bind(&MicrofacetDistribution::integrand1, this,
wi, extIOR, intIOR, alpha, _1, _2, _3),
min, max, &integral, &error, &nEvals
);
spline->append(z, 1-integral);
nEvalsTotal += nEvals;
}
SLog(EInfo, "Created a " SIZE_T_FMT "-node cubic spline approximation to the rough Fresnel "
"transmittance (integration took %i ms and " SIZE_T_FMT " function evaluations)",
resolution, timer->getMilliseconds(), nEvalsTotal);
spline->build();
return spline;
}
std::string toString() const { std::string toString() const {
switch (m_type) { switch (m_type) {
case EBeckmann: return "beckmann"; break; case EBeckmann: return "beckmann"; break;
@ -557,24 +508,6 @@ public:
return ""; return "";
} }
} }
protected:
/// Integrand helper function called by \ref computeRoughTransmission
void integrand1(const Vector &wi, Float extIOR, Float intIOR, Float alpha,
size_t nPts, const Float *in, Float *out) const {
for (int i=0; i<(int) nPts; ++i) {
Normal m = sample(Point2(in[2*i], in[2*i+1]), alpha);
Vector wo = 2 * dot(wi, m) * Vector(m) - wi;
if (Frame::cosTheta(wo) <= 0) {
out[i] = 0;
continue;
}
/* Calculate the specular reflection component */
out[i] = std::abs(fresnel(dot(wi, m), extIOR, intIOR)
* G(wi, wo, m, alpha) * dot(wi, m) /
(Frame::cosTheta(wi) * Frame::cosTheta(m)));
}
}
protected: protected:
EType m_type; EType m_type;
}; };

View File

@ -333,13 +333,13 @@ public:
<< " if (alpha > 0.0)" << endl << " if (alpha > 0.0)" << endl
<< " specRef = pow(alpha, exponent) * " << endl << " specRef = pow(alpha, exponent) * " << endl
<< " (exponent + 2) * 0.15915;" << endl << " (exponent + 2) * 0.15915;" << endl
<< " return (" << depNames[1] << "(uv) * 0.31831" << endl << " return (" << depNames[1] << "(uv) * inv_pi" << endl
<< " + " << depNames[2] << "(uv) * specRef) * cosTheta(wo);" << endl << " + " << depNames[2] << "(uv) * specRef) * cosTheta(wo);" << endl
<< "}" << endl << "}" << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (wi.z <= 0.0 || wo.z <= 0.0)" << endl << " if (wi.z <= 0.0 || wo.z <= 0.0)" << endl
<< " return vec3(0.0);" << endl << " return vec3(0.0);" << endl
<< " return " << depNames[1] << "(uv) * (0.31831 * cosTheta(wo));" << endl << " return " << depNames[1] << "(uv) * (inv_pi * cosTheta(wo));" << endl
<< "}" << endl; << "}" << endl;
} }

View File

@ -31,14 +31,14 @@ MTS_NAMESPACE_BEGIN
* \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{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the specular reflection component. Note that for physical * factor that can be used to modulate the specular reflection component. Note that
* realism, this parameter should never be touched. \default{1.0}} * for physical realism, this parameter should never be touched. \default{1.0}}
* \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the diffuse reflection component\default{0.5}} * factor used to modulate the diffuse reflection component\default{0.5}}
* \parameter{preserveColors}{\Boolean}{ * \parameter{nonlinear}{\Boolean}{
* Account for color shifts due to internal scattering? See the main text * Account for nonlinear color shifts due to internal scattering? See the
* for a detailed description.\default{Don't account for them and * main text for details.\default{Don't account for them and
* preserve the colors, i.e. \code{true}} * preserve the texture colors, i.e. \code{false}}
* } * }
* } * }
* *
@ -48,64 +48,26 @@ MTS_NAMESPACE_BEGIN
* {bsdf_plastic_shiny} * {bsdf_plastic_shiny}
* } * }
* *
* This plugin simulates a realistic smooth plastic-like material with internal * \vspace{3mm}
* scattering. Internally, this is modeled as a diffuse base layer with a * This plugin describes a smooth plastic-like material with internal scattering.
* dielectric material boundary. * It uses the Fresnel reflection and transmission coefficients to provide
* * direction-dependent specular and diffuse components.
* Given some illumination that is incident on such a material, a portion
* of the illumination is specularly reflected at the material
* boundary, which results in a sharp reflection in the mirror direction.
* The remaining illumination refracts into the material, where it
* scatters from the diffuse base layer. While some of the diffusely
* scattered illumination is able to directly refract outwards again,
* the remainder is reflected from the interior side of the dielectric boundary
* and will in fact remain trapped inside the material for some number of
* internal scattering events until it is finally able to escape.
*
* Due to the mathematical simplicity of this setup, it is possible to work
* out the correct form of the model without ever having to spend
* computational resources on the potentially large number of
* internal scattering events.
* Since it is simple, realistic, and fast, this model is often a better choice * Since it is simple, realistic, and fast, this model is often a better choice
* than the \pluginref{phong}, \pluginref{ward}, and \pluginref{roughplastic} * than the \pluginref{phong}, \pluginref{ward}, and \pluginref{roughplastic}
* plugins when rendering smooth plastic-like materials. * plugins when rendering smooth plastic-like materials.
* *
* * For convenience, this model allows to specify IOR values either numerically,
* \renderings{ * or based on a list of known materials (see \tblref{dielectric-iors} for
* \medrendering{Diffuse textured rendering}{bsdf_plastic_diffuse} * an overview).
* \medrendering{Plastic, \code{preserveColors=true}}{bsdf_plastic_preserve}
* \medrendering{Plastic, \code{preserveColors=false}}{bsdf_plastic_nopreserve}
* \caption{
* \label{fig:plastic-preservecolors}
* When asked to do so, this model can account for subtle color shifts due
* to internal scattering processes. The above images show a textured
* object first rendered using \pluginref{diffuse}, then
* \pluginref{plastic} with the default parameters, and finally using
* \pluginref{plastic} and support for color shifts.
* }
* }
*
* Note that due to the internal scattering, the diffuse color of the
* material is in practice slightly different from the color of the
* base layer on its own---in particular, the material color will tend to shift towards
* darker colors with higher saturation. Since this can be counter-intuitive when
* using bitmap textures, these color shifts are disabled by default. Specify
* the parameter \code{preserveColors=false} to enable them.
* \figref{plastic-preservecolors} illustrates this effect.
*
* Similar to the \pluginref{dielectric} plugin, this model allows to specify
* IOR values either numerically, or based on a list of known materials (see
* \tblref{dielectric-iors} for an overview).
* *
* Note that this plugin is quite similar to what one would get by applying the * Note that this plugin is quite similar to what one would get by applying the
* \pluginref{coating} plugin to the \pluginref{diffuse} material. The main * \pluginref{coating} plugin to the \pluginref{diffuse} material. The main
* difference is that this plugin is significantly faster, while at the same * difference is that this plugin is significantly faster, while at the same
* time causing less variance. Furthermore, it accounts for multiple * time causing less variance. Furthermore, it accounts for multiple
* interreflections inside the material, which avoids a serious energy * interreflections inside the material (read on for details), which avoids
* loss problem of the aforementioned plugin combination. * a serious energy loss problem of the aforementioned plugin
* * combination.
* \vspace{4mm} * \newpage
* *
* \begin{xml}[caption=A shiny material whose diffuse reflectance is * \begin{xml}[caption=A shiny material whose diffuse reflectance is
* specified using sRGB, label=lst:plastic-shiny] * specified using sRGB, label=lst:plastic-shiny]
@ -114,6 +76,68 @@ MTS_NAMESPACE_BEGIN
* <float name="intIOR" value="1.9"/> * <float name="intIOR" value="1.9"/>
* </bsdf> * </bsdf>
* \end{xml} * \end{xml}
*
* \renderings{
* \medrendering{Diffuse textured rendering}{bsdf_plastic_diffuse}
* \medrendering{Plastic model, \code{nonlinear=false}}{bsdf_plastic_preserve}
* \medrendering{Plastic model, \code{nonlinear=true}}{bsdf_plastic_nopreserve}
* \caption{
* \label{fig:plastic-nonlinear}
* When asked to do so, this model can account for subtle nonlinear color shifts due
* to internal scattering processes. The above images show a textured
* object first rendered using \pluginref{diffuse}, then
* \pluginref{plastic} with the default parameters, and finally using
* \pluginref{plastic} and support for nonlinear color shifts.
* }
* }
*
* \subsubsection*{Internal scattering}
* Internally, this is model simulates the interaction of light with a diffuse
* base surface coated by a thin dielectric layer. This is a convenient
* abstraction rather than a restriction. In other words, there are many
* materials that can be rendered with this model, even if they might not not
* fit this description perfectly well.
*
* \begin{figure}[h]
* \setcounter{subfigure}{0}
* \centering
* \subfloat[At the boundary, incident illumination is partly \mbox{reflected} and refracted]
* {\includegraphics[width=4.9cm]{images/bsdf_plastic_intscat_1.pdf}}\hfill
* \subfloat[The refracted portion scatters diffusely at the base layer]
* {\includegraphics[width=4.9cm]{images/bsdf_plastic_intscat_2.pdf}}\hfill
* \subfloat[Some of the illumination undergoes further internal scattering events]
* {\includegraphics[width=4.9cm]{images/bsdf_plastic_intscat_3.pdf}}
* \caption{
* \label{fig:plastic-intscat}
* An illustration of the scattering events that are internally
* handled by this plugin}
* \end{figure}
*
* Given illumination that is incident upon such a material, a portion
* of the illumination is specularly reflected at the material
* boundary, which results in a sharp reflection in the mirror direction
* (\subfigref{plastic-intscat}{a}).
* The remaining illumination refracts into the material, where it
* scatters from the diffuse base layer. (\subfigref{plastic-intscat}{b}).
* While some of the diffusely scattered illumination is able to
* directly refract outwards again, the remainder is reflected from the
* interior side of the dielectric boundary and will in fact remain
* trapped inside the material for some number of internal scattering
* events until it is finally able to escape (\subfigref{plastic-intscat}{c}).
*
* Due to the mathematical simplicity of this setup, it is possible to work
* out the correct form of the model without actually having to simulate
* the potentially large number of internal scattering events.
*
* Note that due to the internal scattering, the diffuse color of the
* material is in practice slightly different from the color of the
* base layer on its own---in particular, the material color will tend to shift towards
* darker colors with higher saturation. Since this can be counter-intuitive when
* using bitmap textures, these color shifts are disabled by default. Specify
* the parameter \code{nonlinear=true} to enable them. \figref{plastic-nonlinear}
* illustrates the resulting change. This effect is also seen in real life,
* for instance a piece of wood will look slightly darker after coating it
* with a layer of varnish.
*/ */
class SmoothPlastic : public BSDF { class SmoothPlastic : public BSDF {
public: public:
@ -129,7 +153,7 @@ public:
m_diffuseReflectance = new ConstantSpectrumTexture( m_diffuseReflectance = new ConstantSpectrumTexture(
props.getSpectrum("diffuseReflectance", Spectrum(0.5f))); props.getSpectrum("diffuseReflectance", Spectrum(0.5f)));
m_preserveColors = props.getBoolean("preserveColors", true); m_nonlinear = props.getBoolean("nonlinear", false);
m_specularSamplingWeight = 0.0f; m_specularSamplingWeight = 0.0f;
} }
@ -138,7 +162,7 @@ public:
: BSDF(stream, manager) { : BSDF(stream, manager) {
m_intIOR = stream->readFloat(); m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat(); m_extIOR = stream->readFloat();
m_preserveColors = stream->readBool(); m_nonlinear = 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));
configure(); configure();
@ -152,7 +176,8 @@ public:
m_diffuseReflectance, "diffuseReflectance", 1.0f); m_diffuseReflectance, "diffuseReflectance", 1.0f);
/* Numerically approximate the diffuse Fresnel reflectance */ /* Numerically approximate the diffuse Fresnel reflectance */
m_fdr = fresnelDiffuseReflectance(m_extIOR / m_intIOR, false); m_fdrInt = fresnelDiffuseReflectance(m_extIOR / m_intIOR, false);
m_fdrExt = fresnelDiffuseReflectance(m_intIOR / m_extIOR, false);
/* Compute weights that further steer samples towards /* Compute weights that further steer samples towards
the specular or diffuse components */ the specular or diffuse components */
@ -161,8 +186,8 @@ public:
m_specularSamplingWeight = sAvg / (dAvg + sAvg); m_specularSamplingWeight = sAvg / (dAvg + sAvg);
Float eta = m_extIOR / m_intIOR; Float invEta = m_extIOR / m_intIOR;
m_eta2 = eta*eta; m_invEta2 = invEta*invEta;
m_usesRayDifferentials = m_usesRayDifferentials =
m_specularReflectance->usesRayDifferentials() || m_specularReflectance->usesRayDifferentials() ||
@ -177,9 +202,8 @@ public:
BSDF::configure(); BSDF::configure();
} }
Spectrum getDiffuseReflectance(const Intersection &its) const { Spectrum getDiffuseReflectance(const Intersection &its) const {
return m_diffuseReflectance->getValue(its); return m_diffuseReflectance->getValue(its) * (1-m_fdrExt);
} }
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
@ -187,7 +211,7 @@ public:
stream->writeFloat(m_intIOR); stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR); stream->writeFloat(m_extIOR);
stream->writeBool(m_preserveColors); stream->writeBool(m_nonlinear);
manager->serialize(stream, m_specularReflectance.get()); manager->serialize(stream, m_specularReflectance.get());
manager->serialize(stream, m_diffuseReflectance.get()); manager->serialize(stream, m_diffuseReflectance.get());
} }
@ -230,14 +254,12 @@ public:
} else if (measure == ESolidAngle && hasDiffuse) { } else if (measure == ESolidAngle && hasDiffuse) {
Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR);
if (hasDiffuse) { Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
Spectrum diff = m_diffuseReflectance->getValue(bRec.its); if (m_nonlinear)
if (m_preserveColors) diff /= Spectrum(1) - diff * m_fdrInt;
diff /= 1 - m_fdr; else
else diff /= 1 - m_fdrInt;
diff /= Spectrum(1) - m_fdr*diff; return diff * (INV_PI * Frame::cosTheta(bRec.wo) * m_invEta2 * (1-Fr) * (1-Fr2));
return diff * (INV_PI * Frame::cosTheta(bRec.wo) * m_eta2 * (1-Fr) * (1-Fr2));
}
} }
return Spectrum(0.0f); return Spectrum(0.0f);
@ -306,12 +328,12 @@ public:
Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR);
Spectrum diff = m_diffuseReflectance->getValue(bRec.its); Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors) if (m_nonlinear)
diff /= 1 - m_fdr; diff /= Spectrum(1) - m_fdrInt*diff;
else else
diff /= Spectrum(1) - m_fdr*diff; diff /= 1 - m_fdrInt;
return diff * (m_eta2 * (1-Fr) * (1-Fr2) / (1-probSpecular)); return diff * (m_invEta2 * (1-Fr) * (1-Fr2) / (1-probSpecular));
} }
} else if (hasSpecular) { } else if (hasSpecular) {
bRec.sampledComponent = 0; bRec.sampledComponent = 0;
@ -325,12 +347,12 @@ public:
bRec.sampledType = EDiffuseReflection; bRec.sampledType = EDiffuseReflection;
Spectrum diff = m_diffuseReflectance->getValue(bRec.its); Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors) if (m_nonlinear)
diff /= 1 - m_fdr; diff /= Spectrum(1) - diff*m_fdrInt;
else else
diff /= Spectrum(1) - m_fdr*diff; diff /= 1 - m_fdrInt;
return diff * (m_eta2 * (1-Fr) * (1-Fr2)); return diff * (m_invEta2 * (1-Fr) * (1-Fr2));
} }
} }
@ -368,14 +390,14 @@ public:
Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR); Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR);
Spectrum diff = m_diffuseReflectance->getValue(bRec.its); Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors) if (m_nonlinear)
diff /= 1 - m_fdr; diff /= Spectrum(1) - diff*m_fdrInt;
else else
diff /= Spectrum(1) - m_fdr*diff; diff /= 1 - m_fdrInt;
pdf = (1-probSpecular) * Frame::cosTheta(bRec.wo) * INV_PI; pdf = (1-probSpecular) * Frame::cosTheta(bRec.wo) * INV_PI;
return diff * (m_eta2 * (1-Fr) * (1-Fr2) / (1-probSpecular)); return diff * (m_invEta2 * (1-Fr) * (1-Fr2) / (1-probSpecular));
} }
} else if (hasSpecular) { } else if (hasSpecular) {
bRec.sampledComponent = 0; bRec.sampledComponent = 0;
@ -392,12 +414,12 @@ public:
pdf = Frame::cosTheta(bRec.wo) * INV_PI; pdf = Frame::cosTheta(bRec.wo) * INV_PI;
Spectrum diff = m_diffuseReflectance->getValue(bRec.its); Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors) if (m_nonlinear)
diff /= 1 - m_fdr; diff /= Spectrum(1) - diff*m_fdrInt;
else else
diff /= Spectrum(1) - m_fdr*diff; diff /= 1 - m_fdrInt;
return diff * (m_eta2 * (1-Fr) * (1-Fr2)); return diff * (m_invEta2 * (1-Fr) * (1-Fr2));
} }
} }
@ -411,21 +433,23 @@ public:
<< " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl << " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " preserveColors = " << m_preserveColors << "," << endl << " nonlinear = " << m_nonlinear << "," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
<< " extIOR = " << m_extIOR << "," << endl << " extIOR = " << m_extIOR << "," << endl
<< " fdr = " << m_fdr << endl << " fdrInt = " << m_fdrInt << "," << endl
<< " fdrExt = " << m_fdrExt << endl
<< "]"; << "]";
return oss.str(); return oss.str();
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
Float m_intIOR, m_extIOR, m_fdr, m_eta2; Float m_intIOR, m_extIOR;
Float m_fdrInt, m_fdrExt, m_invEta2;
ref<Texture> m_diffuseReflectance; ref<Texture> m_diffuseReflectance;
ref<Texture> m_specularReflectance; ref<Texture> m_specularReflectance;
Float m_specularSamplingWeight; Float m_specularSamplingWeight;
bool m_preserveColors; bool m_nonlinear;
}; };
/** /**
@ -513,12 +537,12 @@ public:
<< " float G = " << evalName << "_G(H, wi, wo);" << endl << " float G = " << evalName << "_G(H, wi, wo);" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl << " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl
<< " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl << " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl
<< " diffuseRef * ((1-F) * cosTheta(wo) * 0.31831);" << endl << " diffuseRef * ((1-F) * cosTheta(wo) * inv_pi);" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl << " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl
<< " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl << " return diffuseRef * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl; << "}" << endl;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()

View File

@ -25,7 +25,7 @@
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
/*!\plugin{sssbrdf}{Subsurface scattering BRDF} /*!\plugin{rmbrdf}{Random medium BRDF}
* *
* \parameters{ * \parameters{
* \parameter{material}{\String}{Name of a material preset, see * \parameter{material}{\String}{Name of a material preset, see
@ -46,20 +46,29 @@ MTS_NAMESPACE_BEGIN
* 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{g}{\Float\Or\String}{Specifies the phase function anisotropy * \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy
* --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}} * --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}}
* \parameter{alpha}{\Float}{ * \parameter{alpha}{\Float\Or\Texture}{
* Specifies the roughness of the unresolved surface micro-geometry. * Specifies the roughness of the unresolved surface micro-geometry.
* \default{0.0, i.e. the surface has a smooth finish} * \default{0.1, i.e. the surface has a slightly rough finish}
* } * }
* } * }
* *
* \renderings{
* \rendering{Rendering using the whole milk material preset}{bsdf_sssbrdf}
* }
*
* This plugin implements a BRDF scattering model that emulates interactions * This plugin implements a BRDF scattering model that emulates interactions
* with a participating medium embedded inside a dielectric layer. By * with a random medium embedded inside a dielectric layer. By
* approximating these events using a BRDF, any scattered illumination * approximating these events using a BRDF, any scattered illumination
* is assumed to exit the material \emph{directly} at the original point of incidence. * is assumed to exit the material \emph{directly} at the original point of incidence.
* To account for internal light transport with \emph{different} incident * To simulate actual subsurface scattering, refer to Sections~\ref{sec:media}
* and exitant positions, please refer to Sections~\ref{sec:media}
* and \ref{sec:subsurface}. * and \ref{sec:subsurface}.
* *
* Note that renderings with this BRDF will usually look very similar to what might
* also be obtained using \pluginref{plastic}. The plugin's reason for existance
* is that can be configured using parameters that are traditionally reserved
* for participating media.
*
* \subsection*{Implementation details}
* Internally, the model is implemented by instantiating a Hanrahan-Krueger * Internally, the model is implemented by instantiating a Hanrahan-Krueger
* BSDF for single scattering in an infinitely thick layer together with * BSDF for single scattering in an infinitely thick layer together with
* an approximate multiple scattering component based on Jensen's * an approximate multiple scattering component based on Jensen's
@ -72,9 +81,9 @@ MTS_NAMESPACE_BEGIN
* in terms of the scattering and absorption coefficients \code{sigmaS} * in terms of the scattering and absorption coefficients \code{sigmaS}
* and \code{sigmaA}. * and \code{sigmaA}.
*/ */
class SSSBRDF : public BSDF { class RandomMediumBRDF : public BSDF {
public: public:
SSSBRDF(const Properties &props) RandomMediumBRDF(const Properties &props)
: BSDF(props), m_configured(false) { : BSDF(props), m_configured(false) {
Spectrum sigmaS, sigmaA; // ignored here Spectrum sigmaS, sigmaA; // ignored here
@ -85,7 +94,7 @@ public:
Properties hgProps("hg"); Properties hgProps("hg");
hgProps.setFloat("g", g); hgProps.setFloat("g", g);
Float alpha = props.getFloat("alpha", 0.0f); Float alpha = props.getFloat("alpha", 0.1f);
ref<PhaseFunction> hg = static_cast<PhaseFunction *> ( ref<PhaseFunction> hg = static_cast<PhaseFunction *> (
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(
@ -129,7 +138,7 @@ public:
props.markQueried("alpha"); props.markQueried("alpha");
} }
SSSBRDF(Stream *stream, InstanceManager *manager) RandomMediumBRDF(Stream *stream, InstanceManager *manager)
: BSDF(stream, manager), m_configured(true) { : BSDF(stream, manager), m_configured(true) {
m_coating = static_cast<BSDF *>(manager->getInstance(stream)); m_coating = static_cast<BSDF *>(manager->getInstance(stream));
m_hk = static_cast<BSDF *>(manager->getInstance(stream)); m_hk = static_cast<BSDF *>(manager->getInstance(stream));
@ -194,8 +203,14 @@ public:
void addChild(const std::string &name, ConfigurableObject *child) { void addChild(const std::string &name, ConfigurableObject *child) {
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) { if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_hk->addChild(name, child); if (name == "sigmaS" || name == "sigmaA" || name == "sigmaT" || name == "albedo") {
m_dipole->addChild(name, child); m_hk->addChild(name, child);
m_dipole->addChild(name, child);
} else if (name == "alpha") {
m_coating->addChild(name, child);
} else {
BSDF::addChild(name, child);
}
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }
@ -207,7 +222,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "SSSBRDF[" << endl oss << "RandomMediumBRDF[" << endl
<< " name = \"" << m_name << "\"" << endl << " name = \"" << m_name << "\"" << endl
<< " nested = " << indent(m_coating->toString()) << endl << " nested = " << indent(m_coating->toString()) << endl
<< "]"; << "]";
@ -223,6 +238,6 @@ private:
bool m_configured; bool m_configured;
}; };
MTS_IMPLEMENT_CLASS_S(SSSBRDF, false, BSDF) MTS_IMPLEMENT_CLASS_S(RandomMediumBRDF, false, BSDF)
MTS_EXPORT_PLUGIN(SSSBRDF, "Subsurface scattering BRDF"); MTS_EXPORT_PLUGIN(RandomMediumBRDF, "Random medium BRDF");
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -19,12 +19,11 @@
#include <mitsuba/render/bsdf.h> #include <mitsuba/render/bsdf.h>
#include <mitsuba/hw/basicshader.h> #include <mitsuba/hw/basicshader.h>
#include "microfacet.h" #include "microfacet.h"
#include "rtrans.h"
#include "ior.h" #include "ior.h"
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
#define TRANSMITTANCE_PRECOMP_NODES 200
/*!\plugin{roughcoating}{Rough dielectric coating} /*!\plugin{roughcoating}{Rough dielectric coating}
* \order{10} * \order{10}
* \icon{bsdf_roughcoating} * \icon{bsdf_roughcoating}
@ -46,7 +45,7 @@ MTS_NAMESPACE_BEGIN
* \vspace{-4mm} * \vspace{-4mm}
* \end{enumerate} * \end{enumerate}
* } * }
* \parameter{alpha}{\Float}{ * \parameter{alpha}{\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 * When the Beckmann distribution is used, this parameter is equal to the
* \emph{root mean square} (RMS) slope of the microfacets. * \emph{root mean square} (RMS) slope of the microfacets.
@ -122,7 +121,7 @@ public:
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_alpha = m_distribution.transformRoughness( m_alpha = new ConstantFloatTexture(
props.getFloat("alpha", 0.1f)); props.getFloat("alpha", 0.1f));
m_specularSamplingWeight = 0.0f; m_specularSamplingWeight = 0.0f;
@ -135,8 +134,7 @@ public:
); );
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_roughTransmittance = static_cast<CubicSpline *>(manager->getInstance(stream)); m_alpha = static_cast<Texture *>(manager->getInstance(stream));
m_alpha = stream->readFloat();
m_intIOR = stream->readFloat(); m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat(); m_extIOR = stream->readFloat();
m_thickness = stream->readFloat(); m_thickness = stream->readFloat();
@ -146,7 +144,7 @@ public:
void configure() { void configure() {
unsigned int extraFlags = 0; unsigned int extraFlags = 0;
if (!m_sigmaA->isConstant()) if (!m_sigmaA->isConstant() || !m_alpha->isConstant())
extraFlags |= ESpatiallyVarying; extraFlags |= ESpatiallyVarying;
m_components.clear(); m_components.clear();
@ -156,7 +154,8 @@ public:
m_components.push_back(EGlossyReflection | EFrontSide | EBackSide); m_components.push_back(EGlossyReflection | EFrontSide | EBackSide);
m_usesRayDifferentials = m_nested->usesRayDifferentials() m_usesRayDifferentials = m_nested->usesRayDifferentials()
|| m_sigmaA->usesRayDifferentials(); || m_sigmaA->usesRayDifferentials()
|| m_alpha->usesRayDifferentials();
/* Compute weights that further steer samples towards /* Compute weights that further steer samples towards
the specular or nested components */ the specular or nested components */
@ -165,9 +164,25 @@ public:
m_specularSamplingWeight = 1.0f / (avgAbsorption + 1.0f); m_specularSamplingWeight = 1.0f / (avgAbsorption + 1.0f);
/* Precompute the rough transmittance through the interface */ if (!m_roughTransmittance.get()) {
m_roughTransmittance = m_distribution.computeRoughTransmittance( /* Load precomputed data used to compute the rough
m_extIOR, m_intIOR, m_alpha, TRANSMITTANCE_PRECOMP_NODES); transmittance through the dielectric interface */
m_roughTransmittance = new RoughTransmittance(
m_distribution.getType());
Float eta = m_intIOR / m_extIOR;
m_roughTransmittance->checkEta(eta);
m_roughTransmittance->checkAlpha(m_alpha->getMinimum().average());
m_roughTransmittance->checkAlpha(m_alpha->getMaximum().average());
/* Reduce the rough transmittance data to a 2D slice */
m_roughTransmittance->setEta(eta);
/* If possible, even reduce it to a 1D slice */
if (m_alpha->isConstant())
m_roughTransmittance->setAlpha(
m_alpha->getValue(Intersection()).average());
}
BSDF::configure(); BSDF::configure();
} }
@ -220,6 +235,10 @@ 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 */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
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) {
/* Calculate the reflection half-vector */ /* Calculate the reflection half-vector */
@ -227,13 +246,13 @@ public:
* signum(Frame::cosTheta(bRec.wo)); * signum(Frame::cosTheta(bRec.wo));
/* Evaluate the microsurface normal distribution */ /* Evaluate the microsurface normal distribution */
const Float D = m_distribution.eval(H, m_alpha); const Float D = m_distribution.eval(H, alphaT);
/* Fresnel term */ /* Fresnel term */
const Float F = fresnel(absDot(bRec.wi, H), m_extIOR, m_intIOR); const Float F = fresnel(absDot(bRec.wi, H), m_extIOR, m_intIOR);
/* Smith's shadow-masking function */ /* Smith's shadow-masking function */
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, m_alpha); const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);
/* Calculate the specular reflection component */ /* Calculate the specular reflection component */
Float value = F * D * G / Float value = F * D * G /
@ -248,8 +267,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))) * m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha) *
m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo))); m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wo)), alpha);
Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness; Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness;
if (!sigmaA.isZero()) if (!sigmaA.isZero())
@ -282,11 +301,15 @@ public:
const Vector H = normalize(bRec.wo+bRec.wi) const Vector H = normalize(bRec.wo+bRec.wi)
* signum(Frame::cosTheta(bRec.wo)); * signum(Frame::cosTheta(bRec.wo));
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
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))); std::abs(Frame::cosTheta(bRec.wi)), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -304,7 +327,7 @@ public:
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 microsurface normal distribution */
const Float prob = m_distribution.pdf(H, m_alpha); const Float prob = m_distribution.pdf(H, alphaT);
result = prob * dwh_dwo * probSpecular; result = prob * dwh_dwo * probSpecular;
} }
@ -337,10 +360,14 @@ public:
bool choseSpecular = hasSpecular; bool choseSpecular = hasSpecular;
Point2 sample(_sample); Point2 sample(_sample);
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
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))); probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -357,7 +384,7 @@ public:
if (choseSpecular) { if (choseSpecular) {
/* Perfect specular reflection based on the microsurface normal */ /* Perfect specular reflection based on the microsurface normal */
Normal m = m_distribution.sample(sample, m_alpha); Normal m = m_distribution.sample(sample, alphaT);
bRec.wo = reflect(bRec.wi, m); bRec.wo = reflect(bRec.wi, m);
bRec.sampledComponent = m_components.size()-1; bRec.sampledComponent = m_components.size()-1;
bRec.sampledType = EGlossyReflection; bRec.sampledType = EGlossyReflection;
@ -398,8 +425,7 @@ public:
stream->writeUInt((uint32_t) m_distribution.getType()); stream->writeUInt((uint32_t) m_distribution.getType());
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_roughTransmittance.get()); manager->serialize(stream, m_alpha.get());
stream->writeFloat(m_alpha);
stream->writeFloat(m_intIOR); stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR); stream->writeFloat(m_extIOR);
stream->writeFloat(m_thickness); stream->writeFloat(m_thickness);
@ -410,8 +436,13 @@ public:
if (m_nested != NULL) if (m_nested != NULL)
Log(EError, "Only a single nested BRDF can be added!"); Log(EError, "Only a single nested BRDF can be added!");
m_nested = static_cast<BSDF *>(child); m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") { } else if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_sigmaA = static_cast<Texture *>(m_sigmaA); if (name == "sigmaA")
m_sigmaA = static_cast<Texture *>(child);
else if (name == "alpha")
m_alpha = static_cast<Texture *>(child);
else
BSDF::addChild(name, child);
} else { } else {
BSDF::addChild(name, child); BSDF::addChild(name, child);
} }
@ -422,8 +453,8 @@ public:
oss << "RoughCoating[" << endl oss << "RoughCoating[" << endl
<< " name = \"" << getName() << "\"," << endl << " name = \"" << getName() << "\"," << endl
<< " distribution = " << m_distribution.toString() << "," << endl << " distribution = " << m_distribution.toString() << "," << endl
<< " alpha = " << m_alpha << "," << endl << " alpha = " << indent(m_alpha->toString()) << "," << endl
<< " sigmaA = " << m_sigmaA->toString() << "," << endl << " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
@ -438,10 +469,11 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
MicrofacetDistribution m_distribution; MicrofacetDistribution m_distribution;
ref<CubicSpline> m_roughTransmittance; ref<RoughTransmittance> m_roughTransmittance;
ref<Texture> m_sigmaA; ref<Texture> m_sigmaA;
ref<Texture> m_alpha;
ref<BSDF> m_nested; ref<BSDF> m_nested;
Float m_alpha, m_intIOR, m_extIOR; Float m_intIOR, m_extIOR;
Float m_specularSamplingWeight; Float m_specularSamplingWeight;
Float m_thickness; Float m_thickness;
}; };
@ -457,46 +489,44 @@ private:
*/ */
class RoughCoatingShader : public Shader { class RoughCoatingShader : public Shader {
public: public:
RoughCoatingShader(Renderer *renderer, RoughCoatingShader(Renderer *renderer, const BSDF *nested,
const BSDF *nested, const Texture *sigmaA, const Texture *alpha,
const Texture *sigmaA, Float extIOR, Float intIOR) : Shader(renderer, EBSDFShader),
Float alpha, Float extIOR, m_nested(nested), m_sigmaA(sigmaA), m_alpha(alpha),
Float intIOR) : Shader(renderer, EBSDFShader), m_extIOR(extIOR), m_intIOR(intIOR) {
m_nested(nested),
m_sigmaA(sigmaA),
m_alpha(alpha), m_extIOR(extIOR), m_intIOR(intIOR) {
m_nestedShader = renderer->registerShaderForResource(m_nested.get()); m_nestedShader = renderer->registerShaderForResource(m_nested.get());
m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get()); m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get());
m_alpha = std::max(m_alpha, (Float) 0.2f); m_alphaShader = renderer->registerShaderForResource(m_alpha.get());
m_R0 = fresnel(1.0f, m_extIOR, m_intIOR); m_R0 = fresnel(1.0f, m_extIOR, m_intIOR);
m_eta = extIOR / intIOR; m_eta = extIOR / intIOR;
} }
bool isComplete() const { bool isComplete() const {
return m_nestedShader.get() != NULL return m_nestedShader.get() != NULL
&& m_sigmaAShader.get() != NULL; && m_sigmaAShader.get() != NULL
&& m_alphaShader.get() != NULL;
} }
void putDependencies(std::vector<Shader *> &deps) { void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_nestedShader.get()); deps.push_back(m_nestedShader.get());
deps.push_back(m_sigmaAShader.get()); deps.push_back(m_sigmaAShader.get());
deps.push_back(m_alphaShader.get());
} }
void cleanup(Renderer *renderer) { void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_nested.get()); renderer->unregisterShaderForResource(m_nested.get());
renderer->unregisterShaderForResource(m_sigmaA.get()); renderer->unregisterShaderForResource(m_sigmaA.get());
renderer->unregisterShaderForResource(m_alpha.get());
} }
void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const { void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const {
parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); parameterIDs.push_back(program->getParameterID(evalName + "_R0", false));
parameterIDs.push_back(program->getParameterID(evalName + "_eta", false)); parameterIDs.push_back(program->getParameterID(evalName + "_eta", false));
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
} }
void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const { void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const {
program->setParameter(parameterIDs[0], m_R0); program->setParameter(parameterIDs[0], m_R0);
program->setParameter(parameterIDs[1], m_eta); program->setParameter(parameterIDs[1], m_eta);
program->setParameter(parameterIDs[2], m_alpha);
} }
void generateCode(std::ostringstream &oss, void generateCode(std::ostringstream &oss,
@ -504,7 +534,6 @@ public:
const std::vector<std::string> &depNames) const { const std::vector<std::string> &depNames) const {
oss << "uniform float " << evalName << "_R0;" << endl oss << "uniform float " << evalName << "_R0;" << endl
<< "uniform float " << evalName << "_eta;" << endl << "uniform float " << evalName << "_eta;" << endl
<< "uniform float " << evalName << "_alpha;" << endl
<< endl << endl
<< "float " << evalName << "_schlick(float ct) {" << endl << "float " << evalName << "_schlick(float ct) {" << endl
<< " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl << " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl
@ -526,13 +555,13 @@ public:
<< " }" << endl << " }" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "float " << evalName << "_D(vec3 m) {" << endl << "float " << evalName << "_D(vec3 m, float alpha) {" << endl
<< " float ct = cosTheta(m);" << endl << " float ct = cosTheta(m);" << endl
<< " if (cosTheta(m) <= 0.0)" << endl << " if (cosTheta(m) <= 0.0)" << endl
<< " return 0.0;" << endl << " return 0.0;" << endl
<< " float ex = tanTheta(m) / " << evalName << "_alpha;" << endl << " float ex = tanTheta(m) / alpha;" << endl
<< " return exp(-(ex*ex)) / (pi * " << evalName << "_alpha" << endl << " return exp(-(ex*ex)) / (pi * alpha * alpha *" << endl
<< " * " << evalName << "_alpha * pow(cosTheta(m), 4.0));" << endl << " pow(cosTheta(m), 4.0));" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl << "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl
@ -559,7 +588,8 @@ public:
<< " 1/abs(cosTheta(woPrime))));" << endl << " 1/abs(cosTheta(woPrime))));" << endl
<< " if (cosTheta(wi)*cosTheta(wo) > 0) {" << endl << " if (cosTheta(wi)*cosTheta(wo) > 0) {" << endl
<< " vec3 H = normalize(wi + wo);" << endl << " vec3 H = normalize(wi + wo);" << endl
<< " float D = " << evalName << "_D(H)" << ";" << endl << " float alpha = max(0.2, " << depNames[2] << "(uv)[0]);" << endl
<< " float D = " << evalName << "_D(H, alpha)" << ";" << endl
<< " float G = " << evalName << "_G(H, wi, wo);" << endl << " float G = " << evalName << "_G(H, wi, wo);" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl << " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl
<< " result += vec3(F * D * G / (4*cosTheta(wi)));" << endl << " result += vec3(F * D * G / (4*cosTheta(wi)));" << endl
@ -578,12 +608,14 @@ private:
ref<Shader> m_nestedShader; ref<Shader> m_nestedShader;
ref<const Texture> m_sigmaA; ref<const Texture> m_sigmaA;
ref<Shader> m_sigmaAShader; ref<Shader> m_sigmaAShader;
Float m_alpha, m_extIOR, m_intIOR, m_R0, m_eta; ref<const Texture> m_alpha;
ref<Shader> m_alphaShader;
Float m_extIOR, m_intIOR, m_R0, m_eta;
}; };
Shader *RoughCoating::createShader(Renderer *renderer) const { Shader *RoughCoating::createShader(Renderer *renderer) const {
return new RoughCoatingShader(renderer, m_nested.get(), return new RoughCoatingShader(renderer, m_nested.get(),
m_sigmaA.get(), m_alpha, m_extIOR, m_intIOR); m_sigmaA.get(), m_alpha.get(), m_extIOR, m_intIOR);
} }
MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader) MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader)

View File

@ -65,8 +65,10 @@ MTS_NAMESPACE_BEGIN
* refraction (the absorption coefficient). * refraction (the absorption coefficient).
* \default{based on \texttt{material}}} * \default{based on \texttt{material}}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the reflectance component\default{1.0}} * 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}
* 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
@ -78,7 +80,6 @@ MTS_NAMESPACE_BEGIN
* $\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}
* \vspace{-8mm}
* } * }
* *
* Microfacet theory describes rough * Microfacet theory describes rough
@ -90,7 +91,7 @@ MTS_NAMESPACE_BEGIN
* *
* 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
* be very similar, though scenes using this plugin will take longer to render * be identical, though scenes using this plugin will take longer to render
* due to the additional computational burden of tracking surface roughness. * due to the additional computational burden of tracking surface roughness.
* *
* The implementation is based on the paper ``Microfacet Models * The implementation is based on the paper ``Microfacet Models
@ -113,8 +114,17 @@ MTS_NAMESPACE_BEGIN
* 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{-7mm} * \vspace{4mm}
* \subsubsection*{Technical details}\vspace{-3mm} * \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"/>
* <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 * When rendering with the Ashikhmin-Shirley or Phong microfacet
* distributions, a conversion is used to turn the specified * distributions, a conversion is used to turn the specified
* $\alpha$ roughness value into the exponents of these distributions. * $\alpha$ roughness value into the exponents of these distributions.
@ -132,20 +142,10 @@ MTS_NAMESPACE_BEGIN
* *
* 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
* in RGB mode, the computations will be much more approximate in this case. * in RGB mode, the computations will be more approximate in nature.
* Also note that this material is one-sided---that is, observed from the * Also note that this material is one-sided---that is, observed from the
* back side, it will be completely black. If this is undesirable, * back side, it will be completely black. If this is undesirable,
* consider using the \pluginref{twosided} BRDF adapter. * consider using the \pluginref{twosided} BRDF adapter.
*
* \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"/>
* <float name="alphaU" value="0.05"/>
* <float name="alphaV" value="0.3"/>
* </bsdf>
* \end{xml}
*
*/ */
class RoughConductor : public BSDF { class RoughConductor : public BSDF {
public: public:
@ -517,7 +517,7 @@ public:
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " return " << evalName << "_R0 * 0.31831 * cosTheta(wo);"<< endl << " return " << evalName << "_R0 * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl; << "}" << endl;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()

View File

@ -69,9 +69,11 @@ MTS_NAMESPACE_BEGIN
* \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{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the reflectance component\default{1.0}} * 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 * \parameter{specular\showbreak Transmittance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the transmittance component\default{1.0}} * factor that can be used to modulate the specular transmission component. Note
* that for physical realism, this parameter should never be touched. \default{1.0}}
* }\vspace{4mm} * }\vspace{4mm}
* *
* This plugin implements a realistic microfacet scattering model for rendering * This plugin implements a realistic microfacet scattering model for rendering
@ -91,7 +93,7 @@ MTS_NAMESPACE_BEGIN
* *
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin * This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
* \pluginref{dielectric}. For very low values of $\alpha$, the two will * \pluginref{dielectric}. For very low values of $\alpha$, the two will
* be very similar, though scenes using this plugin will take longer to render * be identical, though scenes using this plugin will take longer to render
* due to the additional computational burden of tracking surface roughness. * due to the additional computational burden of tracking surface roughness.
* *
* The implementation is based on the paper ``Microfacet Models * The implementation is based on the paper ``Microfacet Models

View File

@ -333,13 +333,13 @@ public:
<< " tanBeta = sinTheta(wo) / cosTheta(wo);" << endl << " tanBeta = sinTheta(wo) / cosTheta(wo);" << endl
<< " }" << endl << " }" << endl
<< " float value = A + B * maxCos * sinAlpha * tanBeta;" << endl << " float value = A + B * maxCos * sinAlpha * tanBeta;" << endl
<< " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo) * value;" << endl << " return " << depNames[0] << "(uv) * inv_pi * cosTheta(wo) * value;" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (cosTheta(wi) <= 0.0 || cosTheta(wo) <= 0.0)" << endl << " if (cosTheta(wi) <= 0.0 || cosTheta(wo) <= 0.0)" << endl
<< " return vec3(0.0);" << endl << " return vec3(0.0);" << endl
<< " return " << depNames[0] << "(uv) * 0.31831 * cosTheta(wo);" << endl << " return " << depNames[0] << "(uv) * inv_pi * cosTheta(wo);" << endl
<< "}" << endl; << "}" << endl;
} }

View File

@ -19,12 +19,11 @@
#include <mitsuba/render/bsdf.h> #include <mitsuba/render/bsdf.h>
#include <mitsuba/hw/basicshader.h> #include <mitsuba/hw/basicshader.h>
#include "microfacet.h" #include "microfacet.h"
#include "rtrans.h"
#include "ior.h" #include "ior.h"
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
#define TRANSMITTANCE_PRECOMP_NODES 100
/*!\plugin{roughplastic}{Rough plastic material} /*!\plugin{roughplastic}{Rough plastic material}
* \order{8} * \order{8}
* \icon{bsdf_roughplastic} * \icon{bsdf_roughplastic}
@ -43,10 +42,10 @@ MTS_NAMESPACE_BEGIN
* Due to the underlying microfacet theory, * Due to the underlying microfacet theory,
* the use of this distribution here leads to more realistic * the use of this distribution here leads to more realistic
* behavior than the separately available \pluginref{phong} plugin. * behavior than the separately available \pluginref{phong} plugin.
* \vspace{-4mm} * \vspace{-3mm}
* \end{enumerate} * \end{enumerate}
* } * }
* \parameter{alpha}{\Float}{ * \parameter{alpha}{\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 * When the Beckmann distribution is used, this parameter is equal to the
* \emph{root mean square} (RMS) slope of the microfacets. * \emph{root mean square} (RMS) slope of the microfacets.
@ -57,26 +56,23 @@ MTS_NAMESPACE_BEGIN
* \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{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the specular reflection component. Note that for physical * factor that can be used to modulate the specular reflection component. Note
* realism, this parameter should never be touched. \default{1.0}} * that for physical realism, this parameter should never be touched. \default{1.0}}
* \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional * \parameter{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the diffuse reflection component\default{0.5}} * factor used to modulate the diffuse reflection component\default{0.5}}
* }\vspace{-1mm} * \parameter{nonlinear}{\Boolean}{
* \renderings{ * Account for nonlinear color shifts due to internal scattering? See the
* \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann} * \pluginref{plastic} plugin for details.\default{Don't account for them and
* \rendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx} * preserve the texture colors, i.e. \code{false}}
* }\vspace{-1mm} * }
* }
* *
* \vspace{3mm}
* This plugin implements a realistic microfacet scattering model for rendering * This plugin implements a realistic microfacet scattering model for rendering
* rough dielectric materials with internal scattering, such as plastic. It can * rough dielectric materials with internal scattering, such as plastic. It can
* be interpreted as a fancy version of the Cook-Torrance model and should be * be interpreted as a fancy version of the Cook-Torrance model and should be
* preferred over empirical models like \pluginref{phong} and \pluginref{ward} * preferred over empirical models like \pluginref{phong} and \pluginref{ward}
* when possible. * when possible.
* \renderings{
* \setcounter{subfigure}{2}
* \rendering{Beckmann, $\alpha=0.05$, diffuseReflectance=0}
* {bsdf_roughplastic_beckmann_lacquer}
* }
* *
* Microfacet theory describes rough surfaces as an arrangement of unresolved and * Microfacet theory describes rough surfaces as an arrangement of unresolved and
* ideally specular facets, whose normal directions are given by a specially * ideally specular facets, whose normal directions are given by a specially
@ -85,30 +81,37 @@ MTS_NAMESPACE_BEGIN
* possible to reproduce the important off-specular reflections peaks observed * possible to reproduce the important off-specular reflections peaks observed
* in real-world measurements of such materials. * in real-world measurements of such materials.
* *
* \renderings{
* \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann}
* \rendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx}
* }
*
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin * This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
* \pluginref{plastic}. For very low values of $\alpha$, the two will * \pluginref{plastic}. For very low values of $\alpha$, the two will
* be very similar, though scenes using this plugin will take longer to render * be identical, though scenes using this plugin will take longer to render
* due to the additional computational burden of tracking surface roughness. * due to the additional computational burden of tracking surface roughness.
* *
* The model uses the integrated specular reflectance to interpolate between the * For convenience, this model allows to specify IOR values either numerically,
* specular and diffuse components (i.e. any light that is not scattered * or based on a list of known materials (see \tblref{dielectric-iors} on
* specularly is assumed to contribute to the diffuse component). * \tblpage{dielectric-iors} for an overview).
* Similar to the \pluginref{dielectric} plugin, IOR values
* can either be specified numerically, or based on a list of known materials
* (see \tblref{dielectric-iors} for an overview).
*
* 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
* distributions. Note that the choices are a bit more restricted here---in
* comparison to other rough scattering models in Mitsuba,
* the roughness cannot be textured, and anisotropic microfacet
* distributions are not allowed.
*
* When no parameters are given, the plugin activates the defaults, * When no parameters are given, the plugin activates the defaults,
* which describe a white polypropylene plastic material with a light amount * which describe a white polypropylene plastic material with a light amount
* of roughness modeled using the Beckmann distribution. * of roughness modeled using the Beckmann distribution.
* *
* Like the \pluginref{plastic} material, this model internally simulates the
* interaction of light with a diffuse base surface coated by a thin dielectric
* layer (where the coating layer is now \emph{rough}). This is a convenient
* abstraction rather than a restriction. In other words, there are many
* materials that can be rendered with this model, even if they might not not
* fit this description perfectly well.
*
* The simplicity of this setup makes it possible to account for interesting
* nonlinear effects due to internal scattering, which is controlled by
* the \texttt{nonlinear} parameter. For more details, please refer to the description
* of this parameter given in the the \pluginref{plastic} plugin section
* on \pluginpage{plastic}.
*
*
* To get an intuition about the effect of the surface roughness * To get an intuition about the effect of the surface roughness
* parameter $\alpha$, consider the following approximate differentiation: * parameter $\alpha$, consider the following approximate differentiation:
* a value of $\alpha=0.001-0.01$ corresponds to a material * a value of $\alpha=0.001-0.01$ corresponds to a material
@ -116,22 +119,73 @@ MTS_NAMESPACE_BEGIN
* 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.
* *
* When rendering with the Phong microfacet * \renderings{
* distributions, a conversion is used to turn the specified * \medrendering{Diffuse textured rendering}{bsdf_plastic_diffuse}
* $\alpha$ roughness value into the Phong exponent. * \medrendering{Textured rough plastic model and \code{nonlinear=false}}{bsdf_roughplastic_preserve}
* This is done in a way, such that the different * \medrendering{Textured rough plastic model and \code{nonlinear=true}}{bsdf_roughplastic_nopreserve}
* distributions all produce a similar appearance for * \caption{
* the same value of $\alpha$. * \label{fig:plastic-nonlinear}
* \begin{xml}[caption={A material definition for rough, black laquer.}, label=lst:roughplastic-lacquer] * When asked to do so, this model can account for subtle nonlinear color shifts due
* to internal scattering processes. The above images show a textured
* object first rendered using \pluginref{diffuse}, then
* \pluginref{roughplastic} with the default parameters, and finally using
* \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
* is modeled e.g. using a bump map.}{bsdf_roughplastic_roughtex2}\vspace{-3mm}
* \caption{
* The ability to texture the roughness parameter makes it possible
* to render materials with a structured finish, as well as
* ``smudgy'' objects.
* }
* }
* \vspace{2mm}
* \begin{xml}[caption={A material definition for black plastic material with
* a spatially varying roughness.},
* label=lst:roughplastic-varyingalpha]
* <bsdf type="roughplastic"> * <bsdf type="roughplastic">
* <string name="distribution" value="beckmann"/> * <string name="distribution" value="beckmann"/>
* <float name="alpha" value="0.05"/>
* <float name="intIOR" value="1.61"/> * <float name="intIOR" value="1.61"/>
* <spectrum name="diffuseReflectance" value="0"/> * <spectrum name="diffuseReflectance" value="0"/>
* <!-- Fetch roughness values from a texture and slightly reduce them -->
* <texture type="scale" name="alpha">
* <texture name="alpha" type="bitmap">
* <string name="filename" value="bump.png"/>
* </texture>
* <float name="scale" value="0.6"/>
* </texture>
* </bsdf> * </bsdf>
* \end{xml} * \end{xml}
* *
* \subsubsection*{Technical details}
* The implementation of this model is partly based on the paper ``Microfacet
* Models for Refraction through Rough Surfaces'' by Walter et al.
* \cite{Walter07Microfacet}. Several different types of microfacet
* distributions are supported. Note that the choices are slightly more
* restricted here---in comparison to other rough scattering models in
* Mitsuba, anisotropic distributions are not allowed.
*
* The implementation of this model makes heavy use of a \emph{rough
* Fresnel transmittance} function, which is a generalization of the
* usual Fresnel transmittion coefficient to microfacet surfaces. Unfortunately,
* this function is normally prohibitively expensive, since each
* evaluation involves a numerical integration over the sphere.
*
* To avoid this performance issue, Mitsuba ships with data files
* (contained in the \code{data/microfacet} directory) containing precomputed
* 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$.
*
*/ */
class RoughPlastic : public BSDF { class RoughPlastic : public BSDF {
public: public:
@ -159,8 +213,11 @@ public:
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_alpha = m_distribution.transformRoughness( m_nonlinear = props.getBoolean("nonlinear", true);
m_alpha = new ConstantFloatTexture(
props.getFloat("alpha", 0.1f)); props.getFloat("alpha", 0.1f));
m_specularSamplingWeight = 0.0f; m_specularSamplingWeight = 0.0f;
} }
@ -171,21 +228,25 @@ public:
); );
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_roughTransmittance = static_cast<CubicSpline *>(manager->getInstance(stream)); m_alpha = static_cast<Texture *>(manager->getInstance(stream));
m_alpha = stream->readFloat();
m_intIOR = stream->readFloat(); m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat(); m_extIOR = stream->readFloat();
m_nonlinear = stream->readBool();
configure(); configure();
} }
void configure() { void configure() {
bool constAlpha = m_alpha->isConstant();
m_components.clear(); m_components.clear();
m_components.push_back(EGlossyReflection | EFrontSide m_components.push_back(EGlossyReflection | EFrontSide
| (m_specularReflectance->isConstant() ? 0 : ESpatiallyVarying)); | ((constAlpha && m_specularReflectance->isConstant())
? 0 : ESpatiallyVarying));
m_components.push_back(EDiffuseReflection | EFrontSide m_components.push_back(EDiffuseReflection | EFrontSide
| (m_diffuseReflectance->isConstant() ? 0 : ESpatiallyVarying)); | ((constAlpha && m_diffuseReflectance->isConstant())
? 0 : ESpatiallyVarying));
/* Verify the input parameters and fix them if necessary */ /* Verify the input parameters and fix them if necessary */
m_specularReflectance = ensureEnergyConservation( m_specularReflectance = ensureEnergyConservation(
@ -199,19 +260,44 @@ public:
sAvg = m_specularReflectance->getAverage().getLuminance(); sAvg = m_specularReflectance->getAverage().getLuminance();
m_specularSamplingWeight = sAvg / (dAvg + sAvg); m_specularSamplingWeight = sAvg / (dAvg + sAvg);
/* Precompute the rough transmittance through the interface */ Float eta = m_intIOR / m_extIOR;
m_roughTransmittance = m_distribution.computeRoughTransmittance( m_invEta2 = 1.0f / (eta*eta);
m_extIOR, m_intIOR, m_alpha, TRANSMITTANCE_PRECOMP_NODES);
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->checkEta(eta);
m_externalRoughTransmittance->checkAlpha(m_alpha->getMinimum().average());
m_externalRoughTransmittance->checkAlpha(m_alpha->getMaximum().average());
/* Reduce the rough transmittance data to a 2D slice */
m_internalRoughTransmittance = m_externalRoughTransmittance->clone();
m_externalRoughTransmittance->setEta(eta);
m_internalRoughTransmittance->setEta(1/eta);
/* If possible, even reduce it to a 1D slice */
if (constAlpha)
m_externalRoughTransmittance->setAlpha(
m_alpha->getValue(Intersection()).average());
}
m_usesRayDifferentials = m_usesRayDifferentials =
m_specularReflectance->usesRayDifferentials() || m_specularReflectance->usesRayDifferentials() ||
m_diffuseReflectance->usesRayDifferentials(); m_diffuseReflectance->usesRayDifferentials() ||
m_alpha->usesRayDifferentials();
BSDF::configure(); BSDF::configure();
} }
Spectrum getDiffuseReflectance(const Intersection &its) const { Spectrum getDiffuseReflectance(const Intersection &its) const {
return m_diffuseReflectance->getValue(its); /* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(its).average();
Float Ftr = m_externalRoughTransmittance->evalDiffuse(alpha);
return m_diffuseReflectance->getValue(its) * Ftr;
} }
/// Helper function: reflect \c wi with respect to a given surface normal /// Helper function: reflect \c wi with respect to a given surface normal
@ -231,19 +317,23 @@ public:
(!hasSpecular && !hasDiffuse)) (!hasSpecular && !hasDiffuse))
return Spectrum(0.0f); return Spectrum(0.0f);
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
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 microsurface normal distribution */
const Float D = m_distribution.eval(H, m_alpha); const Float D = m_distribution.eval(H, alphaT);
/* Fresnel term */ /* Fresnel term */
const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR); const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR);
/* Smith's shadow-masking function */ /* Smith's shadow-masking function */
const Float G = m_distribution.G(bRec.wi, bRec.wo, H, m_alpha); const Float G = m_distribution.G(bRec.wi, bRec.wo, H, alphaT);
/* Calculate the specular reflection component */ /* Calculate the specular reflection component */
Float value = F * D * G / Float value = F * D * G /
@ -252,10 +342,19 @@ public:
result += m_specularReflectance->getValue(bRec.its) * value; result += m_specularReflectance->getValue(bRec.its) * value;
} }
if (hasDiffuse) if (hasDiffuse) {
result += m_diffuseReflectance->getValue(bRec.its) * (INV_PI Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
* m_roughTransmittance->eval(Frame::cosTheta(bRec.wi)) Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
* Frame::cosTheta(bRec.wo)); Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), alpha);
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(alpha);
if (m_nonlinear)
diff /= Spectrum(1.0f) - diff * Fdr;
else
diff /= 1-Fdr;
result += diff * (INV_PI * Frame::cosTheta(bRec.wo) * T12 * T21 * m_invEta2);
}
return result; return result;
} }
@ -272,13 +371,17 @@ public:
(!hasSpecular && !hasDiffuse)) (!hasSpecular && !hasDiffuse))
return 0.0f; return 0.0f;
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
/* 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);
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_roughTransmittance->eval(Frame::cosTheta(bRec.wi)); probSpecular = 1-m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -296,7 +399,7 @@ public:
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 microsurface normal distribution */
const Float prob = m_distribution.pdf(H, m_alpha); const Float prob = m_distribution.pdf(H, alphaT);
result = prob * dwh_dwo * probSpecular; result = prob * dwh_dwo * probSpecular;
} }
@ -319,10 +422,14 @@ public:
bool choseSpecular = hasSpecular; bool choseSpecular = hasSpecular;
Point2 sample(_sample); Point2 sample(_sample);
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
Float probSpecular; Float probSpecular;
if (hasSpecular && hasDiffuse) { if (hasSpecular && hasDiffuse) {
/* Find the probability of sampling the diffuse component */ /* Find the probability of sampling the specular component */
probSpecular = 1 - m_roughTransmittance->eval(Frame::cosTheta(bRec.wi)); probSpecular = 1 - m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -339,7 +446,7 @@ public:
if (choseSpecular) { if (choseSpecular) {
/* Perfect specular reflection based on the microsurface normal */ /* Perfect specular reflection based on the microsurface normal */
Normal m = m_distribution.sample(sample, m_alpha); Normal m = m_distribution.sample(sample, alphaT);
bRec.wo = reflect(bRec.wi, m); bRec.wo = reflect(bRec.wi, m);
bRec.sampledComponent = 0; bRec.sampledComponent = 0;
bRec.sampledType = EGlossyReflection; bRec.sampledType = EGlossyReflection;
@ -373,15 +480,17 @@ public:
stream->writeUInt((uint32_t) m_distribution.getType()); stream->writeUInt((uint32_t) m_distribution.getType());
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_roughTransmittance.get()); manager->serialize(stream, m_alpha.get());
stream->writeFloat(m_alpha);
stream->writeFloat(m_intIOR); stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR); stream->writeFloat(m_extIOR);
stream->writeBool(m_nonlinear);
} }
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 == "specularReflectance") if (name == "alpha")
m_alpha = static_cast<Texture *>(child);
else if (name == "specularReflectance")
m_specularReflectance = static_cast<Texture *>(child); m_specularReflectance = static_cast<Texture *>(child);
else if (name == "diffuseReflectance") else if (name == "diffuseReflectance")
m_diffuseReflectance = static_cast<Texture *>(child); m_diffuseReflectance = static_cast<Texture *>(child);
@ -397,11 +506,12 @@ public:
oss << "RoughPlastic[" << endl oss << "RoughPlastic[" << endl
<< " name = \"" << getName() << "\"," << endl << " name = \"" << getName() << "\"," << endl
<< " distribution = " << m_distribution.toString() << "," << endl << " distribution = " << m_distribution.toString() << "," << endl
<< " alpha = " << m_alpha << "," << 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
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl << " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl << " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " nonlinear = " << m_nonlinear << "," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
<< " extIOR = " << m_extIOR << endl << " extIOR = " << m_extIOR << endl
<< "]"; << "]";
@ -413,11 +523,14 @@ public:
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
MicrofacetDistribution m_distribution; MicrofacetDistribution m_distribution;
ref<CubicSpline> m_roughTransmittance; ref<RoughTransmittance> m_externalRoughTransmittance;
ref<RoughTransmittance> m_internalRoughTransmittance;
ref<Texture> m_diffuseReflectance; ref<Texture> m_diffuseReflectance;
ref<Texture> m_specularReflectance; ref<Texture> m_specularReflectance;
Float m_alpha, m_intIOR, m_extIOR; ref<Texture> m_alpha;
Float m_intIOR, m_extIOR, m_invEta2;
Float m_specularSamplingWeight; Float m_specularSamplingWeight;
bool m_nonlinear;
}; };
/** /**
@ -427,60 +540,61 @@ private:
* it also makes use of the Schlick approximation to the Fresnel * it also makes use of the Schlick approximation to the Fresnel
* reflectance of dielectrics. When the roughness is lower than * reflectance of dielectrics. When the roughness is lower than
* \alpha < 0.2, the shader clamps it to 0.2 so that it will still perform * \alpha < 0.2, the shader clamps it to 0.2 so that it will still perform
* reasonably well in a VPL-based preview. * reasonably well in a VPL-based preview. There is no support for
* non-linear effects due to internal scattering.
*/ */
class RoughPlasticShader : public Shader { class RoughPlasticShader : public Shader {
public: public:
RoughPlasticShader(Renderer *renderer, const Texture *specularReflectance, RoughPlasticShader(Renderer *renderer, const Texture *specularReflectance,
const Texture *diffuseReflectance, Float alpha, Float extIOR, const Texture *diffuseReflectance, const Texture *alpha, Float extIOR,
Float intIOR) : Shader(renderer, EBSDFShader), Float intIOR) : Shader(renderer, EBSDFShader),
m_specularReflectance(specularReflectance), m_specularReflectance(specularReflectance),
m_diffuseReflectance(diffuseReflectance), m_diffuseReflectance(diffuseReflectance),
m_alpha(alpha), m_extIOR(extIOR), m_intIOR(intIOR) { m_alpha(alpha), m_extIOR(extIOR), m_intIOR(intIOR) {
m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get()); m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.get());
m_diffuseReflectanceShader = renderer->registerShaderForResource(m_diffuseReflectance.get()); m_diffuseReflectanceShader = renderer->registerShaderForResource(m_diffuseReflectance.get());
m_alpha = std::max(m_alpha, (Float) 0.2f); m_alphaShader = renderer->registerShaderForResource(m_alpha.get());
m_R0 = fresnel(1.0f, m_extIOR, m_intIOR); m_R0 = fresnel(1.0f, m_extIOR, m_intIOR);
} }
bool isComplete() const { bool isComplete() const {
return m_specularReflectanceShader.get() != NULL && return m_specularReflectanceShader.get() != NULL &&
m_diffuseReflectanceShader.get() != NULL; m_diffuseReflectanceShader.get() != NULL &&
m_alphaShader.get() != NULL;
} }
void putDependencies(std::vector<Shader *> &deps) { void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_specularReflectanceShader.get()); deps.push_back(m_specularReflectanceShader.get());
deps.push_back(m_diffuseReflectanceShader.get()); deps.push_back(m_diffuseReflectanceShader.get());
deps.push_back(m_alphaShader.get());
} }
void cleanup(Renderer *renderer) { void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_specularReflectance.get()); renderer->unregisterShaderForResource(m_specularReflectance.get());
renderer->unregisterShaderForResource(m_diffuseReflectance.get()); renderer->unregisterShaderForResource(m_diffuseReflectance.get());
renderer->unregisterShaderForResource(m_alpha.get());
} }
void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const { void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const {
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); parameterIDs.push_back(program->getParameterID(evalName + "_R0", false));
} }
void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const { void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const {
program->setParameter(parameterIDs[0], m_alpha); program->setParameter(parameterIDs[0], m_R0);
program->setParameter(parameterIDs[1], m_R0);
} }
void generateCode(std::ostringstream &oss, void generateCode(std::ostringstream &oss,
const std::string &evalName, const std::string &evalName,
const std::vector<std::string> &depNames) const { const std::vector<std::string> &depNames) const {
oss << "uniform float " << evalName << "_alpha;" << endl oss << "uniform float " << evalName << "_R0;" << endl
<< "uniform float " << evalName << "_R0;" << endl
<< endl << endl
<< "float " << evalName << "_D(vec3 m) {" << endl << "float " << evalName << "_D(vec3 m, float alpha) {" << endl
<< " float ct = cosTheta(m);" << endl << " float ct = cosTheta(m);" << endl
<< " if (cosTheta(m) <= 0.0)" << endl << " if (cosTheta(m) <= 0.0)" << endl
<< " return 0.0;" << endl << " return 0.0;" << endl
<< " float ex = tanTheta(m) / " << evalName << "_alpha;" << endl << " float ex = tanTheta(m) / alpha;" << endl
<< " return exp(-(ex*ex)) / (pi * " << evalName << "_alpha" << endl << " return exp(-(ex*ex)) / (pi * alpha * alpha *" << endl
<< " * " << evalName << "_alpha * pow(cosTheta(m), 4.0));" << endl << " pow(cosTheta(m), 4.0));" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl << "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl
@ -505,31 +619,34 @@ public:
<< " vec3 H = normalize(wi + wo);" << endl << " vec3 H = normalize(wi + wo);" << endl
<< " vec3 specRef = " << depNames[0] << "(uv);" << endl << " vec3 specRef = " << depNames[0] << "(uv);" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl << " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl
<< " float D = " << evalName << "_D(H)" << ";" << endl << " float alpha = max(0.2, " << depNames[2] << "(uv)[0]);" << endl
<< " float D = " << evalName << "_D(H, alpha)" << ";" << endl
<< " float G = " << evalName << "_G(H, wi, wo);" << endl << " float G = " << evalName << "_G(H, wi, wo);" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl << " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl
<< " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl << " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl
<< " diffuseRef * ((1-F) * cosTheta(wo) * 0.31831);" << endl << " diffuseRef * ((1-F) * cosTheta(wo) * inv_pi);" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl << " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl
<< " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl << " return diffuseRef * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl; << "}" << endl;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
ref<const Texture> m_specularReflectance; ref<const Texture> m_specularReflectance;
ref<const Texture> m_diffuseReflectance; ref<const Texture> m_diffuseReflectance;
ref<const Texture> m_alpha;
ref<Shader> m_specularReflectanceShader; ref<Shader> m_specularReflectanceShader;
ref<Shader> m_diffuseReflectanceShader; ref<Shader> m_diffuseReflectanceShader;
Float m_alpha, m_extIOR, m_intIOR, m_R0; ref<Shader> m_alphaShader;
Float m_extIOR, m_intIOR, m_R0;
}; };
Shader *RoughPlastic::createShader(Renderer *renderer) const { Shader *RoughPlastic::createShader(Renderer *renderer) const {
return new RoughPlasticShader(renderer, return new RoughPlasticShader(renderer,
m_specularReflectance.get(), m_diffuseReflectance.get(), m_specularReflectance.get(), m_diffuseReflectance.get(),
m_alpha, m_extIOR, m_intIOR); m_alpha.get(), m_extIOR, m_intIOR);
} }
MTS_IMPLEMENT_CLASS(RoughPlasticShader, false, Shader) MTS_IMPLEMENT_CLASS(RoughPlasticShader, false, Shader)

View File

@ -21,6 +21,7 @@
#include <mitsuba/core/fstream.h> #include <mitsuba/core/fstream.h>
#include <mitsuba/core/fresolver.h> #include <mitsuba/core/fresolver.h>
#include "microfacet.h"
MTS_NAMESPACE_BEGIN MTS_NAMESPACE_BEGIN
@ -40,7 +41,7 @@ MTS_NAMESPACE_BEGIN
* in every transmittance evaluation, this function is usually prohibitively * in every transmittance evaluation, this function is usually prohibitively
* expensive. That is the motivation for this class, which instead performs * expensive. That is the motivation for this class, which instead performs
* lookups into an extensive precomputed three-dimensional table containing * lookups into an extensive precomputed three-dimensional table containing
* regularly spaced evaluations of this function. The lookups are combined * appropriately spaced evaluations of this function. The lookups are combined
* using tricubic interpolation (more specifically, Catmull-Rom splines). * using tricubic interpolation (more specifically, Catmull-Rom splines).
* *
* The 3D array is parameterized in a way so that the transmittance * The 3D array is parameterized in a way so that the transmittance
@ -62,16 +63,26 @@ MTS_NAMESPACE_BEGIN
* rough transmittance, which is defined as a cosine-weighted integral * rough transmittance, which is defined as a cosine-weighted integral
* of the rough transmittance over the incident hemisphere. * of the rough transmittance over the incident hemisphere.
*/ */
class RoughTransmittance { class RoughTransmittance : public Object {
public: public:
/** /**
* \brief Load a rough transmittance data file from disk * \brief Load a rough transmittance data file from disk
* *
* \param name * \param type
* Denotes the name of a microfacet distribution, * Denotes the type of a microfacet distribution,
* i.e. 'beckmann' or 'ggx' * i.e. Beckmann or GGX
*/ */
RoughTransmittance(const std::string &name) : m_trans(NULL), m_diffTrans(NULL) { RoughTransmittance(MicrofacetDistribution::EType type) : m_trans(NULL), m_diffTrans(NULL) {
std::string name;
switch (type) {
case MicrofacetDistribution::EBeckmann: name = "beckmann"; break;
case MicrofacetDistribution::EPhong: name = "phong"; break;
case MicrofacetDistribution::EGGX: name = "ggx"; break;
default:
SLog(EError, "RoughTransmittance: unsupported distribution type!");
}
/* Resolve the precomputed data file */ /* Resolve the precomputed data file */
fs::path sourceFile = Thread::getThread()->getFileResolver()->resolve( fs::path sourceFile = Thread::getThread()->getFileResolver()->resolve(
formatString("data/microfacet/%s.dat", name.c_str())); formatString("data/microfacet/%s.dat", name.c_str()));
@ -91,15 +102,17 @@ public:
m_alphaSamples = fstream->readSize(); m_alphaSamples = fstream->readSize();
m_thetaSamples = fstream->readSize(); m_thetaSamples = fstream->readSize();
SLog(EInfo, "Loading " SIZE_T_FMT "x" SIZE_T_FMT "x" SIZE_T_FMT m_transSize = 2 * m_etaSamples * m_alphaSamples * m_thetaSamples;
" rough transmittance samples from \"%s\"", 2*m_etaSamples, m_diffTransSize = 2 * m_etaSamples * m_alphaSamples;
m_alphaSamples, m_thetaSamples, sourceFile.file_string().c_str());
size_t transSize = 2 * m_etaSamples * m_alphaSamples * m_thetaSamples; SLog(EDebug, "Loading " SIZE_T_FMT "x" SIZE_T_FMT "x" SIZE_T_FMT
size_t diffTransSize = 2 * m_etaSamples * m_alphaSamples; " (%s) rough transmittance samples from \"%s\"", 2*m_etaSamples,
m_alphaSamples, m_thetaSamples,
memString((m_transSize + m_diffTransSize) * sizeof(float)).c_str(),
sourceFile.file_string().c_str());
m_trans = new Float[transSize]; m_trans = new Float[m_transSize];
m_diffTrans = new Float[diffTransSize]; m_diffTrans = new Float[m_diffTransSize];
m_etaFixed = false; m_etaFixed = false;
m_alphaFixed = false; m_alphaFixed = false;
@ -108,12 +121,12 @@ public:
m_alphaMin = (Float) fstream->readSingle(); m_alphaMin = (Float) fstream->readSingle();
m_alphaMax = (Float) fstream->readSingle(); m_alphaMax = (Float) fstream->readSingle();
SLog(EInfo, "Precomputed data is available for the IOR range " SLog(EDebug, "Precomputed data is available for the IOR range "
"[%.4f, %.1f] and roughness range [%.4f, %.1f]", m_etaMin, "[%.4f, %.1f] and roughness range [%.4f, %.1f]", m_etaMin,
m_etaMax, m_alphaMin, m_alphaMax); m_etaMax, m_alphaMin, m_alphaMax);
float *temp = new float[transSize + diffTransSize]; float *temp = new float[m_transSize + m_diffTransSize];
fstream->readSingleArray(temp, transSize + diffTransSize); fstream->readSingleArray(temp, m_transSize + m_diffTransSize);
float *ptr = temp; float *ptr = temp;
size_t fdrEntry = 0, dataEntry = 0; size_t fdrEntry = 0, dataEntry = 0;
@ -130,7 +143,7 @@ public:
} }
/// Release all memory /// Release all memory
~RoughTransmittance() { virtual ~RoughTransmittance() {
if (m_trans) if (m_trans)
delete[] m_trans; delete[] m_trans;
if (m_diffTrans) if (m_diffTrans)
@ -154,14 +167,14 @@ public:
* \brief Evaluate the rough transmittance for a given index of refraction, * \brief Evaluate the rough transmittance for a given index of refraction,
* roughness, and angle of incidence. * roughness, and angle of incidence.
* *
* \param eta
* Relative index of refraction
* \param alpha
* Roughness parameter
* \param cosTheta * \param cosTheta
* Cosine of the angle of incidence * Cosine of the angle of incidence
* \param alpha
* Roughness parameter
* \param eta
* Relative index of refraction
*/ */
Float eval(Float eta, Float alpha, Float cosTheta) const { Float eval(Float cosTheta, Float alpha = 0, Float eta = 0) const {
Float warpedCosTheta = std::pow(std::abs(cosTheta), 0.25f), Float warpedCosTheta = std::pow(std::abs(cosTheta), 0.25f),
result; result;
@ -223,7 +236,7 @@ public:
* \param alpha * \param alpha
* Roughness parameter * Roughness parameter
*/ */
Float evalDiffuse(Float eta, Float alpha) const { Float evalDiffuse(Float alpha = 0, Float eta = 0) const {
Float result; Float result;
if (m_alphaFixed && m_etaFixed) { if (m_alphaFixed && m_etaFixed) {
@ -269,6 +282,12 @@ public:
if (m_etaFixed) if (m_etaFixed)
return; return;
m_transSize = m_alphaSamples * m_thetaSamples;
m_diffTransSize = m_alphaSamples;
SLog(EDebug, "Reducing dimension from 3D to 2D (%s), eta = %f",
memString((m_transSize + m_diffTransSize) * sizeof(Float)).c_str(), eta);
Float *trans = m_trans, Float *trans = m_trans,
*diffTrans = m_diffTrans; *diffTrans = m_diffTrans;
@ -286,8 +305,8 @@ public:
Float warpedEta = std::pow((eta - m_etaMin) Float warpedEta = std::pow((eta - m_etaMin)
/ (m_etaMax-m_etaMin), 0.25f); / (m_etaMax-m_etaMin), 0.25f);
Float *newTrans = new Float[m_alphaSamples*m_thetaSamples]; Float *newTrans = new Float[m_transSize];
Float *newDiffTrans = new Float[m_alphaSamples]; Float *newDiffTrans = new Float[m_diffTransSize];
Float dAlpha = 1.0f / (m_alphaSamples - 1), Float dAlpha = 1.0f / (m_alphaSamples - 1),
dTheta = 1.0f / (m_thetaSamples - 1); dTheta = 1.0f / (m_thetaSamples - 1);
@ -325,11 +344,17 @@ public:
if (m_alphaFixed) if (m_alphaFixed)
return; return;
m_transSize = m_thetaSamples;
m_diffTransSize = 1;
SLog(EDebug, "Reducing dimension from 2D to 1D (%s), alpha = %f",
memString((m_transSize + m_diffTransSize) * sizeof(Float)).c_str(), alpha);
Float warpedAlpha = std::pow((alpha - m_alphaMin) Float warpedAlpha = std::pow((alpha - m_alphaMin)
/ (m_alphaMax-m_alphaMin), 0.25f); / (m_alphaMax-m_alphaMin), 0.25f);
Float *newTrans = new Float[m_thetaSamples]; Float *newTrans = new Float[m_transSize];
Float *newDiffTrans = new Float[1]; Float *newDiffTrans = new Float[m_diffTransSize];
Float dTheta = 1.0f / (m_thetaSamples - 1); Float dTheta = 1.0f / (m_thetaSamples - 1);
@ -349,6 +374,49 @@ public:
m_diffTrans = newDiffTrans; m_diffTrans = newDiffTrans;
m_alphaFixed = true; m_alphaFixed = true;
} }
void checkAlpha(Float alpha) {
if (alpha < m_alphaMin || alpha > m_alphaMax) {
SLog(EError, "Error: the requested roughness value alpha=%f is"
" outside of the supported range [%f, %f]! Please scale "
" your roughness value/texture to lie within this range.",
alpha, m_alphaMin, m_alphaMax);
}
}
void checkEta(Float eta) {
if (eta < 1)
eta = 1/eta;
if (eta < m_etaMin || eta > m_etaMax)
SLog(EError, "Error: the requested relative index of refraction "
"eta=%f is outside of the supported range [%f, %f]! Please "
"update your scene so that it uses realistic IOR values.",
eta, m_etaMin, m_etaMax);
}
/// Create a deep copy of the current instance
ref<RoughTransmittance> clone() const {
RoughTransmittance *result = new RoughTransmittance();
result->m_name = m_name;
result->m_etaSamples = m_etaSamples;
result->m_alphaSamples = m_alphaSamples;
result->m_thetaSamples = m_thetaSamples;
result->m_etaFixed = m_etaFixed;
result->m_alphaFixed = m_alphaFixed;
result->m_etaMin = m_etaMin;
result->m_etaMax = m_etaMax;
result->m_alphaMin = m_alphaMin;
result->m_alphaMax = m_alphaMax;
result->m_transSize = m_transSize;
result->m_diffTransSize = m_diffTransSize;
result->m_trans = new Float[m_transSize];
result->m_diffTrans = new Float[m_diffTransSize];
memcpy(result->m_trans, m_trans, m_transSize * sizeof(Float));
memcpy(result->m_diffTrans, m_diffTrans, m_diffTransSize * sizeof(Float));
return result;
}
protected:
inline RoughTransmittance() { }
protected: protected:
std::string m_name; std::string m_name;
size_t m_etaSamples; size_t m_etaSamples;
@ -358,6 +426,8 @@ protected:
bool m_alphaFixed; bool m_alphaFixed;
Float m_etaMin, m_etaMax; Float m_etaMin, m_etaMax;
Float m_alphaMin, m_alphaMax; Float m_alphaMin, m_alphaMax;
size_t m_transSize;
size_t m_diffTransSize;
Float *m_trans, *m_diffTrans; Float *m_trans, *m_diffTrans;
}; };

View File

@ -449,13 +449,13 @@ public:
<< " float factor2 = H.x / alphaU, factor3 = H.y / alphaV;" << endl << " float factor2 = H.x / alphaU, factor3 = H.y / alphaV;" << endl
<< " float exponent = -(factor2*factor2 + factor3*factor3)/(H.z*H.z);" << endl << " float exponent = -(factor2*factor2 + factor3*factor3)/(H.z*H.z);" << endl
<< " float specRef = factor1 * exp(exponent);" << endl << " float specRef = factor1 * exp(exponent);" << endl
<< " return (" << depNames[0] << "(uv) * 0.31831" << endl << " return (" << depNames[0] << "(uv) * inv_pi" << endl
<< " + " << depNames[1] << "(uv) * specRef) * cosTheta(wo);" << endl << " + " << depNames[1] << "(uv) * specRef) * cosTheta(wo);" << endl
<< "}" << endl << "}" << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (wi.z <= 0.0 || wo.z <= 0.0)" << endl << " if (wi.z <= 0.0 || wo.z <= 0.0)" << endl
<< " return vec3(0.0);" << endl << " return vec3(0.0);" << endl
<< " return " << depNames[0] << "(uv) * (0.31831 * cosTheta(wo));" << endl << " return " << depNames[0] << "(uv) * (inv_pi * cosTheta(wo));" << endl
<< "}" << endl; << "}" << endl;
} }

View File

@ -33,7 +33,7 @@ libcore_objects = [
'serialization.cpp', 'sstream.cpp', 'cstream.cpp', 'mstream.cpp', 'serialization.cpp', 'sstream.cpp', 'cstream.cpp', 'mstream.cpp',
'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp', 'wavelet.cpp', 'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp', 'wavelet.cpp',
'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'quad.cpp', 'mmap.cpp', 'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'quad.cpp', 'mmap.cpp',
'chisquare.cpp', 'spline.cpp' 'chisquare.cpp'
] ]
# Add some platform-specific components # Add some platform-specific components

View File

@ -1,114 +0,0 @@
/*
This file is part of Mitsuba, a physically based rendering system.
Copyright (c) 2007-2011 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/spline.h>
MTS_NAMESPACE_BEGIN
CubicSpline::CubicSpline(Stream *stream, InstanceManager *manager) {
size_t size = stream->readSize();
m_x.resize(size); m_y.resize(size); m_deriv.resize(size);
stream->readFloatArray(&m_x[0], size);
stream->readFloatArray(&m_y[0], size);
stream->readFloatArray(&m_deriv[0], size);
}
void CubicSpline::build() {
if (m_x.size() != m_y.size())
Log(EError, "build(): encountered a different number "
" of X and Y components!");
size_t size = m_x.size();
if (size < 3)
Log(EError, "build(): need at least three points!");
Float *temp = new Float[size];
m_deriv.resize(size);
/* Solve a tridiagonal system (based on Numerical Recipes) */
m_deriv[0] = temp[0] = 0.0f;
for(size_t i=1; i<size-1; i++){
Float sig = (m_x[i] - m_x[i-1]) / (m_x[i+1] - m_x[i-1]);
Float invP = 1.0f / (sig * m_deriv[i-1] + 2);
m_deriv[i]=(sig-1) * invP;
temp[i]=(m_y[i+1]-m_y[i]) / (m_x[i+1]-m_x[i])
- (m_y[i]-m_y[i-1]) / (m_x[i]-m_x[i-1]);
temp[i]=(6*temp[i] / (m_x[i+1]-m_x[i-1]) - sig*temp[i-1]) * invP;
}
m_deriv[size-1] = 0.0f;
/* Backsubstitute */
for(ptrdiff_t k=size-2; k>=0; k--)
m_deriv[k] = m_deriv[k] * m_deriv[k+1] + temp[k];
delete[] temp;
}
void CubicSpline::clear() {
m_x.clear();
m_y.clear();
m_deriv.clear();
}
Float CubicSpline::eval(Float x) const {
size_t lo = 0, hi = m_x.size()-1;
/* Binary search */
while (hi-lo > 1){
size_t k = (hi+lo) >> 1;
if (m_x[k] > x)
hi = k;
else
lo = k;
}
const Float h = m_x[hi]-m_x[lo],
invH = 1.0f / h,
a = (m_x[hi]-x) * invH,
b = (x- m_x[lo]) * invH;
return a*m_y[lo] + b*m_y[hi] + (1.0f/6.0f) *
((a*a*a-a)*m_deriv[lo] + (b*b*b-b)*m_deriv[hi]) * (h*h);
}
void CubicSpline::serialize(Stream *stream, InstanceManager *manager) const {
Assert(m_x.size() == m_y.size() && m_x.size() == m_deriv.size());
stream->writeSize(m_x.size());
stream->writeFloatArray(&m_x[0], m_x.size());
stream->writeFloatArray(&m_y[0], m_x.size());
stream->writeFloatArray(&m_deriv[0], m_x.size());
}
std::string CubicSpline::toString() const {
std::ostringstream oss;
Assert(m_x.size() == m_y.size());
oss << "CubicSpline[" << endl
<< " nodeCount = " << m_x.size() << "," << endl
<< " nodes = {" << endl;
for (size_t i=0; i<m_x.size(); ++i) {
oss << " " << m_x[i] << " => " << m_y[i];
if (i+1 < m_x.size())
oss << ",";
oss << endl;
}
oss << " }" << endl
<< "]";
return oss.str();
}
MTS_IMPLEMENT_CLASS_S(CubicSpline, false, SerializableObject)
MTS_NAMESPACE_END

View File

@ -503,6 +503,7 @@ void VPLShaderManager::configure(const VPL &vpl, const BSDF *bsdf,
<< "float sinPhi(vec3 v) { return v.y/sinTheta(v); }" << endl << "float sinPhi(vec3 v) { return v.y/sinTheta(v); }" << endl
<< "float cosPhi(vec3 v) { return v.x/sinTheta(v); }" << endl << "float cosPhi(vec3 v) { return v.x/sinTheta(v); }" << endl
<< "const float pi = 3.141592653589;" << endl << "const float pi = 3.141592653589;" << endl
<< "const float inv_pi = 0.318309886183791;" << endl
<< endl; << endl;
std::string vplEvalName, bsdfEvalName, lumEvalName; std::string vplEvalName, bsdfEvalName, lumEvalName;

View File

@ -98,21 +98,79 @@ MIPMap::MIPMap(int width, int height, Spectrum *pixels,
m_levelHeight= new int[m_levels]; m_levelHeight= new int[m_levels];
m_levelWidth[0] = m_width; m_levelWidth[0] = m_width;
m_levelHeight[0] = m_height; m_levelHeight[0] = m_height;
m_maximum = Spectrum(-std::numeric_limits<Float>::infinity());
m_minimum = Spectrum(std::numeric_limits<Float>::infinity());
m_average = Spectrum(0.0f);
/* Generate the mip-map hierarchy */ if (m_levels > 1) {
for (int i=1; i<m_levels; i++) { /* Generate the mip-map hierarchy */
m_levelWidth[i] = std::max(1, m_levelWidth[i-1]/2); for (int i=1; i<m_levels; i++) {
m_levelHeight[i] = std::max(1, m_levelHeight[i-1]/2); m_levelWidth[i] = std::max(1, m_levelWidth[i-1]/2);
m_pyramid[i] = new Spectrum[m_levelWidth[i] * m_levelHeight[i]]; m_levelHeight[i] = std::max(1, m_levelHeight[i-1]/2);
for (int y = 0; y < m_levelHeight[i]; y++) { m_pyramid[i] = new Spectrum[m_levelWidth[i] * m_levelHeight[i]];
for (int x = 0; x < m_levelWidth[i]; x++) {
m_pyramid[i][x+y*m_levelWidth[i]] = ( if (i == 1) {
getTexel(i-1, 2*x, 2*y) + for (int y = 0; y < m_levelHeight[i]; y++) {
getTexel(i-1, 2*x+1, 2*y) + for (int x = 0; x < m_levelWidth[i]; x++) {
getTexel(i-1, 2*x, 2*y+1) + Spectrum t00 = getTexel(i-1, 2*x, 2*y),
getTexel(i-1, 2*x+1, 2*y+1)) * 0.25f; t10 = getTexel(i-1, 2*x+1, 2*y),
t01 = getTexel(i-1, 2*x, 2*y+1),
t11 = getTexel(i-1, 2*x+1, 2*y+1);
/* Compute minima and maxima while processing level 0 */
for (int k=0; k<SPECTRUM_SAMPLES; ++k) {
m_maximum[k] = std::max(m_maximum[k],
std::max(std::max(t00[k], t10[k]),
std::max(t01[k], t11[k])));
m_minimum[k] = std::min(m_minimum[k],
std::min(std::min(t00[k], t10[k]),
std::min(t01[k], t11[k])));
}
m_pyramid[i][x+y*m_levelWidth[i]] =
(t00 + t10 + t01 + t11) * 0.25f;
}
}
} else {
for (int y = 0; y < m_levelHeight[i]; y++) {
for (int x = 0; x < m_levelWidth[i]; x++) {
m_pyramid[i][x+y*m_levelWidth[i]] = (
getTexel(i-1, 2*x, 2*y) +
getTexel(i-1, 2*x+1, 2*y) +
getTexel(i-1, 2*x, 2*y+1) +
getTexel(i-1, 2*x+1, 2*y+1)) * 0.25f;
}
}
} }
} }
m_average = m_pyramid[m_levels-1][0];
} else {
/* Nearest filtering, no hierarchy needed --
still compute average/min/max values */
int width = m_levelWidth[0], height = m_levelHeight[0];
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
Spectrum value = m_pyramid[0][x + width * y];
/* Compute minima and maxima while processing level 0 */
for (int k=0; k<SPECTRUM_SAMPLES; ++k) {
m_maximum[k] = std::max(m_maximum[k], value[k]);
m_minimum[k] = std::min(m_minimum[k], value[k]);
}
m_average += value;
}
}
m_average /= width*height;
}
if (m_wrapMode == EBlack || m_wrapMode == EWhite) {
Float value = (m_wrapMode == EBlack) ? 0.0f : 1.0f;
for (int k=0; k<3; ++k) {
m_maximum[k] = std::max(m_maximum[k], value);
m_minimum[k] = std::min(m_minimum[k], value);
}
} }
if (m_filterType == EEWA) { if (m_filterType == EEWA) {
@ -134,25 +192,6 @@ MIPMap::~MIPMap() {
delete[] m_pyramid; delete[] m_pyramid;
} }
Spectrum MIPMap::getMaximum() const {
Spectrum max(0.0f);
int height = m_levelHeight[0];
int width = m_levelWidth[0];
Spectrum *pixels = m_pyramid[0];
for (int y=0; y<height; ++y) {
for (int x=0; x<width; ++x) {
Spectrum value = *pixels++;
for (int j=0; j<SPECTRUM_SAMPLES; ++j)
max[j] = std::max(max[j], value[j]);
}
}
if (m_wrapMode == EWhite) {
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
max[i] = std::max(max[i], (Float) 1.0f);
}
return max;
}
ref<MIPMap> MIPMap::fromBitmap(Bitmap *bitmap, EFilterType filterType, ref<MIPMap> MIPMap::fromBitmap(Bitmap *bitmap, EFilterType filterType,
EWrapMode wrapMode, Float maxAnisotropy, EWrapMode wrapMode, Float maxAnisotropy,
Spectrum::EConversionIntent intent) { Spectrum::EConversionIntent intent) {

View File

@ -43,7 +43,7 @@ Shape::~Shape() { }
void Shape::configure() { void Shape::configure() {
if (isLuminaire() && m_bsdf == NULL) { if ((hasSubsurface() || isLuminaire()) && m_bsdf == NULL) {
/* Light source & no BSDF -> set an all-absorbing BSDF to turn /* Light source & no BSDF -> set an all-absorbing BSDF to turn
the shape into an occluder. This is needed for the path the shape into an occluder. This is needed for the path
tracer implementation to work correctly. */ tracer implementation to work correctly. */

View File

@ -47,11 +47,12 @@ MTS_NAMESPACE_BEGIN
* Note that non-uniform scales are not permitted! * Note that non-uniform scales are not permitted!
* \default{none (i.e. object space $=$ world space)} * \default{none (i.e. object space $=$ world space)}
* } * }
* \parameter{recenter}{\Boolean}{
* When set to \code{true}, the geometry will be uniformly scaled
* and shifted to so that its object-space footprint fits into $[-1, 1]^3$.
* }
* } * }
*
* This plugin implements a simple loader for Wavefront OBJ files. It handles
* triangle and quad meshes, vertex normals, and UV coordinates. Due to the
* heavy-weight nature of OBJ files, this loader is usually quite a bit slower
* than the \pluginref{ply} or \pluginref{serialized} plugins.
*/ */
class WavefrontOBJ : public Shape { class WavefrontOBJ : public Shape {
public: public:
@ -103,10 +104,6 @@ public:
*/ */
m_faceNormals = props.getBoolean("faceNormals", false); m_faceNormals = props.getBoolean("faceNormals", false);
/* Re-center & scale all contents to move them into the
AABB [-1, -1, -1]x[1, 1, 1]? */
m_recenter = props.getBoolean("recenter", false);
/* Causes all normals to be flipped */ /* Causes all normals to be flipped */
m_flipNormals = props.getBoolean("flipNormals", false); m_flipNormals = props.getBoolean("flipNormals", false);
@ -360,20 +357,6 @@ public:
std::map<Vertex, int, vertex_key_order> vertexMap; std::map<Vertex, int, vertex_key_order> vertexMap;
std::vector<Vertex> vertexBuffer; std::vector<Vertex> vertexBuffer;
size_t numMerged = 0; size_t numMerged = 0;
Vector translate(0.0f);
Float scale = 0.0f;
if (m_recenter) {
AABB aabb;
for (unsigned int i=0; i<triangles.size(); i++) {
for (unsigned int j=0; j<3; j++) {
unsigned int vertexId = triangles[i].p[j];
aabb.expandBy(vertices.at(vertexId));
}
}
scale = 2/aabb.getExtents()[aabb.getLargestAxis()];
translate = -Vector(aabb.getCenter());
}
/* Collapse the mesh into a more usable form */ /* Collapse the mesh into a more usable form */
Triangle *triangleArray = new Triangle[triangles.size()]; Triangle *triangleArray = new Triangle[triangles.size()];
@ -387,10 +370,7 @@ public:
Vertex vertex; Vertex vertex;
if (m_recenter) vertex.p = objectToWorld(vertices.at(vertexId));
vertex.p = objectToWorld((vertices.at(vertexId) + translate)*scale);
else
vertex.p = objectToWorld(vertices.at(vertexId));
if (hasNormals && normalId >= 0 && normals.at(normalId) != Normal(0.0f)) if (hasNormals && normalId >= 0 && normals.at(normalId) != Normal(0.0f))
vertex.n = normalize(objectToWorld(normals.at(normalId))); vertex.n = normalize(objectToWorld(normals.at(normalId)));
@ -533,7 +513,7 @@ public:
private: private:
std::vector<TriMesh *> m_meshes; std::vector<TriMesh *> m_meshes;
std::map<std::string, BSDF *> m_materials; std::map<std::string, BSDF *> m_materials;
bool m_flipNormals, m_faceNormals, m_recenter; bool m_flipNormals, m_faceNormals;
std::string m_name; std::string m_name;
AABB m_aabb; AABB m_aabb;
}; };

View File

@ -67,8 +67,15 @@ MTS_NAMESPACE_BEGIN
* \rendering{The Stanford bunny loaded with \code{faceNormals=true}. Note * \rendering{The Stanford bunny loaded with \code{faceNormals=true}. Note
* the faceted appearance.}{shape_ply_bunny} * the faceted appearance.}{shape_ply_bunny}
* } * }
* This plugin is based on the library \code{libply} by Ares Lagae * This plugin implements a fast loader for the Stanford PLY format (both
* (\url{http://people.cs.kuleuven.be/~ares.lagae/libply}). * the ASCII and binary format). It is based on the \code{libply} library by
* Ares Lagae (\url{http://people.cs.kuleuven.be/~ares.lagae/libply}).
* The current plugin implementation supports triangle meshes with optional
* UV coordinates, vertex normals, and vertex colors.
*
* When loading meshes that contain vertex colors, note that they need to be
* explicitly referenced in a BSDF using a special texture named
* \pluginref{vertexcolors}.
*/ */
class PLYLoader : public TriMesh { class PLYLoader : public TriMesh {
public: public:

View File

@ -125,9 +125,7 @@ public:
} }
void test01_roughTransmittance() { void test01_roughTransmittance() {
const char *distr = "beckmann"; RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);
RoughTransmittance rtr(distr);
ref<Random> random = new Random(); ref<Random> random = new Random();
for (int i=0; i<50; ++i) { for (int i=0; i<50; ++i) {
@ -139,8 +137,8 @@ public:
eta = 1+1e-5f; eta = 1+1e-5f;
//eta = 1/eta; //eta = 1/eta;
Float refD = computeDiffuseTransmittance(distr, eta, alpha); Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
Float datD = rtr.evalDiffuse(eta, alpha); Float datD = rtr.evalDiffuse(alpha, eta);
cout << "Testing " << i << "/50" << endl; cout << "Testing " << i << "/50" << endl;
if (std::abs(refD-datD) > 1e-3f) { if (std::abs(refD-datD) > 1e-3f) {
@ -164,8 +162,8 @@ public:
eta = 1+1e-5f; eta = 1+1e-5f;
//eta = 1/eta; //eta = 1/eta;
Float ref = computeTransmittance(distr, eta, alpha, cosTheta); Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
Float dat = rtr.eval(eta, alpha, cosTheta); Float dat = rtr.eval(cosTheta, alpha, eta);
if (i % 20 == 0) if (i % 20 == 0)
cout << "Testing " << i << "/1000" << endl; cout << "Testing " << i << "/1000" << endl;
@ -184,9 +182,7 @@ public:
} }
void test02_roughTransmittanceFixedEta() { void test02_roughTransmittanceFixedEta() {
const char *distr = "beckmann"; RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);
RoughTransmittance rtr(distr);
Float eta = 1.5f; Float eta = 1.5f;
rtr.setEta(eta); rtr.setEta(eta);
@ -198,8 +194,8 @@ public:
if (alpha < 1e-5) if (alpha < 1e-5)
alpha = 1e-5f; alpha = 1e-5f;
Float refD = computeDiffuseTransmittance(distr, eta, alpha); Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
Float datD = rtr.evalDiffuse(eta, alpha); Float datD = rtr.evalDiffuse(alpha, eta);
cout << "Testing " << i << "/50" << endl; cout << "Testing " << i << "/50" << endl;
if (std::abs(refD-datD) > 1e-3f) { if (std::abs(refD-datD) > 1e-3f) {
@ -218,8 +214,8 @@ public:
if (alpha < 1e-5) if (alpha < 1e-5)
alpha = 1e-5f; alpha = 1e-5f;
Float ref = computeTransmittance(distr, eta, alpha, cosTheta); Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
Float dat = rtr.eval(eta, alpha, cosTheta); Float dat = rtr.eval(cosTheta, alpha, eta);
if (i % 20 == 0) if (i % 20 == 0)
cout << "Testing " << i << "/1000" << endl; cout << "Testing " << i << "/1000" << endl;
@ -238,19 +234,18 @@ public:
} }
void test03_roughTransmittanceFixedEtaFixedAlpha() { void test03_roughTransmittanceFixedEtaFixedAlpha() {
const char *distr = "beckmann"; ref<Timer> timer = new Timer();
RoughTransmittance rtr(MicrofacetDistribution::EBeckmann);
RoughTransmittance rtr(distr);
Float eta = 1.5f; Float eta = 1.5f;
Float alpha = 0.2f; Float alpha = 0.2f;
rtr.setEta(eta); rtr.setEta(eta);
rtr.setAlpha(alpha); rtr.setAlpha(alpha);
cout << "Loading and projecting took " << timer->getMilliseconds() << " ms" << endl;
ref<Random> random = new Random(); ref<Random> random = new Random();
Float refD = computeDiffuseTransmittance(distr, eta, alpha); Float refD = computeDiffuseTransmittance("beckmann", eta, alpha);
Float datD = rtr.evalDiffuse(eta, alpha); Float datD = rtr.evalDiffuse(alpha, eta);
if (std::abs(refD-datD) > 1e-3f) { if (std::abs(refD-datD) > 1e-3f) {
cout << endl; cout << endl;
@ -264,8 +259,8 @@ public:
if (cosTheta < 1e-5) if (cosTheta < 1e-5)
cosTheta = 1e-5f; cosTheta = 1e-5f;
Float ref = computeTransmittance(distr, eta, alpha, cosTheta); Float ref = computeTransmittance("beckmann", eta, alpha, cosTheta);
Float dat = rtr.eval(eta, alpha, cosTheta); Float dat = rtr.eval(cosTheta, alpha, eta);
if (i % 20 == 0) if (i % 20 == 0)
cout << "Testing " << i << "/1000" << endl; cout << "Testing " << i << "/1000" << endl;

View File

@ -266,8 +266,6 @@ public:
m_mipmap = MIPMap::fromBitmap(corrected, m_filterType, m_mipmap = MIPMap::fromBitmap(corrected, m_filterType,
m_wrapMode, m_maxAnisotropy); m_wrapMode, m_maxAnisotropy);
m_average = m_mipmap->triangle(m_mipmap->getLevels()-1, 0, 0);
m_maximum = m_mipmap->getMaximum();
} }
void serialize(Stream *stream, InstanceManager *manager) const { void serialize(Stream *stream, InstanceManager *manager) const {
@ -300,11 +298,15 @@ public:
} }
Spectrum getAverage() const { Spectrum getAverage() const {
return m_average; return m_mipmap->getAverage();
} }
Spectrum getMaximum() const { Spectrum getMaximum() const {
return m_maximum; return m_mipmap->getMaximum();
}
Spectrum getMinimum() const {
return m_mipmap->getMinimum();
} }
bool isConstant() const { bool isConstant() const {
@ -347,10 +349,9 @@ protected:
fs::path m_filename; fs::path m_filename;
Bitmap::EFileFormat m_format; Bitmap::EFileFormat m_format;
MIPMap::EFilterType m_filterType; MIPMap::EFilterType m_filterType;
Spectrum m_average, m_maximum;
Float m_gamma;
MIPMap::EWrapMode m_wrapMode; MIPMap::EWrapMode m_wrapMode;
Float m_maxAnisotropy; Float m_maxAnisotropy;
Float m_gamma;
int m_bpp; int m_bpp;
}; };

View File

@ -75,12 +75,22 @@ public:
return false; return false;
} }
Spectrum getAverage() const { Spectrum getMaximum() const {
return m_color1 * .5f; Spectrum max;
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
max[i] = std::max(m_color0[i], m_color1[i]);
return max;
} }
Spectrum getMaximum() const { Spectrum getMinimum() const {
return m_color0; Spectrum min;
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
min[i] = std::min(m_color0[i], m_color1[i]);
return min;
}
Spectrum getAverage() const {
return (m_color0 + m_color1) * 0.5f;
} }
bool isConstant() const { bool isConstant() const {

View File

@ -68,13 +68,13 @@ public:
} }
inline Spectrum getValue(const Point2 &uv) const { inline Spectrum getValue(const Point2 &uv) const {
Float x = uv.x - (int) uv.x; Float x = uv.x - floorToInt(uv.x);
Float y = uv.y - (int) uv.y; Float y = uv.y - floorToInt(uv.y);
if (x > .5) if (x > .5)
x-=1; x -= 1;
if (y > .5) if (y > .5)
y-=1; y -= 1;
if (std::abs(x) < m_lineWidth || std::abs(y) < m_lineWidth) if (std::abs(x) < m_lineWidth || std::abs(y) < m_lineWidth)
return m_color1; return m_color1;
@ -92,11 +92,24 @@ public:
} }
Spectrum getMaximum() const { Spectrum getMaximum() const {
return m_color0; Spectrum max;
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
max[i] = std::max(m_color0[i], m_color1[i]);
return max;
}
Spectrum getMinimum() const {
Spectrum min;
for (int i=0; i<SPECTRUM_SAMPLES; ++i)
min[i] = std::min(m_color0[i], m_color1[i]);
return min;
} }
Spectrum getAverage() const { Spectrum getAverage() const {
return m_color0; // that's not quite right Float interiorWidth = std::max((Float) 0.0f, 1-2*m_lineWidth),
interiorArea = interiorWidth * interiorWidth,
lineArea = 1 - interiorArea;
return m_color1 * lineArea + m_color0 * interiorArea;
} }
bool isConstant() const { bool isConstant() const {

View File

@ -38,6 +38,8 @@ public:
m_scale = Spectrum(props.getFloat("scale", 1.0f)); m_scale = Spectrum(props.getFloat("scale", 1.0f));
else else
m_scale = props.getSpectrum("scale", Spectrum(1.0f)); m_scale = props.getSpectrum("scale", Spectrum(1.0f));
Assert(m_scale.min() > 0);
} }
ScalingTexture(Stream *stream, InstanceManager *manager) ScalingTexture(Stream *stream, InstanceManager *manager)
@ -70,6 +72,10 @@ public:
return m_nested->getMaximum() * m_scale; return m_nested->getMaximum() * m_scale;
} }
Spectrum getMinimum() const {
return m_nested->getMinimum() * m_scale;
}
bool isConstant() const { bool isConstant() const {
return m_nested->isConstant(); return m_nested->isConstant();
} }

View File

@ -61,10 +61,17 @@ public:
} }
Spectrum getAverage() const { Spectrum getAverage() const {
return Spectrum(1.0f); // For lack of having a better estimate
return Spectrum(0.5f);
}
Spectrum getMinimum() const {
// For lack of having a better estimate
return Spectrum(0.0f);
} }
Spectrum getMaximum() const { Spectrum getMaximum() const {
// For lack of having a better estimate
return Spectrum(1.0f); return Spectrum(1.0f);
} }