approximate GLSL shader for the rough coating

metadata
Wenzel Jakob 2011-08-22 01:26:52 -04:00
parent a535eaf3ae
commit 1a5d962c4f
3 changed files with 98 additions and 42 deletions

View File

@ -2,6 +2,17 @@
to be tested for consistency. This is done to be tested for consistency. This is done
using the testcase 'test_chisquare' --> using the testcase 'test_chisquare' -->
<scene version="0.3.0"> <scene version="0.3.0">
<bsdf type="roughcoating">
<!--
<bsdf type="roughconductor">
<string name="distribution" value="beckmann"/>
<float name="alpha" value=".3"/>
</bsdf>
-->
<bsdf type="diffuse"/>
<float name="alpha" value=".3"/>
</bsdf>
<!-- Test the smooth diffuse model --> <!-- Test the smooth diffuse model -->
<bsdf type="diffuse"/> <bsdf type="diffuse"/>

View File

@ -256,7 +256,8 @@ public:
Float probNested, probSpecular; Float probNested, probSpecular;
if (hasSpecular && hasNested) { if (hasSpecular && hasNested) {
/* Find the probability of sampling the specular component */ /* Find the probability of sampling the specular component */
probSpecular = 1-m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi))); probSpecular = 1-m_roughTransmittance->eval(
std::abs(Frame::cosTheta(bRec.wi)));
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -271,7 +272,7 @@ public:
Float result = 0.0f; Float result = 0.0f;
if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) { if (hasSpecular && Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) > 0) {
/* Jacobian of the half-direction transform */ /* Jacobian of the half-direction transform */
const Float dwh_dwo = 1.0f / (4.0f * dot(bRec.wo, H)); const Float dwh_dwo = 1.0f / (4.0f * absDot(bRec.wo, H));
/* Evaluate the microsurface normal distribution */ /* Evaluate the microsurface normal distribution */
const Float prob = m_distribution.pdf(H, m_alpha); const Float prob = m_distribution.pdf(H, m_alpha);
@ -284,15 +285,15 @@ public:
bRecInt.wi = refractTo(EInterior, bRec.wi); bRecInt.wi = refractTo(EInterior, bRec.wi);
bRecInt.wo = refractTo(EInterior, bRec.wo); bRecInt.wo = refractTo(EInterior, bRec.wo);
Float pdf = m_nested->pdf(bRecInt, measure); Float prob = m_nested->pdf(bRecInt, measure);
if (measure == ESolidAngle) { if (measure == ESolidAngle) {
Float eta = m_extIOR / m_intIOR; Float eta = m_extIOR / m_intIOR;
pdf *= eta * eta * Frame::cosTheta(bRec.wo) prob *= eta * eta * Frame::cosTheta(bRec.wo)
/ Frame::cosTheta(bRecInt.wo); / Frame::cosTheta(bRecInt.wo);
} }
result += pdf * probNested; result += prob * probNested;
} }
return result; return result;
@ -310,7 +311,7 @@ public:
Float probSpecular; Float probSpecular;
if (hasSpecular && hasNested) { if (hasSpecular && hasNested) {
/* Find the probability of sampling the diffuse component */ /* Find the probability of sampling the diffuse component */
probSpecular = 1 - m_roughTransmittance->eval(Frame::cosTheta(bRec.wi)); probSpecular = 1 - m_roughTransmittance->eval(std::abs(Frame::cosTheta(bRec.wi)));
/* Reallocate samples */ /* Reallocate samples */
probSpecular = (probSpecular*m_specularSamplingWeight) / probSpecular = (probSpecular*m_specularSamplingWeight) /
@ -336,18 +337,25 @@ public:
if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) <= 0) if (Frame::cosTheta(bRec.wo) * Frame::cosTheta(bRec.wi) <= 0)
return Spectrum(0.0f); return Spectrum(0.0f);
} else { } else {
bRec.sampledComponent = 1; Vector wiBackup = bRec.wi;
bRec.sampledType = EDiffuseReflection; bRec.wi = refractTo(EInterior, bRec.wi);
bRec.wo = squareToHemispherePSA(sample); Spectrum result = m_nested->sample(bRec, _pdf, sample);
bRec.wi = wiBackup;
if (result.isZero())
return Spectrum(0.0f);
bRec.wo = refractTo(EExterior, bRec.wo);
if (bRec.wo.isZero())
return Spectrum(0.0f);
} }
/* Guard against numerical imprecisions */ /* Guard against numerical imprecisions */
_pdf = pdf(bRec, ESolidAngle); EMeasure measure = getMeasure(bRec.sampledType);
_pdf = pdf(bRec, measure);
if (_pdf == 0) if (_pdf == 0)
return Spectrum(0.0f); return Spectrum(0.0f);
else else
return eval(bRec, ESolidAngle) / _pdf; return eval(bRec, measure) / _pdf;
} }
Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const { Spectrum sample(BSDFQueryRecord &bRec, const Point2 &sample) const {
@ -396,7 +404,7 @@ public:
return oss.str(); return oss.str();
} }
// Shader *createShader(Renderer *renderer) const; Shader *createShader(Renderer *renderer) const;
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
@ -408,7 +416,7 @@ private:
Float m_specularSamplingWeight; Float m_specularSamplingWeight;
Float m_thickness; Float m_thickness;
}; };
#if 0
/** /**
* GLSL port of the rough coating shader. This version is much more * GLSL port of the rough coating shader. This version is much more
* approximate -- it only supports the Beckmann distribution, * approximate -- it only supports the Beckmann distribution,
@ -432,6 +440,7 @@ public:
m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get()); m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get());
m_alpha = std::max(m_alpha, (Float) 0.2f); m_alpha = std::max(m_alpha, (Float) 0.2f);
m_R0 = fresnel(1.0f, m_extIOR, m_intIOR); m_R0 = fresnel(1.0f, m_extIOR, m_intIOR);
m_eta = extIOR / intIOR;
} }
bool isComplete() const { bool isComplete() const {
@ -450,20 +459,43 @@ public:
} }
void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const { void resolve(const GPUProgram *program, const std::string &evalName, std::vector<int> &parameterIDs) const {
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
parameterIDs.push_back(program->getParameterID(evalName + "_R0", false)); parameterIDs.push_back(program->getParameterID(evalName + "_R0", false));
parameterIDs.push_back(program->getParameterID(evalName + "_eta", false));
parameterIDs.push_back(program->getParameterID(evalName + "_alpha", false));
} }
void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const { void bind(GPUProgram *program, const std::vector<int> &parameterIDs, int &textureUnitOffset) const {
program->setParameter(parameterIDs[0], m_alpha); program->setParameter(parameterIDs[0], m_R0);
program->setParameter(parameterIDs[1], m_R0); program->setParameter(parameterIDs[1], m_eta);
program->setParameter(parameterIDs[2], m_alpha);
} }
void generateCode(std::ostringstream &oss, void generateCode(std::ostringstream &oss,
const std::string &evalName, const std::string &evalName,
const std::vector<std::string> &depNames) const { const std::vector<std::string> &depNames) const {
oss << "uniform float " << evalName << "_alpha;" << endl oss << "uniform float " << evalName << "_R0;" << endl
<< "uniform float " << evalName << "_R0;" << endl << "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
<< " return " << evalName << "_R0 + (1.0 - " << evalName << "_R0) * ct5;" << endl
<< "}" << endl
<< endl
<< "vec3 " << evalName << "_refract(vec3 wi, out float T) {" << endl
<< " float cosThetaI = cosTheta(wi);" << endl
<< " bool entering = cosThetaI > 0.0;" << endl
<< " float eta = " << evalName << "_eta;" << endl
<< " float sinThetaTSqr = eta * eta * sinTheta2(wi);" << endl
<< " if (sinThetaTSqr >= 1.0) {" << endl
<< " T = 0.0; /* Total internal reflection */" << endl
<< " return vec3(0.0);" << endl
<< " } else {" << endl
<< " float cosThetaT = sqrt(1.0 - sinThetaTSqr);" << endl
<< " T = 1.0 - " << evalName << "_schlick(1.0 - abs(cosThetaI));" << endl
<< " return vec3(eta*wi.x, eta*wi.y, entering ? cosThetaT : -cosThetaT);" << endl
<< " }" << endl
<< "}" << endl
<< endl << endl
<< "float " << evalName << "_D(vec3 m) {" << endl << "float " << evalName << "_D(vec3 m) {" << endl
<< " float ct = cosTheta(m);" << endl << " float ct = cosTheta(m);" << endl
@ -484,37 +516,40 @@ public:
<< " abs(2 * nDotM * cosTheta(wi) / dot(wi, m))));" << endl << " abs(2 * nDotM * cosTheta(wi) / dot(wi, m))));" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< endl
<< "float " << evalName << "_schlick(float ct) {" << endl
<< " float ctSqr = ct*ct, ct5 = ctSqr*ctSqr*ct;" << endl
<< " return " << evalName << "_R0 + (1.0 - " << evalName << "_R0) * ct5;" << endl
<< "}" << endl
<< endl
<< "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " if (cosTheta(wi) <= 0 || cosTheta(wo) <= 0)" << endl << " float T12, T21;" << endl
<< " return vec3(0.0);" << endl << " vec3 wiPrime = " << evalName << "_refract(wi, T12);" << endl
<< " vec3 H = normalize(wi + wo);" << endl << " vec3 woPrime = " << evalName << "_refract(wo, T21);" << endl
<< " vec3 specRef = " << depNames[0] << "(uv);" << endl << " vec3 nested = " << depNames[0] << "(uv, wiPrime, woPrime);" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl << " vec3 sigmaA = " << depNames[1] << "(uv);" << endl
<< " float D = " << evalName << "_D(H)" << ";" << endl << " vec3 result = nested * " << evalName << "_eta * " << evalName << "_eta" << endl
<< " float G = " << evalName << "_G(H, wi, wo);" << endl << " * T12 * T21 * (cosTheta(wi)*cosTheta(wo)) /" << endl
<< " float F = " << evalName << "_schlick(1-dot(wi, H));" << endl << " (cosTheta(wiPrime)*cosTheta(woPrime));" << endl
<< " return specRef * (F * D * G / (4*cosTheta(wi))) + " << endl << " if (sigmaA != vec3(0.0))" << endl
<< " diffuseRef * ((1-F) * cosTheta(wo) * 0.31831);" << endl << " result *= exp(-sigmaA * (1/abs(cosTheta(wiPrime)) + " << endl
<< " 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 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
<< " }" << endl
<< " return result;" << endl
<< "}" << endl << "}" << endl
<< endl << endl
<< "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
<< " vec3 diffuseRef = " << depNames[1] << "(uv);" << endl << " return " << depNames[0] << "_diffuse(uv, wi, wo);" << endl
<< " return diffuseRef * 0.31831 * cosTheta(wo);"<< endl
<< "}" << endl; << "}" << endl;
} }
MTS_DECLARE_CLASS() MTS_DECLARE_CLASS()
private: private:
ref<const BSDF> m_nested; ref<const BSDF> m_nested;
ref<Shader> m_nestedShader; ref<Shader> m_nestedShader;
ref<const Texture> m_sigmaA; ref<const Texture> m_sigmaA;
ref<Shader> m_sigmaAShader; ref<Shader> m_sigmaAShader;
Float m_alpha, m_extIOR, m_intIOR, m_R0; Float m_alpha, m_extIOR, m_intIOR, m_R0, m_eta;
}; };
Shader *RoughCoating::createShader(Renderer *renderer) const { Shader *RoughCoating::createShader(Renderer *renderer) const {
@ -523,7 +558,6 @@ Shader *RoughCoating::createShader(Renderer *renderer) const {
} }
MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader) MTS_IMPLEMENT_CLASS(RoughCoatingShader, false, Shader)
#endif
MTS_IMPLEMENT_CLASS_S(RoughCoating, false, BSDF) MTS_IMPLEMENT_CLASS_S(RoughCoating, false, BSDF)
MTS_EXPORT_PLUGIN(RoughCoating, "Rough coating BSDF"); MTS_EXPORT_PLUGIN(RoughCoating, "Rough coating BSDF");
MTS_NAMESPACE_END MTS_NAMESPACE_END

