diff --git a/data/tests/test_bsdf.xml b/data/tests/test_bsdf.xml
index 9302c0be..18a57651 100644
--- a/data/tests/test_bsdf.xml
+++ b/data/tests/test_bsdf.xml
@@ -53,6 +53,18 @@
+
+
+
+
+
+
+
+
+
+
@@ -137,18 +149,6 @@
-
-
-
-
-
-
-
-
-
-
diff --git a/src/bsdfs/hk.cpp b/src/bsdfs/hk.cpp
index b33e84fb..1e6f58e3 100644
--- a/src/bsdfs/hk.cpp
+++ b/src/bsdfs/hk.cpp
@@ -185,7 +185,6 @@ public:
for (int i = 0; i < SPECTRUM_SAMPLES; i++)
albedo[i] = sigmaT[i] > 0 ? (sigmaS[i]/sigmaT[i]) : (Float) 0;
-
const Float cosThetaI = Frame::cosTheta(bRec.wi),
cosThetaO = Frame::cosTheta(bRec.wo),
dp = cosThetaI*cosThetaO;
@@ -202,7 +201,7 @@ public:
const Float phaseVal = m_phase->eval(pRec);
result = albedo * (phaseVal*cosThetaI/(cosThetaI+cosThetaO)) *
- (Spectrum(1.0f)-((-tauD/std::abs(cosThetaI))+(-tauD/std::abs(cosThetaO))).exp());
+ (Spectrum(1.0f)-((-1.0f/std::abs(cosThetaI)-1.0f/std::abs(cosThetaO)) * tauD).exp());
}
/* ==================================================================== */
@@ -378,6 +377,8 @@ public:
<< "]";
return oss.str();
}
+
+ Shader *createShader(Renderer *renderer) const;
MTS_DECLARE_CLASS()
private:
@@ -387,6 +388,71 @@ private:
Float m_thickness;
};
+
+// ================ Hardware shader implementation ================
+
+/**
+ * This is a relatively approximate GLSL shader for the HK model.
+ * It assumes that the layer is infinitely thick (i.e. there is no
+ * transmission) and that the phase function is isotropic
+ */
+class HanrahanKruegerShader : public Shader {
+public:
+ HanrahanKruegerShader(Renderer *renderer, const Texture *sigmaS, const Texture *sigmaA)
+ : Shader(renderer, EBSDFShader), m_sigmaS(sigmaS), m_sigmaA(sigmaA) {
+ m_sigmaSShader = renderer->registerShaderForResource(m_sigmaS.get());
+ m_sigmaAShader = renderer->registerShaderForResource(m_sigmaA.get());
+ }
+
+ bool isComplete() const {
+ return m_sigmaSShader.get() != NULL
+ && m_sigmaAShader.get() != NULL;
+ }
+
+ void cleanup(Renderer *renderer) {
+ renderer->unregisterShaderForResource(m_sigmaS.get());
+ renderer->unregisterShaderForResource(m_sigmaA.get());
+ }
+
+ void putDependencies(std::vector &deps) {
+ deps.push_back(m_sigmaSShader.get());
+ deps.push_back(m_sigmaAShader.get());
+ }
+
+ void generateCode(std::ostringstream &oss,
+ const std::string &evalName,
+ const std::vector &depNames) const {
+ oss << "vec3 " << evalName << "(vec2 uv, vec3 wi, vec3 wo) {" << endl
+ << " vec3 sigmaS = " << depNames[0] << "(uv);" << endl
+ << " vec3 sigmaA = " << depNames[1] << "(uv);" << endl
+ << " vec3 albedo = sigmaS/(sigmaS + sigmaA);" << endl
+ << " float cosThetaI = abs(cosTheta(wi));" << endl
+ << " float cosThetaO = abs(cosTheta(wo));" << endl
+ << " return albedo * (0.079577*cosThetaI*cosThetaO/(cosThetaI + cosThetaO));" << endl
+ << "}" << endl
+ << endl
+ << "vec3 " << evalName << "_diffuse(vec2 uv, vec3 wi, vec3 wo) {" << endl
+ << " vec3 sigmaS = " << depNames[0] << "(uv);" << endl
+ << " vec3 sigmaA = " << depNames[1] << "(uv);" << endl
+ << " vec3 albedo = sigmaS/(sigmaS + sigmaA);" << endl
+ << " float cosThetaO = abs(cosTheta(wo));" << endl
+ << " return albedo * 0.079577 * cosThetaO;" << endl
+ << "}" << endl;
+ }
+
+ MTS_DECLARE_CLASS()
+private:
+ ref m_sigmaS;
+ ref m_sigmaA;
+ ref m_sigmaSShader;
+ ref m_sigmaAShader;
+};
+
+Shader *HanrahanKrueger::createShader(Renderer *renderer) const {
+ return new HanrahanKruegerShader(renderer, m_sigmaS.get(), m_sigmaA.get());
+}
+
+MTS_IMPLEMENT_CLASS(HanrahanKruegerShader, false, Shader)
MTS_IMPLEMENT_CLASS_S(HanrahanKrueger, false, BSDF)
MTS_EXPORT_PLUGIN(HanrahanKrueger, "Hanrahan-Krueger BSDF");
MTS_NAMESPACE_END
diff --git a/src/bsdfs/microfacet.h b/src/bsdfs/microfacet.h
index b1126e22..e1d159d8 100644
--- a/src/bsdfs/microfacet.h
+++ b/src/bsdfs/microfacet.h
@@ -367,6 +367,10 @@ public:
+ alphaV * sinPhiM*sinPhiM;
pdf = std::sqrt((alphaU + 1) * (alphaV + 1))
* INV_TWOPI * std::pow(cosThetaM, exponent);
+
+ /* Prevent potential numerical issues in other stages of the model */
+ if (pdf < 1e-20f)
+ pdf = 0;
return Vector(
sinThetaM * cosPhiM,
@@ -379,6 +383,10 @@ public:
SLog(EError, "Invalid distribution function!");
}
+ /* Prevent potential numerical issues in other stages of the model */
+ if (pdf < 1e-20f)
+ pdf = 0;
+
const Float sinThetaM = std::sqrt(
std::max((Float) 0, 1 - cosThetaM*cosThetaM));
Float phiM = (2.0f * M_PI) * sample.y;
diff --git a/src/bsdfs/roughconductor.cpp b/src/bsdfs/roughconductor.cpp
index ae423fff..986d1da9 100644
--- a/src/bsdfs/roughconductor.cpp
+++ b/src/bsdfs/roughconductor.cpp
@@ -302,6 +302,9 @@ public:
const Normal m = m_distribution.sample(sample,
alphaU, alphaV, microfacetPDF);
+ if (microfacetPDF == 0)
+ return Spectrum(0.0f);
+
/* Perfect specular reflection based on the microsurface normal */
bRec.wo = reflect(bRec.wi, m);
bRec.sampledComponent = 0;
@@ -341,6 +344,9 @@ public:
const Normal m = m_distribution.sample(sample,
alphaU, alphaV, pdf);
+ if (pdf == 0)
+ return Spectrum(0.0f);
+
/* Perfect specular reflection based on the microsurface normal */
bRec.wo = reflect(bRec.wi, m);
bRec.sampledComponent = 0;
diff --git a/src/bsdfs/roughdielectric.cpp b/src/bsdfs/roughdielectric.cpp
index 1f3d6ada..339b51ed 100644
--- a/src/bsdfs/roughdielectric.cpp
+++ b/src/bsdfs/roughdielectric.cpp
@@ -464,6 +464,9 @@ public:
const Normal m = m_distribution.sample(sample,
sampleAlphaU, sampleAlphaV, microfacetPDF);
+ if (microfacetPDF == 0)
+ return Spectrum(0.0f);
+
Float F = fresnel(dot(bRec.wi, m), m_extIOR, m_intIOR),
numerator = 1.0f;
@@ -551,11 +554,8 @@ public:
const Normal m = m_distribution.sample(sample,
sampleAlphaU, sampleAlphaV, microfacetPDF);
-#if 1
- Float ref = m_distribution.pdf(m, sampleAlphaU, sampleAlphaV);
- if (std::abs(ref-microfacetPDF)/ref > Epsilon)
- cout << "OOPS! ref=" << ref << ", got=" << microfacetPDF << endl;
-#endif
+ if (microfacetPDF == 0)
+ return Spectrum(0.0f);
pdf = microfacetPDF;