extended the chi2-tester with some extra checks for BSDF::pdf() and BSDF::eval()

metadata
Wenzel Jakob 2011-07-07 19:34:40 +02:00
parent dacb6f96b6
commit e22b47cc4a
7 changed files with 92 additions and 62 deletions

View File

@ -2,22 +2,6 @@
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> <scene>
<!-- Test the rough dielectric model with the anisotropic
Ashikhmin-Shirley microfacet distribution -->
<bsdf type="roughdielectric">
<string name="distribution" value="as"/>
<float name="alphaU" value=".1"/>
<float name="alphaV" value=".3"/>
<float name="intIOR" value="1.5"/>
<float name="extIOR" value="1.0"/>
</bsdf>
<!-- Test the dielectric model -->
<bsdf type="dielectric">
<string name="intIOR" value="water"/>
<string name="extIOR" value="air"/>
</bsdf>
<!-- Test the diffuse model --> <!-- Test the diffuse model -->
<bsdf type="diffuse"/> <bsdf type="diffuse"/>
@ -40,6 +24,27 @@
<!-- Test the conductor model --> <!-- Test the conductor model -->
<bsdf type="conductor"/> <bsdf type="conductor"/>
<!-- Test the dielectric model -->
<bsdf type="dielectric">
<string name="intIOR" value="water"/>
<string name="extIOR" value="air"/>
</bsdf>
<!-- Test a mixture of degenerate materials -->
<bsdf type="mixture">
<string name="weights" value=".8 .2"/>
<bsdf type="dielectric"/>
<bsdf type="conductor"/>
</bsdf>
<!-- Test a mixture of degenerate and
non-degenerate materials -->
<bsdf type="mixture">
<string name="weights" value=".8 .2"/>
<bsdf type="dielectric"/>
<bsdf type="diffuse"/>
</bsdf>
<!-- Test the rough glass model with the <!-- Test the rough glass model with the
Beckmann microfacet distribution --> Beckmann microfacet distribution -->
<bsdf type="roughdielectric"> <bsdf type="roughdielectric">

View File

@ -265,6 +265,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "SmoothConductor[" << endl oss << "SmoothConductor[" << endl
<< " name = \"" << getName() << "\"," << endl
<< " eta = " << m_eta.toString() << "," << endl << " eta = " << m_eta.toString() << "," << endl
<< " k = " << m_k.toString() << "," << endl << " k = " << m_k.toString() << "," << endl
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << endl << " specularReflectance = " << indent(m_specularReflectance->toString()) << endl

View File

@ -477,6 +477,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "SmoothDielectric[" << endl oss << "SmoothDielectric[" << endl
<< " name = \"" << getName() << "\"," << endl
<< " intIOR = " << m_intIOR << "," << endl << " intIOR = " << m_intIOR << "," << endl
<< " extIOR = " << m_extIOR << "," << endl << " extIOR = " << m_extIOR << "," << endl
<< " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl << " specularReflectance = " << indent(m_specularReflectance->toString()) << "," << endl

View File

@ -129,6 +129,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "DiffuseTransmitter[" << endl oss << "DiffuseTransmitter[" << endl
<< " name = \"" << getName() << "\"," << endl
<< " transmittance = " << indent(m_transmittance->toString()) << endl << " transmittance = " << indent(m_transmittance->toString()) << endl
<< "]"; << "]";
return oss.str(); return oss.str();

View File