View File

@ -46,10 +46,14 @@ MTS_NAMESPACE_BEGIN
* numerically or using a known material name. \default{\texttt{air} / 1.000277}} * numerically or using a known material name. \default{\texttt{air} / 1.000277}}
* \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy * \parameter{g}{\Float\Or\String}{Specifies the phase function anisotropy
* --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}} * --- see the \pluginref{hg} plugin for details\default{0, i.e. isotropic}}
* \parameter{alpha}{\Float}{
* Specifies the roughness of the unresolved surface micro-geometry.
* \default{0.0, i.e. the surface has a smooth finish}
* }
* } * }
* *
* This plugin implements a BRDF scattering model that emulates interactions * This plugin implements a BRDF scattering model that emulates interactions
* with a participating medium embedded inside a smooth dielectric layer. By * with a participating medium embedded inside a dielectric layer. By
* approximating these events using a BRDF, any scattered illumination * approximating these events using a BRDF, any scattered illumination
* is assumed to exit the material \emph{directly} at the original point of incidence. * is assumed to exit the material \emph{directly} at the original point of incidence.
* To account for internal light transport with \emph{different} incident * To account for internal light transport with \emph{different} incident
@ -60,9 +64,11 @@ MTS_NAMESPACE_BEGIN
* BSDF for single scattering in an infinitely thick layer together with * BSDF for single scattering in an infinitely thick layer together with
* an approximate multiple scattering component based on Jensen's * an approximate multiple scattering component based on Jensen's
* \cite{Jensen2001Practical} integrated dipole BRDF. These are then * \cite{Jensen2001Practical} integrated dipole BRDF. These are then
* embedded into a dielectric layer using the \pluginref{coating} plugin. * embedded into a dielectric layer using either the \pluginref{coating}
* or \pluginref{roughcoating} plugins depending on whether or not
* \code{alpha}=0.
* This yields a very convenient parameterization of a scattering model * This yields a very convenient parameterization of a scattering model
* that roughly behaves like a coated diffuse material, but expressed * that behaves similarly to a coated diffuse material, but expressed
* in terms of the scattering and absorption coefficients \code{sigmaS} * in terms of the scattering and absorption coefficients \code{sigmaS}
* and \code{sigmaA}. * and \code{sigmaA}.
*/ */
@ -79,6 +85,8 @@ public:
Properties hgProps("hg"); Properties hgProps("hg");
hgProps.setFloat("g", g); hgProps.setFloat("g", g);
Float alpha = props.getFloat("alpha", 0.0f);
ref<PhaseFunction> hg = static_cast<PhaseFunction *> ( ref<PhaseFunction> hg = static_cast<PhaseFunction *> (
PluginManager::getInstance()->createObject( PluginManager::getInstance()->createObject(
MTS_CLASS(PhaseFunction), hgProps)); MTS_CLASS(PhaseFunction), hgProps));
@ -91,9 +99,11 @@ public:
m_hk->addChild(hg); m_hk->addChild(hg);
Properties coatingProps(props); Properties coatingProps(props);
coatingProps.setPluginName("coating"); coatingProps.setPluginName(alpha == 0 ? "coating" : "roughcoating");
if (!props.hasProperty("intIOR")) if (!props.hasProperty("intIOR"))
coatingProps.setFloat("intIOR", eta); coatingProps.setFloat("intIOR", eta);
if (alpha != 0)
coatingProps.setFloat("alpha", alpha);
m_coating = static_cast<BSDF *> (PluginManager::getInstance()-> m_coating = static_cast<BSDF *> (PluginManager::getInstance()->
createObject(MTS_CLASS(BSDF), coatingProps)); createObject(MTS_CLASS(BSDF), coatingProps));
@ -116,6 +126,7 @@ public:
props.markQueried("sigmaT"); props.markQueried("sigmaT");
props.markQueried("intIOR"); props.markQueried("intIOR");
props.markQueried("extIOR"); props.markQueried("extIOR");
props.markQueried("alpha");
} }
SSSBRDF(Stream *stream, InstanceManager *manager) SSSBRDF(Stream *stream, InstanceManager *manager)