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
\renewcommand{\lstlistingname}{Listing}
\newcommand{\figref}[1]{\mbox{Figure~\ref{fig:#1}}}
\newcommand{\secref}[1]{\mbox{Section~\ref{sec:#1}}}
\newcommand{\lstref}[1]{\mbox{Listing~\ref{lst:#1}}}
\newcommand{\tblref}[1]{\mbox{Table~\ref{tbl:#1}}}
\newcommand{\figref}[1]{\mbox{\hyperref[fig:#1]{Figure~\ref*{fig:#1}}}}
\newcommand{\figpage}[1]{\hyperref[fig:#1]{page \pageref*{fig:#1}}}
\newcommand{\subfigref}[2]{\mbox{\hyperref[fig:#1]{Figure~\ref*{fig:#1}#2}}}
\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}}
% Macros that are used in the plugin documentation
\newcommand{\plugin}[2]{\newpage\subsubsection{#2 (\texttt{#1})}\label{plg:#1}}
\newcommand{\pluginref}[1]{\texttt{\hyperref[plg:#1]{#1}}}
\newcommand{\pluginpage}[1]{\hyperref[plg:#1]{page \pageref*{plg:#1}}}
\newcommand{\order}[1]{} % Ignore
\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;
}
inline Spectrum getMinimum() const {
return m_value;
}
inline bool isConstant() const {
return true;
}
@ -105,6 +109,10 @@ public:
return Spectrum(m_value);
}
inline Spectrum getMinimum() const {
return Spectrum(m_value);
}
inline bool isConstant() const {
return true;
}
@ -150,8 +158,13 @@ public:
}
inline Spectrum getMaximum() const {
SLog(EError, "SpectrumAdditionTexture::getMaximum() -- information unavailable!");
return Spectrum(0.0f);
// Return a conservative estimate
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 {
@ -202,8 +215,13 @@ public:
}
inline Spectrum getMaximum() const {
SLog(EError, "SpectrumSubtractionTexture::getMaximum() -- information unavailable!");
return Spectrum(0.0f);
// Return a conservative estimate
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 {
@ -255,9 +273,15 @@ public:
}
inline Spectrum getMaximum() const {
// Return a conservative estimate
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 {
return m_a->isConstant() && m_b->isConstant();
}

View File

@ -88,7 +88,13 @@ public:
}
/// 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
Bitmap *getBitmap() const;
@ -130,6 +136,9 @@ private:
EWrapMode m_wrapMode;
Float *m_weightLut;
Float m_maxAnisotropy;
Spectrum m_minimum;
Spectrum m_maximum;
Spectrum m_average;
};
MTS_NAMESPACE_END

View File

@ -38,6 +38,9 @@ public:
/// Return the component-wise average value of the texture over its domain
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
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('hk', ['hk.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
# 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!");
m_nested = static_cast<BSDF *>(child);
} else if (child->getClass()->derivesFrom(MTS_CLASS(Texture)) && name == "sigmaA") {
m_sigmaA = static_cast<Texture *>(m_sigmaA);
m_sigmaA = static_cast<Texture *>(child);
} else {
BSDF::addChild(name, child);
}

View File

@ -33,9 +33,9 @@ MTS_NAMESPACE_BEGIN
* \parameter{k}{\Spectrum}{Imaginary part of the material's index of
* refraction, also known as absorption coefficient.
* \default{based on the value of \texttt{material}}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{
* Optional factor used to modulate the reflectance component
* \default{1.0}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor that can be used to modulate the specular reflection component. Note
* that for physical realism, this parameter should never be touched. \default{1.0}}
* }
* \renderings{
* \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
* 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
* back side, it will be completely black. If this is undesirable,
* consider using the \pluginref{twosided} BRDF adapter plugin.\vspace{4mm}
@ -369,7 +369,7 @@ public:
<< "}" << endl
<< 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;
}
MTS_DECLARE_CLASS()

View File

@ -31,9 +31,11 @@ MTS_NAMESPACE_BEGIN
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor 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
* 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{
@ -72,8 +74,7 @@ MTS_NAMESPACE_BEGIN
* 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
* 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:
* \newpage
* describe a slightly absorbing piece of glass is shown below:
* \begin{xml}[caption=A glass material with absorption (based on the
* Beer-Lambert law). This material can only be used by an integrator
* 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
<< " if (cosTheta(wi) * cosTheta(wo) >= 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
<< "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
<< " if (cosTheta(wi) < 0.0 || cosTheta(wo) < 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
<< "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
<< " vec3 rootExp = sqrt((1.0 - reducedAlbedo) * 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
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl

View File

@ -19,9 +19,7 @@
#if !defined(__MICROFACET_H)
#define __MICROFACET_H
#include <mitsuba/core/quad.h>
#include <mitsuba/core/timer.h>
#include <mitsuba/core/spline.h>
#include <boost/algorithm/string.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 {
switch (m_type) {
case EBeckmann: return "beckmann"; break;
@ -557,24 +508,6 @@ public:
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:
EType m_type;
};

View File

@ -333,13 +333,13 @@ public:
<< " if (alpha > 0.0)" << endl
<< " specRef = pow(alpha, exponent) * " << 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
<< "}" << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (wi.z <= 0.0 || wo.z <= 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;
}

View File

@ -31,14 +31,14 @@ MTS_NAMESPACE_BEGIN
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the specular reflection component. Note that for physical
* realism, this parameter should never be touched. \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{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the diffuse reflection component\default{0.5}}
* \parameter{preserveColors}{\Boolean}{
* Account for color shifts due to internal scattering? See the main text
* for a detailed description.\default{Don't account for them and
* preserve the colors, i.e. \code{true}}
* \parameter{nonlinear}{\Boolean}{
* Account for nonlinear color shifts due to internal scattering? See the
* main text for details.\default{Don't account for them and
* preserve the texture colors, i.e. \code{false}}
* }
* }
*
@ -48,64 +48,26 @@ MTS_NAMESPACE_BEGIN
* {bsdf_plastic_shiny}
* }
*
* This plugin simulates a realistic smooth plastic-like material with internal
* scattering. Internally, this is modeled as a diffuse base layer with a
* dielectric material boundary.
*
* 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.
* \vspace{3mm}
* This plugin describes a smooth plastic-like material with internal scattering.
* It uses the Fresnel reflection and transmission coefficients to provide
* direction-dependent specular and diffuse components.
* Since it is simple, realistic, and fast, this model is often a better choice
* than the \pluginref{phong}, \pluginref{ward}, and \pluginref{roughplastic}
* plugins when rendering smooth plastic-like materials.
*
*
* \renderings{
* \medrendering{Diffuse textured rendering}{bsdf_plastic_diffuse}
* \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).
* For convenience, 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
* \pluginref{coating} plugin to the \pluginref{diffuse} material. The main
* difference is that this plugin is significantly faster, while at the same
* time causing less variance. Furthermore, it accounts for multiple
* interreflections inside the material, which avoids a serious energy
* loss problem of the aforementioned plugin combination.
*
* \vspace{4mm}
* interreflections inside the material (read on for details), which avoids
* a serious energy loss problem of the aforementioned plugin
* combination.
* \newpage
*
* \begin{xml}[caption=A shiny material whose diffuse reflectance is
* specified using sRGB, label=lst:plastic-shiny]
@ -114,6 +76,68 @@ MTS_NAMESPACE_BEGIN
* <float name="intIOR" value="1.9"/>
* </bsdf>
* \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 {
public:
@ -129,7 +153,7 @@ public:
m_diffuseReflectance = new ConstantSpectrumTexture(
props.getSpectrum("diffuseReflectance", Spectrum(0.5f)));
m_preserveColors = props.getBoolean("preserveColors", true);
m_nonlinear = props.getBoolean("nonlinear", false);
m_specularSamplingWeight = 0.0f;
}
@ -138,7 +162,7 @@ public:
: BSDF(stream, manager) {
m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat();
m_preserveColors = stream->readBool();
m_nonlinear = stream->readBool();
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
m_diffuseReflectance = static_cast<Texture *>(manager->getInstance(stream));
configure();
@ -152,7 +176,8 @@ public:
m_diffuseReflectance, "diffuseReflectance", 1.0f);
/* 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
the specular or diffuse components */
@ -161,8 +186,8 @@ public:
m_specularSamplingWeight = sAvg / (dAvg + sAvg);
Float eta = m_extIOR / m_intIOR;
m_eta2 = eta*eta;
Float invEta = m_extIOR / m_intIOR;
m_invEta2 = invEta*invEta;
m_usesRayDifferentials =
m_specularReflectance->usesRayDifferentials() ||
@ -177,9 +202,8 @@ public:
BSDF::configure();
}
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 {
@ -187,7 +211,7 @@ public:
stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR);
stream->writeBool(m_preserveColors);
stream->writeBool(m_nonlinear);
manager->serialize(stream, m_specularReflectance.get());
manager->serialize(stream, m_diffuseReflectance.get());
}
@ -230,14 +254,12 @@ public:
} else if (measure == ESolidAngle && hasDiffuse) {
Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR);
if (hasDiffuse) {
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors)
diff /= 1 - m_fdr;
else
diff /= Spectrum(1) - m_fdr*diff;
return diff * (INV_PI * Frame::cosTheta(bRec.wo) * m_eta2 * (1-Fr) * (1-Fr2));
}
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_nonlinear)
diff /= Spectrum(1) - diff * m_fdrInt;
else
diff /= 1 - m_fdrInt;
return diff * (INV_PI * Frame::cosTheta(bRec.wo) * m_invEta2 * (1-Fr) * (1-Fr2));
}
return Spectrum(0.0f);
@ -306,12 +328,12 @@ public:
Float Fr2 = fresnel(Frame::cosTheta(bRec.wo), m_extIOR, m_intIOR);
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors)
diff /= 1 - m_fdr;
if (m_nonlinear)
diff /= Spectrum(1) - m_fdrInt*diff;
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) {
bRec.sampledComponent = 0;
@ -325,12 +347,12 @@ public:
bRec.sampledType = EDiffuseReflection;
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors)
diff /= 1 - m_fdr;
if (m_nonlinear)
diff /= Spectrum(1) - diff*m_fdrInt;
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);
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors)
diff /= 1 - m_fdr;
if (m_nonlinear)
diff /= Spectrum(1) - diff*m_fdrInt;
else
diff /= Spectrum(1) - m_fdr*diff;
diff /= 1 - m_fdrInt;
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) {
bRec.sampledComponent = 0;
@ -392,12 +414,12 @@ public:
pdf = Frame::cosTheta(bRec.wo) * INV_PI;
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
if (m_preserveColors)
diff /= 1 - m_fdr;
if (m_nonlinear)
diff /= Spectrum(1) - diff*m_fdrInt;
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
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " preserveColors = " << m_preserveColors << "," << endl
<< " nonlinear = " << m_nonlinear << "," << endl
<< " intIOR = " << m_intIOR << "," << endl
<< " extIOR = " << m_extIOR << "," << endl
<< " fdr = " << m_fdr << endl
<< " fdrInt = " << m_fdrInt << "," << endl
<< " fdrExt = " << m_fdrExt << endl
<< "]";
return oss.str();
}
MTS_DECLARE_CLASS()
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_specularReflectance;
Float m_specularSamplingWeight;
bool m_preserveColors;
bool m_nonlinear;
};
/**
@ -513,12 +537,12 @@ public:
<< " float G = " << evalName << "_G(H, wi, wo);" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << 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
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl
<< " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl
<< " return diffuseRef * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl;
}
MTS_DECLARE_CLASS()

View File

@ -25,7 +25,7 @@
MTS_NAMESPACE_BEGIN
/*!\plugin{sssbrdf}{Subsurface scattering BRDF}
/*!\plugin{rmbrdf}{Random medium BRDF}
*
* \parameters{
* \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}}
* \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy
* --- 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.
* \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
* 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
* is assumed to exit the material \emph{directly} at the original point of incidence.
* To account for internal light transport with \emph{different} incident
* and exitant positions, please refer to Sections~\ref{sec:media}
* To simulate actual subsurface scattering, refer to Sections~\ref{sec:media}
* 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
* BSDF for single scattering in an infinitely thick layer together with
* 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}
* and \code{sigmaA}.
*/
class SSSBRDF : public BSDF {
class RandomMediumBRDF : public BSDF {
public:
SSSBRDF(const Properties &props)
RandomMediumBRDF(const Properties &props)
: BSDF(props), m_configured(false) {
Spectrum sigmaS, sigmaA; // ignored here
@ -85,7 +94,7 @@ public:
Properties hgProps("hg");
hgProps.setFloat("g", g);
Float alpha = props.getFloat("alpha", 0.0f);
Float alpha = props.getFloat("alpha", 0.1f);
ref<PhaseFunction> hg = static_cast<PhaseFunction *> (
PluginManager::getInstance()->createObject(
@ -129,7 +138,7 @@ public:
props.markQueried("alpha");
}
SSSBRDF(Stream *stream, InstanceManager *manager)
RandomMediumBRDF(Stream *stream, InstanceManager *manager)
: BSDF(stream, manager), m_configured(true) {
m_coating = 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) {
if (child->getClass()->derivesFrom(MTS_CLASS(Texture))) {
m_hk->addChild(name, child);
m_dipole->addChild(name, child);
if (name == "sigmaS" || name == "sigmaA" || name == "sigmaT" || name == "albedo") {
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 {
BSDF::addChild(name, child);
}
@ -207,7 +222,7 @@ public:
std::string toString() const {
std::ostringstream oss;
oss << "SSSBRDF[" << endl
oss << "RandomMediumBRDF[" << endl
<< " name = \"" << m_name << "\"" << endl
<< " nested = " << indent(m_coating->toString()) << endl
<< "]";
@ -223,6 +238,6 @@ private:
bool m_configured;
};
MTS_IMPLEMENT_CLASS_S(SSSBRDF, false, BSDF)
MTS_EXPORT_PLUGIN(SSSBRDF, "Subsurface scattering BRDF");
MTS_IMPLEMENT_CLASS_S(RandomMediumBRDF, false, BSDF)
MTS_EXPORT_PLUGIN(RandomMediumBRDF, "Random medium BRDF");
MTS_NAMESPACE_END

View File

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

View File

@ -65,8 +65,10 @@ MTS_NAMESPACE_BEGIN
* refraction (the absorption coefficient).
* \default{based on \texttt{material}}}
* \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
* rough conducting materials, such as metals. It can be interpreted as a fancy
* 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
* \lstref{roughconductor-aluminium}}
* {bsdf_roughconductor_anisotropic_aluminium.jpg}
* \vspace{-8mm}
* }
*
* Microfacet theory describes rough
@ -90,7 +91,7 @@ MTS_NAMESPACE_BEGIN
*
* This plugin is essentially the ``roughened'' equivalent of the (smooth) plugin
* \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.
*
* 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,
* and $\alpha=0.3-0.7$ is \emph{extremely} rough (e.g. an etched or ground
* finish). Values significantly above that are probably not too realistic.
* \vspace{-7mm}
* \subsubsection*{Technical details}\vspace{-3mm}
* \vspace{4mm}
* \begin{xml}[caption={A material definition for brushed aluminium}, label=lst:roughconductor-aluminium]
* <bsdf type="roughconductor">
* <string name="material" value="Al"/>
* <string name="distribution" value="as"/>
* <float name="alphaU" value="0.05"/>
* <float name="alphaV" value="0.3"/>
* </bsdf>
* \end{xml}
*
* \subsubsection*{Technical details}
* When rendering with the Ashikhmin-Shirley or Phong microfacet
* distributions, a conversion is used to turn the specified
* $\alpha$ roughness value into the exponents of these distributions.
@ -132,20 +142,10 @@ MTS_NAMESPACE_BEGIN
*
* When using this plugin, you should ideally compile Mitsuba with support for
* 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
* back side, it will be completely black. If this is undesirable,
* 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 {
public:
@ -517,7 +517,7 @@ public:
<< "}" << endl
<< 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;
}
MTS_DECLARE_CLASS()

View File

@ -69,9 +69,11 @@ MTS_NAMESPACE_BEGIN
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor 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
* 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}
*
* 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
* \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.
*
* The implementation is based on the paper ``Microfacet Models

View File

@ -333,13 +333,13 @@ public:
<< " tanBeta = sinTheta(wo) / cosTheta(wo);" << endl
<< " }" << 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
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (cosTheta(wi) <= 0.0 || cosTheta(wo) <= 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;
}

View File

@ -19,12 +19,11 @@
#include <mitsuba/render/bsdf.h>
#include <mitsuba/hw/basicshader.h>
#include "microfacet.h"
#include "rtrans.h"
#include "ior.h"
MTS_NAMESPACE_BEGIN
#define TRANSMITTANCE_PRECOMP_NODES 100
/*!\plugin{roughplastic}{Rough plastic material}
* \order{8}
* \icon{bsdf_roughplastic}
@ -43,10 +42,10 @@ MTS_NAMESPACE_BEGIN
* Due to the underlying microfacet theory,
* the use of this distribution here leads to more realistic
* behavior than the separately available \pluginref{phong} plugin.
* \vspace{-4mm}
* \vspace{-3mm}
* \end{enumerate}
* }
* \parameter{alpha}{\Float}{
* \parameter{alpha}{\Float\Or\Texture}{
* Specifies the roughness of the unresolved surface micro-geometry.
* When the Beckmann distribution is used, this parameter is equal to the
* \emph{root mean square} (RMS) slope of the microfacets.
@ -57,26 +56,23 @@ MTS_NAMESPACE_BEGIN
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{specular\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the specular reflection component. Note that for physical
* realism, this parameter should never be touched. \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{diffuse\showbreak Reflectance}{\Spectrum\Or\Texture}{Optional
* factor used to modulate the diffuse reflection component\default{0.5}}
* }\vspace{-1mm}
* \renderings{
* \rendering{Beckmann, $\alpha=0.1$}{bsdf_roughplastic_beckmann}
* \rendering{GGX, $\alpha=0.3$}{bsdf_roughplastic_ggx}
* }\vspace{-1mm}
* \parameter{nonlinear}{\Boolean}{
* Account for nonlinear color shifts due to internal scattering? See the
* \pluginref{plastic} plugin for details.\default{Don't account for them and
* preserve the texture colors, i.e. \code{false}}
* }
* }
*
* \vspace{3mm}
* This plugin implements a realistic microfacet scattering model for rendering
* 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
* preferred over empirical models like \pluginref{phong} and \pluginref{ward}
* 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
* 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
* 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
* \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.
*
* The model uses the integrated specular reflectance to interpolate between the
* specular and diffuse components (i.e. any light that is not scattered
* specularly is assumed to contribute to the diffuse component).
* 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.
*
* For convenience, this model allows to specify IOR values either numerically,
* or based on a list of known materials (see \tblref{dielectric-iors} on
* \tblpage{dielectric-iors} for an overview).
* When no parameters are given, the plugin activates the defaults,
* which describe a white polypropylene plastic material with a light amount
* 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
* parameter $\alpha$, consider the following approximate differentiation:
* 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,
* 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.
*
* 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$.
* \begin{xml}[caption={A material definition for rough, black laquer.}, label=lst:roughplastic-lacquer]
*
* \renderings{
* \medrendering{Diffuse textured rendering}{bsdf_plastic_diffuse}
* \medrendering{Textured rough plastic model and \code{nonlinear=false}}{bsdf_roughplastic_preserve}
* \medrendering{Textured rough plastic model and \code{nonlinear=true}}{bsdf_roughplastic_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{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">
* <string name="distribution" value="beckmann"/>
* <float name="alpha" value="0.05"/>
* <float name="intIOR" value="1.61"/>
* <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>
* \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 {
public:
@ -159,8 +213,11 @@ public:
Log(EError, "The 'roughplastic' plugin currently does not support "
"anisotropic microfacet distributions!");
m_alpha = m_distribution.transformRoughness(
m_nonlinear = props.getBoolean("nonlinear", true);
m_alpha = new ConstantFloatTexture(
props.getFloat("alpha", 0.1f));
m_specularSamplingWeight = 0.0f;
}
@ -171,21 +228,25 @@ public:
);
m_specularReflectance = static_cast<Texture *>(manager->getInstance(stream));
m_diffuseReflectance = static_cast<Texture *>(manager->getInstance(stream));
m_roughTransmittance = static_cast<CubicSpline *>(manager->getInstance(stream));
m_alpha = stream->readFloat();
m_alpha = static_cast<Texture *>(manager->getInstance(stream));
m_intIOR = stream->readFloat();
m_extIOR = stream->readFloat();
m_nonlinear = stream->readBool();
configure();
}
void configure() {
bool constAlpha = m_alpha->isConstant();
m_components.clear();
m_components.push_back(EGlossyReflection | EFrontSide
| (m_specularReflectance->isConstant() ? 0 : ESpatiallyVarying));
| ((constAlpha && m_specularReflectance->isConstant())
? 0 : ESpatiallyVarying));
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 */
m_specularReflectance = ensureEnergyConservation(
@ -198,20 +259,45 @@ public:
Float dAvg = m_diffuseReflectance->getAverage().getLuminance(),
sAvg = m_specularReflectance->getAverage().getLuminance();
m_specularSamplingWeight = sAvg / (dAvg + sAvg);
Float eta = m_intIOR / m_extIOR;
m_invEta2 = 1.0f / (eta*eta);
/* Precompute the rough transmittance through the interface */
m_roughTransmittance = m_distribution.computeRoughTransmittance(
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_specularReflectance->usesRayDifferentials() ||
m_diffuseReflectance->usesRayDifferentials();
m_diffuseReflectance->usesRayDifferentials() ||
m_alpha->usesRayDifferentials();
BSDF::configure();
}
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
@ -230,6 +316,10 @@ public:
Frame::cosTheta(bRec.wo) <= 0 ||
(!hasSpecular && !hasDiffuse))
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);
if (hasSpecular) {
@ -237,13 +327,13 @@ public:
const Vector H = normalize(bRec.wo+bRec.wi);
/* Evaluate the microsurface normal distribution */
const Float D = m_distribution.eval(H, m_alpha);
const Float D = m_distribution.eval(H, alphaT);
/* Fresnel term */
const Float F = fresnel(dot(bRec.wi, H), m_extIOR, m_intIOR);
/* 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 */
Float value = F * D * G /
@ -252,10 +342,19 @@ public:
result += m_specularReflectance->getValue(bRec.its) * value;
}
if (hasDiffuse)
result += m_diffuseReflectance->getValue(bRec.its) * (INV_PI
* m_roughTransmittance->eval(Frame::cosTheta(bRec.wi))
* Frame::cosTheta(bRec.wo));
if (hasDiffuse) {
Spectrum diff = m_diffuseReflectance->getValue(bRec.its);
Float T12 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
Float T21 = m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wo), alpha);
Float Fdr = 1-m_internalRoughTransmittance->evalDiffuse(alpha);
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;
}
@ -272,13 +371,17 @@ public:
(!hasSpecular && !hasDiffuse))
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 */
const Vector H = normalize(bRec.wo+bRec.wi);
Float probDiffuse, probSpecular;
if (hasSpecular && hasDiffuse) {
/* 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 */
probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -296,7 +399,7 @@ public:
const Float dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H));
/* 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;
}
@ -319,10 +422,14 @@ public:
bool choseSpecular = hasSpecular;
Point2 sample(_sample);
/* Evaluate the roughness texture */
Float alpha = m_alpha->getValue(bRec.its).average();
Float alphaT = m_distribution.transformRoughness(alpha);
Float probSpecular;
if (hasSpecular && hasDiffuse) {
/* Find the probability of sampling the diffuse component */
probSpecular = 1 - m_roughTransmittance->eval(Frame::cosTheta(bRec.wi));
/* Find the probability of sampling the specular component */
probSpecular = 1 - m_externalRoughTransmittance->eval(Frame::cosTheta(bRec.wi), alpha);
/* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -339,7 +446,7 @@ public:
if (choseSpecular) {
/* 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.sampledComponent = 0;
bRec.sampledType = EGlossyReflection;
@ -373,15 +480,17 @@ public:
stream->writeUInt((uint32_t) m_distribution.getType());
manager->serialize(stream, m_specularReflectance.get());
manager->serialize(stream, m_diffuseReflectance.get());
manager->serialize(stream, m_roughTransmittance.get());
stream->writeFloat(m_alpha);
manager->serialize(stream, m_alpha.get());
stream->writeFloat(m_intIOR);
stream->writeFloat(m_extIOR);
stream->writeBool(m_nonlinear);
}
void addChild(const std::string &name, ConfigurableObject *child) {
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);
else if (name == "diffuseReflectance")
m_diffuseReflectance = static_cast<Texture *>(child);
@ -397,11 +506,12 @@ public:
oss << "RoughPlastic[" << endl
<< " name = \"" << getName() << "\"," << endl
<< " distribution = " << m_distribution.toString() << "," << endl
<< " alpha = " << m_alpha << "," << endl
<< " alpha = " << indent(m_alpha->toString()) << "," << endl
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl
<< " diffuseReflectance = " << indent(m_diffuseReflectance->toString()) << "," << endl
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
<< " diffuseSamplingWeight = " << (1-m_specularSamplingWeight) << "," << endl
<< " nonlinear = " << m_nonlinear << "," << endl
<< " intIOR = " << m_intIOR << "," << endl
<< " extIOR = " << m_extIOR << endl
<< "]";
@ -413,11 +523,14 @@ public:
MTS_DECLARE_CLASS()
private:
MicrofacetDistribution m_distribution;
ref<CubicSpline> m_roughTransmittance;
ref<RoughTransmittance> m_externalRoughTransmittance;
ref<RoughTransmittance> m_internalRoughTransmittance;
ref<Texture> m_diffuseReflectance;
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;
bool m_nonlinear;
};
/**
@ -427,60 +540,61 @@ private:
* it also makes use of the Schlick approximation to the Fresnel
* 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
* 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 {
public:
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),
m_specularReflectance(specularReflectance),
m_diffuseReflectance(diffuseReflectance),
m_alpha(alpha), m_extIOR(extIOR), m_intIOR(intIOR) {
m_specularReflectanceShader = renderer->registerShaderForResource(m_specularReflectance.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);
}
bool isComplete() const {
return m_specularReflectanceShader.get() != NULL &&
m_diffuseReflectanceShader.get() != NULL;
m_diffuseReflectanceShader.get() != NULL &&
m_alphaShader.get() != NULL;
}
void putDependencies(std::vector<Shader *> &deps) {
deps.push_back(m_specularReflectanceShader.get());
deps.push_back(m_diffuseReflectanceShader.get());
deps.push_back(m_alphaShader.get());
}
void cleanup(Renderer *renderer) {
renderer->unregisterShaderForResource(m_specularReflectance.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 {
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
parameterIDs.push_back(program->getParameterID(evalName + "_R0", false));
}
void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const {
program->setParameter(parameterIDs[0], m_alpha);
program->setParameter(parameterIDs[1], m_R0);
program->setParameter(parameterIDs[0], m_R0);
}
void generateCode(std::ostringstream &oss,
const std::string &evalName,
const std::vector<std::string> &depNames) const {
oss << "uniform float " << evalName << "_alpha;" << endl
<< "uniform float " << evalName << "_R0;" << endl
oss << "uniform float " << evalName << "_R0;" << endl
<< endl
<< "float " << evalName << "_D(vec3 m) {" << endl
<< "float " << evalName << "_D(vec3 m, float alpha) {" << endl
<< " float ct = cosTheta(m);" << endl
<< " if (cosTheta(m) <= 0.0)" << endl
<< " return 0.0;" << endl
<< " float ex = tanTheta(m) / " << evalName << "_alpha;" << endl
<< " return exp(-(ex*ex)) / (pi * " << evalName << "_alpha" << endl
<< " * " << evalName << "_alpha * pow(cosTheta(m), 4.0));" << endl
<< " float ex = tanTheta(m) / alpha;" << endl
<< " return exp(-(ex*ex)) / (pi * alpha * alpha *" << endl
<< " pow(cosTheta(m), 4.0));" << endl
<< "}" << endl
<< endl
<< "float " << evalName << "_G(vec3 m, vec3 wi, vec3 wo) {" << endl
@ -505,31 +619,34 @@ public:
<< " vec3 H = normalize(wi + wo);" << endl
<< " vec3 specRef = " << depNames[0] << "(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 F = " << evalName << "_schlick(1-dot(wi, H));" << 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
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl
<< " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl
<< " return diffuseRef * inv_pi * cosTheta(wo);"<< endl
<< "}" << endl;
}
MTS_DECLARE_CLASS()
private:
ref<const Texture> m_specularReflectance;
ref<const Texture> m_diffuseReflectance;
ref<const Texture> m_alpha;
ref<Shader> m_specularReflectanceShader;
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 {
return new RoughPlasticShader(renderer,
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)

View File

@ -21,6 +21,7 @@
#include <mitsuba/core/fstream.h>
#include <mitsuba/core/fresolver.h>
#include "microfacet.h"
MTS_NAMESPACE_BEGIN
@ -40,7 +41,7 @@ MTS_NAMESPACE_BEGIN
* in every transmittance evaluation, this function is usually prohibitively
* expensive. That is the motivation for this class, which instead performs
* 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).
*
* 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
* of the rough transmittance over the incident hemisphere.
*/
class RoughTransmittance {
class RoughTransmittance : public Object {
public:
/**
* \brief Load a rough transmittance data file from disk
*
* \param name
* Denotes the name of a microfacet distribution,
* i.e. 'beckmann' or 'ggx'
* \param type
* Denotes the type of a microfacet distribution,
* 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 */
fs::path sourceFile = Thread::getThread()->getFileResolver()->resolve(
formatString("data/microfacet/%s.dat", name.c_str()));
@ -90,16 +101,18 @@ public:
m_etaSamples = fstream->readSize();
m_alphaSamples = fstream->readSize();
m_thetaSamples = fstream->readSize();
m_transSize = 2 * m_etaSamples * m_alphaSamples * m_thetaSamples;
m_diffTransSize = 2 * m_etaSamples * m_alphaSamples;
SLog(EInfo, "Loading " SIZE_T_FMT "x" SIZE_T_FMT "x" SIZE_T_FMT
" rough transmittance samples from \"%s\"", 2*m_etaSamples,
m_alphaSamples, m_thetaSamples, sourceFile.file_string().c_str());
SLog(EDebug, "Loading " SIZE_T_FMT "x" SIZE_T_FMT "x" SIZE_T_FMT
" (%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());
size_t transSize = 2 * m_etaSamples * m_alphaSamples * m_thetaSamples;
size_t diffTransSize = 2 * m_etaSamples * m_alphaSamples;
m_trans = new Float[transSize];
m_diffTrans = new Float[diffTransSize];
m_trans = new Float[m_transSize];
m_diffTrans = new Float[m_diffTransSize];
m_etaFixed = false;
m_alphaFixed = false;
@ -108,12 +121,12 @@ public:
m_alphaMin = (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,
m_etaMax, m_alphaMin, m_alphaMax);
float *temp = new float[transSize + diffTransSize];
fstream->readSingleArray(temp, transSize + diffTransSize);
float *temp = new float[m_transSize + m_diffTransSize];
fstream->readSingleArray(temp, m_transSize + m_diffTransSize);
float *ptr = temp;
size_t fdrEntry = 0, dataEntry = 0;
@ -130,7 +143,7 @@ public:
}
/// Release all memory
~RoughTransmittance() {
virtual ~RoughTransmittance() {
if (m_trans)
delete[] m_trans;
if (m_diffTrans)
@ -154,14 +167,14 @@ public:
* \brief Evaluate the rough transmittance for a given index of refraction,
* roughness, and angle of incidence.
*
* \param eta
* Relative index of refraction
* \param alpha
* Roughness parameter
* \param cosTheta
* 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),
result;
@ -223,7 +236,7 @@ public:
* \param alpha
* Roughness parameter
*/
Float evalDiffuse(Float eta, Float alpha) const {
Float evalDiffuse(Float alpha = 0, Float eta = 0) const {
Float result;
if (m_alphaFixed && m_etaFixed) {
@ -269,6 +282,12 @@ public:
if (m_etaFixed)
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,
*diffTrans = m_diffTrans;
@ -286,8 +305,8 @@ public:
Float warpedEta = std::pow((eta - m_etaMin)
/ (m_etaMax-m_etaMin), 0.25f);
Float *newTrans = new Float[m_alphaSamples*m_thetaSamples];
Float *newDiffTrans = new Float[m_alphaSamples];
Float *newTrans = new Float[m_transSize];
Float *newDiffTrans = new Float[m_diffTransSize];
Float dAlpha = 1.0f / (m_alphaSamples - 1),
dTheta = 1.0f / (m_thetaSamples - 1);
@ -325,11 +344,17 @@ public:
if (m_alphaFixed)
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)
/ (m_alphaMax-m_alphaMin), 0.25f);
Float *newTrans = new Float[m_thetaSamples];
Float *newDiffTrans = new Float[1];
Float *newTrans = new Float[m_transSize];
Float *newDiffTrans = new Float[m_diffTransSize];
Float dTheta = 1.0f / (m_thetaSamples - 1);
@ -349,6 +374,49 @@ public:
m_diffTrans = newDiffTrans;
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:
std::string m_name;
size_t m_etaSamples;
@ -358,6 +426,8 @@ protected:
bool m_alphaFixed;
Float m_etaMin, m_etaMax;
Float m_alphaMin, m_alphaMax;
size_t m_transSize;
size_t m_diffTransSize;
Float *m_trans, *m_diffTrans;
};

View File

@ -449,13 +449,13 @@ public:
<< " float factor2 = H.x / alphaU, factor3 = H.y / alphaV;" << endl
<< " float exponent = -(factor2*factor2 + factor3*factor3)/(H.z*H.z);" << 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
<< "}" << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (wi.z <= 0.0 || wo.z <= 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;
}

View File

@ -33,7 +33,7 @@ libcore_objects = [
'serialization.cpp', 'sstream.cpp', 'cstream.cpp', 'mstream.cpp',
'sched.cpp', 'sched_remote.cpp', 'sshstream.cpp', 'wavelet.cpp',
'zstream.cpp', 'shvector.cpp', 'fresolver.cpp', 'quad.cpp', 'mmap.cpp',
'chisquare.cpp', 'spline.cpp'
'chisquare.cpp'
]
# 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 cosPhi(vec3 v) { return v.x/sinTheta(v); }" << endl
<< "const float pi = 3.141592653589;" << endl
<< "const float inv_pi = 0.318309886183791;" << endl
<< endl;
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_levelWidth[0] = m_width;
m_levelHeight[0] = m_height;
/* Generate the mip-map hierarchy */
for (int i=1; i<m_levels; i++) {
m_levelWidth[i] = std::max(1, m_levelWidth[i-1]/2);
m_levelHeight[i] = std::max(1, m_levelHeight[i-1]/2);
m_pyramid[i] = new Spectrum[m_levelWidth[i] * m_levelHeight[i]];
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_maximum = Spectrum(-std::numeric_limits<Float>::infinity());
m_minimum = Spectrum(std::numeric_limits<Float>::infinity());
m_average = Spectrum(0.0f);
if (m_levels > 1) {
/* Generate the mip-map hierarchy */
for (int i=1; i<m_levels; i++) {
m_levelWidth[i] = std::max(1, m_levelWidth[i-1]/2);
m_levelHeight[i] = std::max(1, m_levelHeight[i-1]/2);
m_pyramid[i] = new Spectrum[m_levelWidth[i] * m_levelHeight[i]];
if (i == 1) {
for (int y = 0; y < m_levelHeight[i]; y++) {
for (int x = 0; x < m_levelWidth[i]; x++) {
Spectrum t00 = getTexel(i-1, 2*x, 2*y),
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) {
@ -134,25 +192,6 @@ MIPMap::~MIPMap() {
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,
EWrapMode wrapMode, Float maxAnisotropy,
Spectrum::EConversionIntent intent) {

View File

@ -43,7 +43,7 @@ Shape::~Shape() { }
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
the shape into an occluder. This is needed for the path
tracer implementation to work correctly. */

View File

@ -47,11 +47,12 @@ MTS_NAMESPACE_BEGIN
* Note that non-uniform scales are not permitted!
* \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 {
public:
@ -103,10 +104,6 @@ public:
*/
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 */
m_flipNormals = props.getBoolean("flipNormals", false);
@ -360,20 +357,6 @@ public:
std::map<Vertex, int, vertex_key_order> vertexMap;
std::vector<Vertex> vertexBuffer;
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 */
Triangle *triangleArray = new Triangle[triangles.size()];
@ -387,10 +370,7 @@ public:
Vertex vertex;
if (m_recenter)
vertex.p = objectToWorld((vertices.at(vertexId) + translate)*scale);
else
vertex.p = objectToWorld(vertices.at(vertexId));
vertex.p = objectToWorld(vertices.at(vertexId));
if (hasNormals && normalId >= 0 && normals.at(normalId) != Normal(0.0f))
vertex.n = normalize(objectToWorld(normals.at(normalId)));
@ -533,7 +513,7 @@ public:
private:
std::vector<TriMesh *> m_meshes;
std::map<std::string, BSDF *> m_materials;
bool m_flipNormals, m_faceNormals, m_recenter;
bool m_flipNormals, m_faceNormals;
std::string m_name;
AABB m_aabb;
};

View File

@ -67,8 +67,15 @@ MTS_NAMESPACE_BEGIN
* \rendering{The Stanford bunny loaded with \code{faceNormals=true}. Note
* the faceted appearance.}{shape_ply_bunny}
* }
* This plugin is based on the library \code{libply} by Ares Lagae
* (\url{http://people.cs.kuleuven.be/~ares.lagae/libply}).
* This plugin implements a fast loader for the Stanford PLY format (both
* 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 {
public:

View File

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

View File

@ -266,8 +266,6 @@ public:
m_mipmap = MIPMap::fromBitmap(corrected, m_filterType,
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 {
@ -300,11 +298,15 @@ public:
}
Spectrum getAverage() const {
return m_average;
return m_mipmap->getAverage();
}
Spectrum getMaximum() const {
return m_maximum;
return m_mipmap->getMaximum();
}
Spectrum getMinimum() const {
return m_mipmap->getMinimum();
}
bool isConstant() const {
@ -347,10 +349,9 @@ protected:
fs::path m_filename;
Bitmap::EFileFormat m_format;
MIPMap::EFilterType m_filterType;
Spectrum m_average, m_maximum;
Float m_gamma;
MIPMap::EWrapMode m_wrapMode;
Float m_maxAnisotropy;
Float m_gamma;
int m_bpp;
};

View File

@ -75,12 +75,22 @@ public:
return false;
}
Spectrum getAverage() const {
return m_color1 * .5f;
}
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 {
return (m_color0 + m_color1) * 0.5f;
}
bool isConstant() const {

View File

@ -68,13 +68,13 @@ public:
}
inline Spectrum getValue(const Point2 &uv) const {
Float x = uv.x - (int) uv.x;
Float y = uv.y - (int) uv.y;
Float x = uv.x - floorToInt(uv.x);
Float y = uv.y - floorToInt(uv.y);
if (x > .5)
x-=1;
x -= 1;
if (y > .5)
y-=1;
y -= 1;
if (std::abs(x) < m_lineWidth || std::abs(y) < m_lineWidth)
return m_color1;
@ -92,13 +92,26 @@ public:
}
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 {
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 {
return false;
}

View File

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

View File

@ -61,10 +61,17 @@ public:
}
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 {
// For lack of having a better estimate
return Spectrum(1.0f);
}