@ -37,7 +37,6 @@ public:
Log(EError, "No weights were supplied!"); Log(EError, "No weights were supplied!");
m_weights.resize(weights.size()); m_weights.resize(weights.size());
Float totalWeight = 0;
char *end_ptr = NULL; char *end_ptr = NULL;
for (size_t i=0; i<weights.size(); ++i) { for (size_t i=0; i<weights.size(); ++i) {
Float weight = (Float) strtod(weights[i].c_str(), &end_ptr); Float weight = (Float) strtod(weights[i].c_str(), &end_ptr);
@ -46,13 +45,7 @@ public:
if (weight < 0) if (weight < 0)
SLog(EError, "Invalid BRDF weight!"); SLog(EError, "Invalid BRDF weight!");
m_weights[i] = weight; m_weights[i] = weight;
totalWeight += weight;
} }
if (totalWeight > 1)
Log(EWarn, "Energy conservation is violated!");
if (totalWeight == 0)
Log(EError, "Combined weight must be > 0!");
} }
MixtureBSDF(Stream *stream, InstanceManager *manager) MixtureBSDF(Stream *stream, InstanceManager *manager)
@ -91,6 +84,27 @@ public:
Log(EError, "BSDF count mismatch: " SIZE_T_FMT " bsdfs, but specified " SIZE_T_FMT " weights", Log(EError, "BSDF count mismatch: " SIZE_T_FMT " bsdfs, but specified " SIZE_T_FMT " weights",
m_bsdfs.size(), m_bsdfs.size()); m_bsdfs.size(), m_bsdfs.size());
Float totalWeight = 0;
for (size_t i=0; i<m_weights.size(); ++i)
totalWeight += m_weights[i];
if (totalWeight <= 0)
Log(EError, "The weights must sum to a value greater than zero!");
if (m_ensureEnergyConservation && totalWeight > 1) {
std::ostringstream oss;
Float scale = 1.0f / totalWeight;
oss << "The BSDF" << endl << toString() << endl
<< "potentially violates energy conservation, since the weights "
<< "sum to " << totalWeight << ", which is greater than one! "
<< "They will be re-scaled to avoid potential issues. Specify "
<< "the parameter ensureEnergyConservation=false to prevent "
<< "this from happening.";
Log(EWarn, "%s", oss.str().c_str());
for (size_t i=0; i<m_weights.size(); ++i)
m_weights[i] *= scale;
}
for (size_t i=0; i<m_bsdfs.size(); ++i) for (size_t i=0; i<m_bsdfs.size(); ++i)
componentCount += m_bsdfs[i]->getComponentCount(); componentCount += m_bsdfs[i]->getComponentCount();
@ -239,6 +253,7 @@ public:
std::string toString() const { std::string toString() const {
std::ostringstream oss; std::ostringstream oss;
oss << "MixtureBSDF[" << endl oss << "MixtureBSDF[" << endl
<< " name = \"" << getName() << "\"," << endl
<< " weights = {"; << " weights = {";
for (size_t i=0; i<m_bsdfs.size(); ++i) { for (size_t i=0; i<m_bsdfs.size(); ++i) {
oss << " " << m_weights[i]; oss << " " << m_weights[i];

View File

@ -90,24 +90,26 @@ Texture *BSDF::ensureEnergyConservation(Texture *texture,
static std::string typeMaskToString(unsigned int typeMask) { static std::string typeMaskToString(unsigned int typeMask) {
std::ostringstream oss; std::ostringstream oss;
oss << "{ "; oss << "{ ";
if (typeMask & BSDF::EAll) { oss << "all "; typeMask &= ~BSDF::EAll; } #define isset(mask) (typeMask & mask) == mask
if (typeMask & BSDF::ESmooth) { oss << "smooth "; typeMask &= ~BSDF::ESmooth; } if (isset(BSDF::EAll)) { oss << "all "; typeMask &= ~BSDF::EAll; }
if (typeMask & BSDF::EDiffuse) { oss << "diffuse "; typeMask &= ~BSDF::EDiffuse; } if (isset(BSDF::ESmooth)) { oss << "smooth "; typeMask &= ~BSDF::ESmooth; }
if (typeMask & BSDF::EGlossy) { oss << "glossy "; typeMask &= ~BSDF::EGlossy; } if (isset(BSDF::EDiffuse)) { oss << "diffuse "; typeMask &= ~BSDF::EDiffuse; }
if (typeMask & BSDF::EDelta) { oss << "delta"; typeMask &= ~BSDF::EDelta; } if (isset(BSDF::EGlossy)) { oss << "glossy "; typeMask &= ~BSDF::EGlossy; }
if (typeMask & BSDF::EDelta1D) { oss << "delta1D "; typeMask &= ~BSDF::EDelta1D; } if (isset(BSDF::EDelta)) { oss << "delta"; typeMask &= ~BSDF::EDelta; }
if (typeMask & BSDF::EDiffuseReflection) { oss << "diffuseReflection "; typeMask &= ~BSDF::EDiffuseReflection; } if (isset(BSDF::EDelta1D)) { oss << "delta1D "; typeMask &= ~BSDF::EDelta1D; }
if (typeMask & BSDF::EDiffuseTransmission) { oss << "diffuseTransmission "; typeMask &= ~BSDF::EDiffuseTransmission; } if (isset(BSDF::EDiffuseReflection)) { oss << "diffuseReflection "; typeMask &= ~BSDF::EDiffuseReflection; }
if (typeMask & BSDF::EGlossyReflection) { oss << "glossyReflection "; typeMask &= ~BSDF::EGlossyReflection; } if (isset(BSDF::EDiffuseTransmission)) { oss << "diffuseTransmission "; typeMask &= ~BSDF::EDiffuseTransmission; }
if (typeMask & BSDF::EGlossyTransmission) { oss << "glossyTransmission "; typeMask &= ~BSDF::EGlossyTransmission; } if (isset(BSDF::EGlossyReflection)) { oss << "glossyReflection "; typeMask &= ~BSDF::EGlossyReflection; }
if (typeMask & BSDF::EDeltaReflection) { oss << "deltaReflection "; typeMask &= ~BSDF::EDeltaReflection; } if (isset(BSDF::EGlossyTransmission)) { oss << "glossyTransmission "; typeMask &= ~BSDF::EGlossyTransmission; }
if (typeMask & BSDF::EDeltaTransmission) { oss << "deltaTransmission "; typeMask &= ~BSDF::EDeltaTransmission; } if (isset(BSDF::EDeltaReflection)) { oss << "deltaReflection "; typeMask &= ~BSDF::EDeltaReflection; }
if (typeMask & BSDF::EDelta1DReflection) { oss << "delta1DReflection "; typeMask &= ~BSDF::EDelta1DReflection; } if (isset(BSDF::EDeltaTransmission)) { oss << "deltaTransmission "; typeMask &= ~BSDF::EDeltaTransmission; }
if (typeMask & BSDF::EDelta1DTransmission) { oss << "delta1DTransmission "; typeMask &= ~BSDF::EDelta1DTransmission; } if (isset(BSDF::EDelta1DReflection)) { oss << "delta1DReflection "; typeMask &= ~BSDF::EDelta1DReflection; }
if (typeMask & BSDF::EAnisotropic) { oss << "anisotropic "; typeMask &= ~BSDF::EAnisotropic; } if (isset(BSDF::EDelta1DTransmission)) { oss << "delta1DTransmission "; typeMask &= ~BSDF::EDelta1DTransmission; }
if (typeMask & BSDF::EFrontSide) { oss << "frontSide "; typeMask &= ~BSDF::EFrontSide; } if (isset(BSDF::EAnisotropic)) { oss << "anisotropic "; typeMask &= ~BSDF::EAnisotropic; }
if (typeMask & BSDF::EBackSide) { oss << "backSide "; typeMask &= ~BSDF::EBackSide; } if (isset(BSDF::EFrontSide)) { oss << "frontSide "; typeMask &= ~BSDF::EFrontSide; }
if (typeMask & BSDF::ECanUseSampler) { oss << "canUseSampler "; typeMask &= ~BSDF::ECanUseSampler; } if (isset(BSDF::EBackSide)) { oss << "backSide "; typeMask &= ~BSDF::EBackSide; }
if (isset(BSDF::ECanUseSampler)) { oss << "canUseSampler "; typeMask &= ~BSDF::ECanUseSampler; }
#undef isset
oss << "}"; oss << "}";
return oss.str(); return oss.str();
} }

View File

@ -112,7 +112,7 @@ public:
enableFPExceptions(); enableFPExceptions();
#endif #endif
Float pdfVal; Float pdfVal, pdfVal2;
/* Only make the sampler available to the BSDF when requested /* Only make the sampler available to the BSDF when requested
by the testcase. This allows testing both sampling variants by the testcase. This allows testing both sampling variants
@ -123,42 +123,48 @@ public:
if (m_passSamplerToBSDF) if (m_passSamplerToBSDF)
bRec.sampler = m_fakeSampler; bRec.sampler = m_fakeSampler;
/* Check the various sampling routines for agreement amongst each other */ /* Check the various sampling routines for agreement
amongst each other */
m_fakeSampler->clear(); m_fakeSampler->clear();
Spectrum f = m_bsdf->sample(bRec, pdfVal, sample); Spectrum f = m_bsdf->sample(bRec, pdfVal, sample);
m_fakeSampler->rewind(); m_fakeSampler->rewind();
Spectrum sampled = m_bsdf->sample(bRec, sample); Spectrum sampled = m_bsdf->sample(bRec, sample);
EMeasure measure = ESolidAngle;
if (!f.isZero())
measure = BSDF::getMeasure(bRec.sampledType);
Spectrum f2 = m_bsdf->eval(bRec, measure);
pdfVal2 = m_bsdf->pdf(bRec, measure);
if (f.isZero() || pdfVal == 0) { if (f.isZero() || pdfVal == 0 || pdfVal2 == 0) {
if (!sampled.isZero()) if (!sampled.isZero())
Log(EWarn, "Inconsistency (1): f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s", Log(EWarn, "Inconsistency (1): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i",
f.toString().c_str(), pdfVal, sampled.toString().c_str(), bRec.toString().c_str()); f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure);
#if defined(MTS_DEBUG_FP) #if defined(MTS_DEBUG_FP)
disableFPExceptions(); disableFPExceptions();
#endif #endif
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle); return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
} else if (sampled.isZero()) { } else if (sampled.isZero()) {
if (!f.isZero() && pdfVal != 0) if ((!f.isZero() && pdfVal != 0) || (!f2.isZero() && pdfVal2 != 0))
Log(EWarn, "Inconsistency (2): f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s", Log(EWarn, "Inconsistency (2): f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i",
f.toString().c_str(), pdfVal, sampled.toString().c_str(), bRec.toString().c_str()); f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure);
#if defined(MTS_DEBUG_FP) #if defined(MTS_DEBUG_FP)
disableFPExceptions(); disableFPExceptions();
#endif #endif
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle); return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
} }
Spectrum sampled2 = f/pdfVal; Spectrum sampled2 = f/pdfVal, evaluated = f2/pdfVal2;
if (!sampled.isValid() || !sampled2.isValid()) { if (!sampled.isValid() || !sampled2.isValid() || !evaluated.isValid()) {
Log(EWarn, "Ooops: f=%s, pdf=%f, sampled f/pdf=%s, bRec=%s", Log(EWarn, "Ooops: f=%s, f2=%s, pdf=%f, pdf2=%f, sampled f/pdf=%s, bRec=%s, measure=%i",
f.toString().c_str(), pdfVal, sampled.toString().c_str(), bRec.toString().c_str()); f.toString().c_str(), f2.toString().c_str(), pdfVal, pdfVal2, sampled.toString().c_str(), bRec.toString().c_str(), measure);
return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle); return boost::make_tuple(bRec.wo, 0.0f, ESolidAngle);
} }
bool mismatch = false; bool mismatch = false;
for (int i=0; i<SPECTRUM_SAMPLES; ++i) { for (int i=0; i<SPECTRUM_SAMPLES; ++i) {
Float a = sampled[i], b = sampled2[i]; Float a = sampled[i], b = sampled2[i], c = evaluated[i];
Float min = std::min(a, b); Float min = std::min(std::min(a, b), c);
Float err = std::abs(a - b); Float err = std::max(std::max(std::abs(a - b), std::abs(a - c)), std::abs(b - c));
m_largestWeight = std::max(m_largestWeight, a); m_largestWeight = std::max(m_largestWeight, a);
if (min < ERROR_REQ && err > ERROR_REQ) // absolute error threshold if (min < ERROR_REQ && err > ERROR_REQ) // absolute error threshold
@ -168,15 +174,14 @@ public:
} }
if (mismatch) if (mismatch)
Log(EWarn, "Potential inconsistency (3): f/pdf=%s, sampled f/pdf=%s", Log(EWarn, "Potential inconsistency (3): f/pdf=%s (method 1), f/pdf=%s (methdod 2), sampled f/pdf=%s",
sampled2.toString().c_str(), sampled.toString().c_str()); sampled2.toString().c_str(), evaluated.toString().c_str(), sampled.toString().c_str());
#if defined(MTS_DEBUG_FP) #if defined(MTS_DEBUG_FP)
disableFPExceptions(); disableFPExceptions();
#endif #endif
return boost::make_tuple(bRec.wo, 1.0f, return boost::make_tuple(bRec.wo, 1.0f, measure);
BSDF::getMeasure(bRec.sampledType));
} }
Float pdf(const Vector &wo, EMeasure measure) { Float pdf(const Vector &wo, EMeasure measure) {