2011-07-05 02:13:36 +08:00
|
|
|
/*
|
|
|
|
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/render/bsdf.h>
|
2011-07-13 07:56:00 +08:00
|
|
|
#include <mitsuba/hw/basicshader.h>
|
|
|
|
#include "ior.h"
|
2011-07-05 02:13:36 +08:00
|
|
|
|
|
|
|
MTS_NAMESPACE_BEGIN
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
/*! \plugin{coating}{Smooth dielectric coating}
|
2011-07-13 05:16:27 +08:00
|
|
|
* \order{9}
|
2011-07-05 02:13:36 +08:00
|
|
|
*
|
|
|
|
* \parameters{
|
2011-07-13 07:56:00 +08:00
|
|
|
* \parameter{intIOR}{\Float\Or\String}{Interior index of refraction specified
|
|
|
|
* numerically or using a known material name. \default{\texttt{bk7} / 1.5046}}
|
|
|
|
* \parameter{extIOR}{\Float\Or\String}{Exterior index of refraction specified
|
|
|
|
* numerically or using a known material name. \default{\texttt{air} / 1.000277}}
|
2011-07-16 00:37:48 +08:00
|
|
|
* \parameter{thickness}{\Float}{Denotes the thickness of the layer (to model absorption --- should be specified in inverse units of \code{sigmaA})\default{1}}
|
2011-07-13 23:40:26 +08:00
|
|
|
* \parameter{sigmaA}{\Spectrum\Or\Texture}{The absorption coefficient of the coating layer. \default{0, i.e. there is no absorption}}
|
2011-07-16 00:37:48 +08:00
|
|
|
* \parameter{\Unnamed}{\BSDF}{A nested BSDF model that should be coated.}
|
2011-07-05 02:13:36 +08:00
|
|
|
* }
|
2011-07-13 11:15:10 +08:00
|
|
|
*
|
|
|
|
* \renderings{
|
2011-07-13 23:40:26 +08:00
|
|
|
* \rendering{Rough copper}
|
2011-07-14 01:47:57 +08:00
|
|
|
* {bsdf_coating_uncoated}
|
|
|
|
* \rendering{The same material coated with a single layer of
|
|
|
|
* clear varnish (see \lstref{coating-roughcopper})}
|
2011-07-13 11:15:10 +08:00
|
|
|
* {bsdf_coating_roughconductor}
|
|
|
|
* }
|
|
|
|
*
|
2011-07-13 23:40:26 +08:00
|
|
|
* This plugin implements a smooth dielectric coating (e.g. a layer of varnish)
|
|
|
|
* in the style of the paper ``Arbitrarily Layered Micro-Facet Surfaces'' by
|
|
|
|
* Weidlich and Wilkie \cite{Weidlich2007Arbitrarily}. Any non-transmissive
|
|
|
|
* BSDF in Mitsuba can be coated using this plugin, and multiple coating layers
|
|
|
|
* can be applied in sequence. This allows designing interesting custom materials
|
|
|
|
* like car paint or glazed metal foil. The coating layer can optionally be
|
|
|
|
* tinted (i.e. filled with an absorbing medium), in which case this model also
|
|
|
|
* accounts for the directionally dependent absorption within the layer.
|
2011-07-13 11:15:10 +08:00
|
|
|
*
|
2011-07-13 23:40:26 +08:00
|
|
|
* Note that the plugin discards illumination that undergoes internal
|
|
|
|
* reflection within the coating. This can lead to a noticeable energy
|
|
|
|
* loss for materials that reflect much of their energy near or below the critical
|
|
|
|
* angle (i.e. diffuse or very rough materials).
|
|
|
|
* Therefore, users are discouraged to use this plugin to coat smooth
|
|
|
|
* diffuse materials, since there is a separately available plugin
|
2011-07-17 21:20:55 +08:00
|
|
|
* named \pluginref{plastic}, which covers the same case and does not
|
2011-07-13 23:40:26 +08:00
|
|
|
* suffer from energy loss.
|
2011-07-13 11:15:10 +08:00
|
|
|
*
|
|
|
|
* Evaluating the internal component of this model entails refracting the
|
|
|
|
* incident and exitant rays through the dielectric interface, followed by
|
|
|
|
* querying the nested material with this modified direction pair. The result
|
2011-07-14 01:47:57 +08:00
|
|
|
* is attenuated by the two Fresnel transmittances and the absorption, if
|
|
|
|
* any.\newpage
|
|
|
|
*
|
|
|
|
* \renderings{
|
|
|
|
* \smallrendering{$\code{thickness}=0$}{bsdf_coating_0}
|
|
|
|
* \smallrendering{$\code{thickness}=1$}{bsdf_coating_1}
|
|
|
|
* \smallrendering{$\code{thickness}=5$}{bsdf_coating_5}
|
|
|
|
* \smallrendering{$\code{thickness}=15$}{bsdf_coating_15}
|
|
|
|
* \caption{The effect of the layer thickness parameter on
|
|
|
|
* a tinted coating ($\code{sigmaT}=(0.1, 0.2, 0.5)$)}
|
|
|
|
* }
|
2011-07-13 11:15:10 +08:00
|
|
|
*
|
|
|
|
* \vspace{4mm}
|
2011-07-05 02:13:36 +08:00
|
|
|
*
|
2011-07-13 23:40:26 +08:00
|
|
|
* \begin{xml}[caption=Rough copper coated with a transparent layer of
|
|
|
|
* varnish, label=lst:coating-roughcopper]
|
2011-07-13 11:15:10 +08:00
|
|
|
* <bsdf type="coating">
|
|
|
|
* <float name="intIOR" value="1.7"/>
|
|
|
|
* <bsdf type="roughconductor">
|
|
|
|
* <string name="material" value="Cu"/>
|
|
|
|
* <float name="alpha" value="0.1"/>
|
|
|
|
* </bsdf>
|
|
|
|
* </bsdf>
|
|
|
|
* \end{xml}
|
2011-07-05 02:13:36 +08:00
|
|
|
*/
|
2011-07-13 07:56:00 +08:00
|
|
|
class SmoothCoating : public BSDF {
|
2011-07-05 02:13:36 +08:00
|
|
|
public:
|
2011-07-13 07:56:00 +08:00
|
|
|
SmoothCoating(const Properties &props)
|
2011-07-05 02:13:36 +08:00
|
|
|
: BSDF(props) {
|
|
|
|
/* Specifies the internal index of refraction at the interface */
|
2011-07-13 07:56:00 +08:00
|
|
|
m_intIOR = lookupIOR(props, "intIOR", "bk7");
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
/* Specifies the external index of refraction at the interface */
|
2011-07-13 07:56:00 +08:00
|
|
|
m_extIOR = lookupIOR(props, "extIOR", "air");
|
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
/* Specifies the layer's thickness using the inverse units of sigmaA */
|
|
|
|
m_thickness = props.getFloat("thickness", 1);
|
2011-07-13 23:40:26 +08:00
|
|
|
|
|
|
|
/* Specifies the absorption within the layer */
|
|
|
|
m_sigmaA = new ConstantSpectrumTexture(
|
|
|
|
props.getSpectrum("sigmaA", Spectrum(0.0f)));
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
SmoothCoating(Stream *stream, InstanceManager *manager)
|
2011-07-05 02:13:36 +08:00
|
|
|
: BSDF(stream, manager) {
|
|
|
|
m_intIOR = stream->readFloat();
|
|
|
|
m_extIOR = stream->readFloat();
|
|
|
|
m_thickness = stream->readFloat();
|
|
|
|
m_nested = static_cast<BSDF *>(manager->getInstance(stream));
|
2011-07-13 11:15:10 +08:00
|
|
|
m_sigmaA = static_cast<Texture *>(manager->getInstance(stream));
|
2011-07-05 02:13:36 +08:00
|
|
|
configure();
|
|
|
|
}
|
|
|
|
|
|
|
|
void configure() {
|
|
|
|
if (!m_nested)
|
|
|
|
Log(EError, "A child BSDF instance is required");
|
|
|
|
if (m_nested->getType() & BSDF::ETransmission)
|
2011-07-13 07:56:00 +08:00
|
|
|
Log(EError, "Tried to put a smooth coating layer on top of a BSDF "
|
2011-07-05 02:13:36 +08:00
|
|
|
"with a transmission component -- this is currently not allowed!");
|
2011-07-13 07:56:00 +08:00
|
|
|
|
2011-07-13 23:40:26 +08:00
|
|
|
unsigned int extraFlags = 0;
|
|
|
|
if (!m_sigmaA->isConstant())
|
|
|
|
extraFlags |= ESpatiallyVarying;
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
m_components.clear();
|
2011-07-13 11:15:10 +08:00
|
|
|
for (int i=0; i<m_nested->getComponentCount(); ++i)
|
2011-07-13 23:40:26 +08:00
|
|
|
m_components.push_back(m_nested->getType(i) | extraFlags);
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
m_components.push_back(EDeltaReflection | EFrontSide);
|
2011-07-13 23:40:26 +08:00
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
m_usesRayDifferentials = m_nested->usesRayDifferentials()
|
2011-07-13 11:15:10 +08:00
|
|
|
|| m_sigmaA->usesRayDifferentials();
|
2011-07-13 07:56:00 +08:00
|
|
|
|
2011-07-14 01:47:57 +08:00
|
|
|
/* Compute weights that further steer samples towards
|
|
|
|
the specular or nested components */
|
|
|
|
Float avgAbsorption = (m_sigmaA->getAverage()
|
|
|
|
*(-2*m_thickness)).exp().average();
|
|
|
|
|
|
|
|
m_specularSamplingWeight = 1.0f / (avgAbsorption + 1.0f);
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
BSDF::configure();
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void serialize(Stream *stream, InstanceManager *manager) const {
|
|
|
|
BSDF::serialize(stream, manager);
|
|
|
|
|
|
|
|
stream->writeFloat(m_intIOR);
|
|
|
|
stream->writeFloat(m_extIOR);
|
|
|
|
stream->writeFloat(m_thickness);
|
|
|
|
manager->serialize(stream, m_nested.get());
|
2011-07-13 11:15:10 +08:00
|
|
|
manager->serialize(stream, m_sigmaA.get());
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
void addChild(const std::string &name, ConfigurableObject *child) {
|
|
|
|
if (child->getClass()->derivesFrom(MTS_CLASS(BSDF))) {
|
|
|
|
if (m_nested != NULL)
|
|
|
|
Log(EError, "Only a single nested BRDF can be added!");
|
|
|
|
m_nested = static_cast<BSDF *>(child);
|
|
|
|
} else {
|
|
|
|
BSDF::addChild(name, child);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reflection in local coordinates
|
|
|
|
inline Vector reflect(const Vector &wi) const {
|
|
|
|
return Vector(-wi.x, -wi.y, wi.z);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Refraction in local coordinates
|
|
|
|
*
|
|
|
|
* To be used when some of the data is already available
|
|
|
|
*/
|
|
|
|
inline Vector refract(const Vector &wi, Float eta, Float cosThetaT) const {
|
|
|
|
return Vector(-eta*wi.x, -eta*wi.y, cosThetaT);
|
|
|
|
}
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
/// Refraction in local coordinates (full version)
|
|
|
|
inline Vector refract(const Vector &wi, Float &F) const {
|
2011-07-05 02:13:36 +08:00
|
|
|
Float cosThetaI = Frame::cosTheta(wi),
|
2011-07-13 11:15:10 +08:00
|
|
|
etaI = m_extIOR, etaT = m_intIOR;
|
2011-07-05 02:13:36 +08:00
|
|
|
|
|
|
|
bool entering = cosThetaI > 0.0f;
|
|
|
|
|
|
|
|
/* Determine the respective indices of refraction */
|
|
|
|
if (!entering)
|
|
|
|
std::swap(etaI, etaT);
|
|
|
|
|
|
|
|
/* Using Snell's law, calculate the squared sine of the
|
|
|
|
angle between the normal and the transmitted ray */
|
|
|
|
Float eta = etaI / etaT,
|
|
|
|
sinThetaTSqr = eta*eta * Frame::sinTheta2(wi);
|
|
|
|
|
|
|
|
if (sinThetaTSqr >= 1.0f) {
|
|
|
|
/* Total internal reflection */
|
2011-07-13 07:56:00 +08:00
|
|
|
F = 1.0f;
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
return Vector(0.0f);
|
|
|
|
} else {
|
2011-07-13 11:15:10 +08:00
|
|
|
Float cosThetaT = std::sqrt(1.0f - sinThetaTSqr);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
/* Compute the Fresnel transmittance */
|
|
|
|
F = fresnelDielectric(std::abs(Frame::cosTheta(wi)),
|
|
|
|
cosThetaT, m_extIOR, m_intIOR);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
return Vector(-eta*wi.x, -eta*wi.y,
|
|
|
|
entering ? -cosThetaT : cosThetaT);
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
Spectrum eval(const BSDFQueryRecord &bRec, EMeasure measure) const {
|
|
|
|
if (Frame::cosTheta(bRec.wi) <= 0 ||
|
|
|
|
Frame::cosTheta(bRec.wo) <= 0)
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
|
|
|
|
bool sampleSpecular = (bRec.typeMask & EDeltaReflection)
|
|
|
|
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1);
|
2011-07-05 02:13:36 +08:00
|
|
|
bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll)
|
2011-07-13 07:56:00 +08:00
|
|
|
&& (bRec.component == -1 || bRec.component < (int) m_components.size()-1);
|
|
|
|
|
|
|
|
if (measure == EDiscrete && sampleSpecular &&
|
|
|
|
std::abs(1-dot(reflect(bRec.wi), bRec.wo)) < Epsilon) {
|
|
|
|
return Spectrum(fresnel(
|
|
|
|
Frame::cosTheta(bRec.wi), m_extIOR, m_intIOR));
|
|
|
|
} else if (sampleNested) {
|
|
|
|
Float R12, R21;
|
|
|
|
BSDFQueryRecord bRec2(bRec);
|
|
|
|
bRec2.wi = -refract(bRec.wi, R12);
|
|
|
|
bRec2.wo = -refract(bRec.wo, R21);
|
|
|
|
|
|
|
|
if (R12 == 1 || R21 == 1) /* Total internal reflection */
|
|
|
|
return Spectrum(0.0f);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 23:40:26 +08:00
|
|
|
Float eta = m_extIOR / m_intIOR;
|
2011-07-13 07:56:00 +08:00
|
|
|
Spectrum result = m_nested->eval(bRec2, measure)
|
2011-07-13 23:40:26 +08:00
|
|
|
* ((1-R12) * (1-R21) * eta * eta);
|
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness;
|
|
|
|
if (!sigmaA.isZero())
|
|
|
|
result *= (-sigmaA *
|
2011-07-13 07:56:00 +08:00
|
|
|
(1/std::abs(Frame::cosTheta(bRec2.wi)) +
|
|
|
|
1/std::abs(Frame::cosTheta(bRec2.wo)))).exp();
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
if (measure == ESolidAngle)
|
2011-07-13 23:40:26 +08:00
|
|
|
result *= Frame::cosTheta(bRec.wo)
|
|
|
|
/ Frame::cosTheta(bRec2.wo);
|
2011-07-13 11:15:10 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Spectrum(0.0f);
|
|
|
|
}
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
Float pdf(const BSDFQueryRecord &bRec, EMeasure measure) const {
|
|
|
|
if (Frame::cosTheta(bRec.wi) <= 0 ||
|
|
|
|
Frame::cosTheta(bRec.wo) <= 0)
|
|
|
|
return 0.0f;
|
|
|
|
|
|
|
|
bool sampleSpecular = (bRec.typeMask & EDeltaReflection)
|
|
|
|
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1);
|
|
|
|
bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll)
|
|
|
|
&& (bRec.component == -1 || bRec.component < (int) m_components.size()-1);
|
2011-07-14 01:47:57 +08:00
|
|
|
|
|
|
|
Float R12;
|
|
|
|
Vector wiPrime = -refract(bRec.wi, R12);
|
|
|
|
|
|
|
|
/* Reallocate samples */
|
|
|
|
Float probSpecular = (R12*m_specularSamplingWeight) /
|
|
|
|
(R12*m_specularSamplingWeight +
|
|
|
|
(1-R12) * (1-m_specularSamplingWeight));
|
2011-07-13 07:56:00 +08:00
|
|
|
|
|
|
|
if (measure == EDiscrete && sampleSpecular &&
|
|
|
|
std::abs(1-dot(reflect(bRec.wi), bRec.wo)) < Epsilon) {
|
2011-07-14 01:47:57 +08:00
|
|
|
return sampleNested ? probSpecular : 1.0f;
|
2011-07-13 07:56:00 +08:00
|
|
|
} else if (sampleNested) {
|
2011-07-14 01:47:57 +08:00
|
|
|
Float R21;
|
2011-07-13 07:56:00 +08:00
|
|
|
BSDFQueryRecord bRec2(bRec);
|
2011-07-14 01:47:57 +08:00
|
|
|
bRec2.wi = wiPrime;
|
2011-07-13 07:56:00 +08:00
|
|
|
bRec2.wo = -refract(bRec.wo, R21);
|
|
|
|
|
|
|
|
if (R12 == 1 || R21 == 1) /* Total internal reflection */
|
|
|
|
return 0.0f;
|
2011-07-13 11:15:10 +08:00
|
|
|
|
2011-07-13 23:40:26 +08:00
|
|
|
Float pdf = m_nested->pdf(bRec2, measure);
|
|
|
|
|
|
|
|
if (measure == ESolidAngle)
|
|
|
|
pdf *= Frame::cosTheta(bRec.wo)
|
|
|
|
/ Frame::cosTheta(bRec2.wo);
|
2011-07-13 07:56:00 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Float eta = m_extIOR / m_intIOR;
|
|
|
|
pdf *= eta * eta;
|
2011-07-13 07:56:00 +08:00
|
|
|
|
2011-07-14 01:47:57 +08:00
|
|
|
return sampleSpecular ? (pdf * (1 - probSpecular)) : pdf;
|
2011-07-05 02:13:36 +08:00
|
|
|
} else {
|
2011-07-13 07:56:00 +08:00
|
|
|
return 0.0f;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Spectrum sample(BSDFQueryRecord &bRec, Float &pdf, const Point2 &_sample) const {
|
|
|
|
bool sampleSpecular = (bRec.typeMask & EDeltaReflection)
|
|
|
|
&& (bRec.component == -1 || bRec.component == (int) m_components.size()-1);
|
|
|
|
bool sampleNested = (bRec.typeMask & m_nested->getType() & BSDF::EAll)
|
|
|
|
&& (bRec.component == -1 || bRec.component < (int) m_components.size()-1);
|
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
if ((!sampleSpecular && !sampleNested) || Frame::cosTheta(bRec.wi) <= 0)
|
2011-07-13 07:56:00 +08:00
|
|
|
return Spectrum(0.0f);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
/* Refract the incident direction and compute the Fresnel reflectance */
|
|
|
|
Float eta = m_extIOR / m_intIOR,
|
|
|
|
sinThetaTSqr = eta*eta * Frame::sinTheta2(bRec.wi),
|
|
|
|
R12, cosThetaT = 0;
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
if (sinThetaTSqr >= 1.0f) {
|
|
|
|
R12 = 1.0f; /* Total internal reflection */
|
|
|
|
} else {
|
|
|
|
cosThetaT = -std::sqrt(1.0f - sinThetaTSqr);
|
|
|
|
R12 = fresnelDielectric(Frame::cosTheta(bRec.wi),
|
|
|
|
-cosThetaT, m_extIOR, m_intIOR);
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
2011-07-14 01:47:57 +08:00
|
|
|
/* Reallocate samples */
|
|
|
|
Float probSpecular = (R12*m_specularSamplingWeight) /
|
|
|
|
(R12*m_specularSamplingWeight +
|
|
|
|
(1-R12) * (1-m_specularSamplingWeight));
|
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
bool choseSpecular = sampleSpecular;
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
Point2 sample(_sample);
|
2011-07-13 11:15:10 +08:00
|
|
|
if (sampleSpecular && sampleNested) {
|
2011-07-14 01:47:57 +08:00
|
|
|
if (sample.x > probSpecular) {
|
|
|
|
sample.x = (sample.x - probSpecular) / (1 - probSpecular);
|
2011-07-13 11:15:10 +08:00
|
|
|
choseSpecular = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (choseSpecular) {
|
|
|
|
bRec.sampledComponent = m_components.size()-1;
|
|
|
|
bRec.sampledType = EDeltaReflection;
|
|
|
|
bRec.wo = reflect(bRec.wi);
|
2011-07-14 01:47:57 +08:00
|
|
|
pdf = sampleNested ? probSpecular : 1.0f;
|
2011-07-13 11:15:10 +08:00
|
|
|
return Spectrum(R12);
|
|
|
|
} else {
|
|
|
|
if (R12 == 1.0f) /* Total internal reflection */
|
|
|
|
return Spectrum(0.0f);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Vector wiBackup = bRec.wi;
|
|
|
|
bRec.wi = -refract(bRec.wi, eta, cosThetaT);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Spectrum result = m_nested->sample(bRec, pdf, sample);
|
|
|
|
if (result.isZero())
|
|
|
|
return Spectrum(0.0f);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Spectrum sigmaA = m_sigmaA->getValue(bRec.its) * m_thickness;
|
|
|
|
if (!sigmaA.isZero())
|
|
|
|
result *= (-sigmaA *
|
|
|
|
(1/std::abs(Frame::cosTheta(bRec.wi)) +
|
|
|
|
1/std::abs(Frame::cosTheta(bRec.wo)))).exp();
|
2011-07-13 07:56:00 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
Float R21, cosThetaWoPrime = Frame::cosTheta(bRec.wo);
|
|
|
|
bRec.wo = refract(-bRec.wo, R21);
|
|
|
|
bRec.wi = wiBackup;
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 11:15:10 +08:00
|
|
|
if (R21 == 1.0f) /* Total internal reflection */
|
|
|
|
return Spectrum(0.0f);
|
2011-07-13 23:40:26 +08:00
|
|
|
bool sampledSA = (BSDF::getMeasure(bRec.sampledType) == ESolidAngle);
|
|
|
|
Float cosRatio = Frame::cosTheta(bRec.wo) / cosThetaWoPrime,
|
|
|
|
commonTerms = (sampledSA ? cosRatio : 1.0f)* eta * eta;
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-14 01:47:57 +08:00
|
|
|
pdf *= (sampleSpecular ? (1 - probSpecular) : 1.0f) * commonTerms;
|
2011-07-13 23:40:26 +08:00
|
|
|
result *= (1 - R12) * (1 - R21) * commonTerms;
|
2011-07-13 11:15:10 +08:00
|
|
|
|
|
|
|
return result;
|
2011-07-13 07:56:00 +08:00
|
|
|
}
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const {
|
|
|
|
Float pdf;
|
|
|
|
Spectrum result = SmoothCoating::sample(bRec, pdf, sample);
|
2011-07-05 02:13:36 +08:00
|
|
|
|
2011-07-13 07:56:00 +08:00
|
|
|
if (result.isZero())
|
2011-07-05 02:13:36 +08:00
|
|
|
return Spectrum(0.0f);
|
2011-07-13 07:56:00 +08:00
|
|
|
else
|
|
|
|
return result / pdf;
|
2011-07-05 02:13:36 +08:00
|
|
|
}
|
|
|
|
|
2011-07-15 16:11:29 +08:00
|
|
|
Shader *createShader(Renderer *renderer) const;
|
|
|
|
|
2011-07-05 02:13:36 +08:00
|
|
|
std::string toString() const {
|
|
|
|
std::ostringstream oss;
|
2011-07-13 07:56:00 +08:00
|
|
|
oss << "SmoothCoating[" << endl
|
|
|
|
<< " name = \"" << getName() << "\"," << endl
|
2011-07-05 02:13:36 +08:00
|
|
|
<< " intIOR = " << m_intIOR << "," << endl
|
|
|
|
<< " extIOR = " << m_extIOR << "," << endl
|
2011-07-14 01:47:57 +08:00
|
|
|
<< " specularSamplingWeight = " << m_specularSamplingWeight << "," << endl
|
2011-07-13 11:15:10 +08:00
|
|
|
<< " sigmaA = " << indent(m_sigmaA->toString()) << "," << endl
|
2011-07-05 02:13:36 +08:00
|
|
|
<< " thickness = " << m_thickness << "," << endl
|
|
|
|
<< " nested = " << indent(m_nested->toString()) << endl
|
|
|
|
<< "]";
|
|
|
|
return oss.str();
|
|
|
|
}
|
|
|
|
|
|
|
|
MTS_DECLARE_CLASS()
|
2011-07-13 23:40:26 +08:00
|
|
|
protected:
|
2011-07-14 01:47:57 +08:00
|
|
|
Float m_specularSamplingWeight;
|
2011-07-05 02:13:36 +08:00
|
|
|
Float m_intIOR, m_extIOR;
|
2011-07-13 11:15:10 +08:00
|
|
|
ref<Texture> m_sigmaA;
|
2011-07-13 07:56:00 +08:00
|
|
|
ref<BSDF> m_nested;
|
2011-07-05 02:13:36 +08:00
|
|
|
Float m_thickness;
|
|
|
|
};
|
|
|
|
|
2011-07-15 16:11:29 +08:00
|
|
|
// ================ Hardware shader implementation ================
|
|
|
|
|
|
|
|
/* Crude GLSL approximation -- just forwards to the nested model */
|
|
|
|
class SmoothCoatingShader : public Shader {
|
|
|
|
public:
|
|
|
|
SmoothCoatingShader(Renderer *renderer, const BSDF *nested)
|
|
|
|
: Shader(renderer, EBSDFShader), m_nested(nested) {
|
|
|
|
m_nestedShader = renderer->registerShaderForResource(m_nested.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
bool isComplete() const {
|
|
|
|
return m_nestedShader.get() != NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cleanup(Renderer *renderer) {
|
|
|
|
renderer->unregisterShaderForResource(m_nested.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
void putDependencies(std::vector<Shader *> &deps) {
|
|
|
|
deps.push_back(m_nestedShader.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
void generateCode(std::ostringstream &oss,
|
|
|
|
const std::string &evalName,
|
|
|
|
const std::vector<std::string> &depNames) const {
|
|
|
|
oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
|
|
|
<< " return " << depNames[0] << "(uv, wi, wo);" << endl
|
|
|
|
<< "}" << endl
|
|
|
|
<< endl
|
|
|
|
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
|
|
|
|
<< " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
|
|
|
|
<< "}" << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
MTS_DECLARE_CLASS()
|
|
|
|
private:
|
|
|
|
ref<const BSDF> m_nested;
|
|
|
|
ref<Shader> m_nestedShader;
|
|
|
|
};
|
|
|
|
|
|
|
|
Shader *SmoothCoating::createShader(Renderer *renderer) const {
|
|
|
|
return new SmoothCoatingShader(renderer, m_nested.get());
|
|
|
|
}
|
|
|
|
|
|
|
|
MTS_IMPLEMENT_CLASS(SmoothCoatingShader, false, Shader)
|
2011-07-13 07:56:00 +08:00
|
|
|
MTS_IMPLEMENT_CLASS_S(SmoothCoating, false, BSDF)
|
2011-07-13 11:15:10 +08:00
|
|
|
MTS_EXPORT_PLUGIN(SmoothCoating, "Smooth dielectric coating");
|
2011-07-05 02:13:36 +08:00
|
|
|
MTS_NAMESPACE_END
|