automated testing of phase functions
parent
8e42e1de87
commit
c3a8ed7038
|
@ -1,111 +1,24 @@
|
||||||
<!-- This file defines a series of BSDF instances
|
<!-- This file defines a series of phase function instances
|
||||||
to be tested for consistency. This is done
|
to be tested for consistency. This is done using the
|
||||||
using the testcase 'test_chisquare' -->
|
testcase 'test_chisquare' -->
|
||||||
<scene>
|
<scene>
|
||||||
<!-- Test the lambertian model -->
|
<!-- Test the isotropic phase function -->
|
||||||
<bsdf type="lambertian"/>
|
<phase type="isotropic"/>
|
||||||
|
|
||||||
<!-- Test the diffuse transmission model -->
|
<!-- Test the Henyey-Greenstein phase function
|
||||||
<bsdf type="difftrans"/>
|
configured as highly forward-scattering -->
|
||||||
|
<phase type="hg">
|
||||||
|
<float name="g" value="0.9"/>
|
||||||
|
</phase>
|
||||||
|
|
||||||
<!-- Test the Phong model -->
|
<!-- Test the Henyey-Greenstein phase function
|
||||||
<bsdf type="phong">
|
configured as slightly backward-scattering -->
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
<phase type="hg">
|
||||||
<float name="specularAmount" value="0.5"/>
|
<float name="g" value="-0.3"/>
|
||||||
<float name="exponent" value="20"/>
|
</phase>
|
||||||
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
<!-- Test the micro-flake phase function -->
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
<phase type="microflake">
|
||||||
</bsdf>
|
<float name="stddev" value="0.1"/>
|
||||||
|
</phase>
|
||||||
<!-- Test the anisotropic Ward model -->
|
|
||||||
<bsdf type="ward">
|
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
|
||||||
<float name="specularAmount" value="0.5"/>
|
|
||||||
<float name="alphaX" value="0.1"/>
|
|
||||||
<float name="alphaY" value="0.3"/>
|
|
||||||
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the two-sided BRDF adapter -->
|
|
||||||
<bsdf type="twosided">
|
|
||||||
<bsdf type="phong">
|
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
|
||||||
<float name="specularAmount" value="0.5"/>
|
|
||||||
<float name="exponent" value="20"/>
|
|
||||||
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
|
||||||
</bsdf>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the composite material adapter with
|
|
||||||
a mix of two previously tested materials -->
|
|
||||||
<bsdf type="composite">
|
|
||||||
<string name="weights" value="0.4, 0.6"/>
|
|
||||||
|
|
||||||
<bsdf type="phong">
|
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
|
||||||
<float name="specularAmount" value="0.5"/>
|
|
||||||
<float name="exponent" value="20"/>
|
|
||||||
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<bsdf type="ward">
|
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
|
||||||
<float name="specularAmount" value="0.5"/>
|
|
||||||
<float name="alphaX" value="0.1"/>
|
|
||||||
<float name="alphaY" value="0.3"/>
|
|
||||||
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
|
||||||
</bsdf>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the microfacet model -->
|
|
||||||
<bsdf type="microfacet">
|
|
||||||
<float name="diffuseAmount" value="0.5"/>
|
|
||||||
<float name="specularAmount" value="0.5"/>
|
|
||||||
<float name="alphaB" value="0.1"/>
|
|
||||||
<spectrum name="diffuseReflectance" value="1"/>
|
|
||||||
<spectrum name="specularReflectance" value="1"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the rough metal model -->
|
|
||||||
<bsdf type="roughmetal">
|
|
||||||
<float name="alphaB" value="0.1"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the rough glass model with the
|
|
||||||
Beckmann microfacet distribution -->
|
|
||||||
<bsdf type="roughglass">
|
|
||||||
<string name="distribution" value="beckmann"/>
|
|
||||||
<float name="alpha" value=".3"/>
|
|
||||||
<float name="intIOR" value="1.5"/>
|
|
||||||
<float name="extIOR" value="1.0"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the rough glass model with the
|
|
||||||
GGX microfacet distribution -->
|
|
||||||
<bsdf type="roughglass">
|
|
||||||
<string name="distribution" value="ggx"/>
|
|
||||||
<float name="alpha" value="0.4"/>
|
|
||||||
<float name="intIOR" value="1.5"/>
|
|
||||||
<float name="extIOR" value="1.0"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<!-- Test the rough glass model with the
|
|
||||||
Phong microfacet distribution -->
|
|
||||||
<bsdf type="roughglass">
|
|
||||||
<string name="distribution" value="phong"/>
|
|
||||||
<float name="alpha" value="0.3"/>
|
|
||||||
<float name="intIOR" value="1.5"/>
|
|
||||||
<float name="extIOR" value="1.0"/>
|
|
||||||
</bsdf>
|
|
||||||
|
|
||||||
<camera type="perspective"/>
|
|
||||||
</scene>
|
</scene>
|
||||||
|
|
|
@ -559,7 +559,8 @@ void Scene::addChild(const std::string &name, ConfigurableObject *child) {
|
||||||
AssertEx(m_integrator == NULL, "There can only be one integrator per scene");
|
AssertEx(m_integrator == NULL, "There can only be one integrator per scene");
|
||||||
m_integrator = static_cast<Integrator *>(child);
|
m_integrator = static_cast<Integrator *>(child);
|
||||||
} else if (cClass->derivesFrom(MTS_CLASS(Texture))
|
} else if (cClass->derivesFrom(MTS_CLASS(Texture))
|
||||||
|| cClass->derivesFrom(MTS_CLASS(BSDF))) {
|
|| cClass->derivesFrom(MTS_CLASS(BSDF))
|
||||||
|
|| cClass->derivesFrom(MTS_CLASS(PhaseFunction))) {
|
||||||
ConfigurableObject *obj= static_cast<ConfigurableObject *>(child);
|
ConfigurableObject *obj= static_cast<ConfigurableObject *>(child);
|
||||||
obj->incRef();
|
obj->incRef();
|
||||||
m_objects.push_back(obj);
|
m_objects.push_back(obj);
|
||||||
|
|
|
@ -38,17 +38,18 @@ class TestChiSquare : public TestCase {
|
||||||
public:
|
public:
|
||||||
MTS_BEGIN_TESTCASE()
|
MTS_BEGIN_TESTCASE()
|
||||||
MTS_DECLARE_TEST(test01_BSDF)
|
MTS_DECLARE_TEST(test01_BSDF)
|
||||||
|
MTS_DECLARE_TEST(test02_PhaseFunction)
|
||||||
MTS_END_TESTCASE()
|
MTS_END_TESTCASE()
|
||||||
|
|
||||||
/// Adapter to use BSDFs in the chi-square test
|
/// Adapter to use BSDFs in the chi-square test
|
||||||
class BSDFAdapter {
|
class BSDFAdapter {
|
||||||
public:
|
public:
|
||||||
BSDFAdapter(const BSDF *bsdf, Random *random, const Vector &wi, int component = -1)
|
BSDFAdapter(const BSDF *bsdf, Sampler *sampler, const Vector &wi, int component = -1)
|
||||||
: m_bsdf(bsdf), m_random(random), m_wi(wi), m_component(component),
|
: m_bsdf(bsdf), m_sampler(sampler), m_wi(wi), m_component(component),
|
||||||
m_largestWeight(0) { }
|
m_largestWeight(0) { }
|
||||||
|
|
||||||
std::pair<Vector, Float> generateSample() {
|
std::pair<Vector, Float> generateSample() {
|
||||||
Point2 sample(m_random->nextFloat(), m_random->nextFloat());
|
Point2 sample(m_sampler->next2D());
|
||||||
Intersection its;
|
Intersection its;
|
||||||
BSDFQueryRecord bRec(its);
|
BSDFQueryRecord bRec(its);
|
||||||
bRec.component = m_component;
|
bRec.component = m_component;
|
||||||
|
@ -108,19 +109,85 @@ public:
|
||||||
inline Float getLargestWeight() const { return m_largestWeight; }
|
inline Float getLargestWeight() const { return m_largestWeight; }
|
||||||
private:
|
private:
|
||||||
ref<const BSDF> m_bsdf;
|
ref<const BSDF> m_bsdf;
|
||||||
ref<Random> m_random;
|
ref<Sampler> m_sampler;
|
||||||
Vector m_wi;
|
Vector m_wi;
|
||||||
int m_component;
|
int m_component;
|
||||||
Float m_largestWeight;
|
Float m_largestWeight;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Adapter to use Phase functions in the chi-square test
|
||||||
|
class PhaseFunctionAdapter {
|
||||||
|
public:
|
||||||
|
PhaseFunctionAdapter(const MediumSamplingRecord &mRec,
|
||||||
|
const PhaseFunction *phase, Sampler *sampler, const Vector &wi)
|
||||||
|
: m_mRec(mRec), m_phase(phase), m_sampler(sampler), m_wi(wi),
|
||||||
|
m_largestWeight(0) { }
|
||||||
|
|
||||||
|
std::pair<Vector, Float> generateSample() {
|
||||||
|
Point2 sample(m_sampler->next2D());
|
||||||
|
PhaseFunctionQueryRecord pRec(m_mRec, m_wi);
|
||||||
|
|
||||||
|
/* Check the various sampling routines for agreement amongst each other */
|
||||||
|
Float pdfVal;
|
||||||
|
Float f = m_phase->sample(pRec, pdfVal, m_sampler);
|
||||||
|
Float sampled = m_phase->sample(pRec, m_sampler);
|
||||||
|
|
||||||
|
if (f == 0 || pdfVal == 0) {
|
||||||
|
if (sampled != 0)
|
||||||
|
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
||||||
|
f, pdfVal, sampled);
|
||||||
|
return std::make_pair(pRec.wo, 0.0f);
|
||||||
|
} else if (sampled == 0) {
|
||||||
|
if (f != 0 && pdfVal != 0)
|
||||||
|
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
||||||
|
f, pdfVal, sampled);
|
||||||
|
return std::make_pair(pRec.wo, 0.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Float sampled2 = f/pdfVal;
|
||||||
|
bool mismatch = false;
|
||||||
|
|
||||||
|
SAssert(sampled >= 0 && sampled2 >= 0);
|
||||||
|
Float min = std::min(sampled, sampled2);
|
||||||
|
Float err = std::abs(sampled - sampled2);
|
||||||
|
m_largestWeight = std::max(m_largestWeight, sampled);
|
||||||
|
|
||||||
|
if (min < Epsilon && err > Epsilon) // absolute error threshold
|
||||||
|
mismatch = true;
|
||||||
|
else if (min > Epsilon && err/min > Epsilon) // relative error threshold
|
||||||
|
mismatch = true;
|
||||||
|
|
||||||
|
if (mismatch)
|
||||||
|
Log(EWarn, "Inconsistency: f=%f, pdf=%f, sampled f/pdf=%f",
|
||||||
|
f, pdfVal, sampled);
|
||||||
|
|
||||||
|
return std::make_pair(pRec.wo, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
Float pdf(const Vector &wo) const {
|
||||||
|
PhaseFunctionQueryRecord pRec(m_mRec, m_wi, wo);
|
||||||
|
if (m_phase->f(pRec) == 0)
|
||||||
|
return 0.0f;
|
||||||
|
return m_phase->pdf(pRec);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline Float getLargestWeight() const { return m_largestWeight; }
|
||||||
|
private:
|
||||||
|
const MediumSamplingRecord &m_mRec;
|
||||||
|
ref<const PhaseFunction> m_phase;
|
||||||
|
ref<Sampler> m_sampler;
|
||||||
|
Vector m_wi;
|
||||||
|
Float m_largestWeight;
|
||||||
|
};
|
||||||
|
|
||||||
void test01_BSDF() {
|
void test01_BSDF() {
|
||||||
/* Load a set of BSDF instances to be tested from the following XML file */
|
/* Load a set of BSDF instances to be tested from the following XML file */
|
||||||
ref<Scene> scene = loadScene("data/tests/test_bsdf.xml");
|
ref<Scene> scene = loadScene("data/tests/test_bsdf.xml");
|
||||||
|
|
||||||
const std::vector<ConfigurableObject *> objects = scene->getReferencedObjects();
|
const std::vector<ConfigurableObject *> objects = scene->getReferencedObjects();
|
||||||
size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
|
size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
|
||||||
ref<Random> random = new Random();
|
ref<Sampler> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
|
||||||
|
createObject(MTS_CLASS(Sampler), Properties("independent")));
|
||||||
ProgressReporter *progress = new ProgressReporter("Checking", wiSamples, NULL);
|
ProgressReporter *progress = new ProgressReporter("Checking", wiSamples, NULL);
|
||||||
|
|
||||||
Log(EInfo, "Verifying BSDF sampling routines ..");
|
Log(EInfo, "Verifying BSDF sampling routines ..");
|
||||||
|
@ -140,11 +207,11 @@ public:
|
||||||
Vector wi;
|
Vector wi;
|
||||||
|
|
||||||
if (bsdf->getType() & BSDF::EBackSide)
|
if (bsdf->getType() & BSDF::EBackSide)
|
||||||
wi = squareToSphere(Point2(random->nextFloat(), random->nextFloat()));
|
wi = squareToSphere(sampler->next2D());
|
||||||
else
|
else
|
||||||
wi = squareToHemispherePSA(Point2(random->nextFloat(), random->nextFloat()));
|
wi = squareToHemispherePSA(sampler->next2D());
|
||||||
|
|
||||||
BSDFAdapter adapter(bsdf, random, wi);
|
BSDFAdapter adapter(bsdf, sampler, wi);
|
||||||
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
|
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
|
||||||
chiSqr->setLogLevel(EDebug);
|
chiSqr->setLogLevel(EDebug);
|
||||||
|
|
||||||
|
@ -180,11 +247,11 @@ public:
|
||||||
Vector wi;
|
Vector wi;
|
||||||
|
|
||||||
if (bsdf->getType(comp) & BSDF::EBackSide)
|
if (bsdf->getType(comp) & BSDF::EBackSide)
|
||||||
wi = squareToSphere(Point2(random->nextFloat(), random->nextFloat()));
|
wi = squareToSphere(sampler->next2D());
|
||||||
else
|
else
|
||||||
wi = squareToHemispherePSA(Point2(random->nextFloat(), random->nextFloat()));
|
wi = squareToHemispherePSA(sampler->next2D());
|
||||||
|
|
||||||
BSDFAdapter adapter(bsdf, random, wi, comp);
|
BSDFAdapter adapter(bsdf, sampler, wi, comp);
|
||||||
|
|
||||||
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
|
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
|
||||||
chiSqr->setLogLevel(EDebug);
|
chiSqr->setLogLevel(EDebug);
|
||||||
|
@ -219,6 +286,70 @@ public:
|
||||||
Log(EInfo, "%i/%i BSDF checks succeeded", testCount-failureCount, testCount);
|
Log(EInfo, "%i/%i BSDF checks succeeded", testCount-failureCount, testCount);
|
||||||
delete progress;
|
delete progress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test02_PhaseFunction() {
|
||||||
|
/* Load a set of BSDF instances to be tested from the following XML file */
|
||||||
|
ref<Scene> scene = loadScene("data/tests/test_phase.xml");
|
||||||
|
|
||||||
|
const std::vector<ConfigurableObject *> objects = scene->getReferencedObjects();
|
||||||
|
size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
|
||||||
|
ref<Sampler> sampler = static_cast<Sampler *> (PluginManager::getInstance()->
|
||||||
|
createObject(MTS_CLASS(Sampler), Properties("independent")));
|
||||||
|
|
||||||
|
ProgressReporter *progress = new ProgressReporter("Checking", wiSamples, NULL);
|
||||||
|
|
||||||
|
Log(EInfo, "Verifying phase function sampling routines ..");
|
||||||
|
for (size_t i=0; i<objects.size(); ++i) {
|
||||||
|
if (!objects[i]->getClass()->derivesFrom(MTS_CLASS(PhaseFunction)))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const PhaseFunction *phase = static_cast<const PhaseFunction *>(objects[i]);
|
||||||
|
Float largestWeight = 0;
|
||||||
|
|
||||||
|
Log(EInfo, "Processing phase function model %s", phase->toString().c_str());
|
||||||
|
Log(EInfo, "Checking the model for %i incident directions", wiSamples);
|
||||||
|
progress->reset();
|
||||||
|
MediumSamplingRecord mRec;
|
||||||
|
|
||||||
|
/* Sampler fiber/particle orientation */
|
||||||
|
mRec.orientation = squareToSphere(sampler->next2D());
|
||||||
|
|
||||||
|
/* Test for a number of different incident directions */
|
||||||
|
for (size_t j=0; j<wiSamples; ++j) {
|
||||||
|
Vector wi = squareToSphere(sampler->next2D());
|
||||||
|
|
||||||
|
PhaseFunctionAdapter adapter(mRec, phase, sampler, wi);
|
||||||
|
ref<ChiSquare> chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
|
||||||
|
chiSqr->setLogLevel(EDebug);
|
||||||
|
|
||||||
|
// Initialize the tables used by the chi-square test
|
||||||
|
chiSqr->fill(
|
||||||
|
boost::bind(&PhaseFunctionAdapter::generateSample, &adapter),
|
||||||
|
boost::bind(&PhaseFunctionAdapter::pdf, &adapter, _1)
|
||||||
|
);
|
||||||
|
|
||||||
|
// (the following assumes that the distribution has 1 parameter, e.g. exponent value)
|
||||||
|
ChiSquare::ETestResult result = chiSqr->runTest(1, SIGNIFICANCE_LEVEL);
|
||||||
|
if (result == ChiSquare::EReject) {
|
||||||
|
std::string filename = formatString("failure_%i.m", failureCount++);
|
||||||
|
chiSqr->dumpTables(filename);
|
||||||
|
failAndContinue(formatString("Uh oh, the chi-square test indicates a potential "
|
||||||
|
"issue for wi=%s. Dumped the contingency tables to '%s' for user analysis",
|
||||||
|
wi.toString().c_str(), filename.c_str()));
|
||||||
|
} else {
|
||||||
|
succeed();
|
||||||
|
}
|
||||||
|
largestWeight = std::max(largestWeight, adapter.getLargestWeight());
|
||||||
|
++testCount;
|
||||||
|
progress->update(j+1);
|
||||||
|
}
|
||||||
|
|
||||||
|
Log(EInfo, "Done with this phase function. The largest encountered "
|
||||||
|
"importance weight was = %.2f", largestWeight);
|
||||||
|
}
|
||||||
|
Log(EInfo, "%i/%i phase function checks succeeded", testCount-failureCount, testCount);
|
||||||
|
delete progress;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
MTS_EXPORT_TESTCASE(TestChiSquare, "Chi-square test for various sampling functions")
|
MTS_EXPORT_TESTCASE(TestChiSquare, "Chi-square test for various sampling functions")
|
||||||
|
|
Loading…
Reference in New Issue