diff --git a/data/tests/test_phase.xml b/data/tests/test_phase.xml
index 0c826dff..443d72c5 100644
--- a/data/tests/test_phase.xml
+++ b/data/tests/test_phase.xml
@@ -1,111 +1,24 @@
-
+
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
diff --git a/src/librender/scene.cpp b/src/librender/scene.cpp
index d64444a6..a52e8e3d 100644
--- a/src/librender/scene.cpp
+++ b/src/librender/scene.cpp
@@ -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");
m_integrator = static_cast(child);
} 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(child);
obj->incRef();
m_objects.push_back(obj);
diff --git a/src/tests/test_chisquare.cpp b/src/tests/test_chisquare.cpp
index e33a83e7..0d41b5a6 100644
--- a/src/tests/test_chisquare.cpp
+++ b/src/tests/test_chisquare.cpp
@@ -38,17 +38,18 @@ class TestChiSquare : public TestCase {
public:
MTS_BEGIN_TESTCASE()
MTS_DECLARE_TEST(test01_BSDF)
+ MTS_DECLARE_TEST(test02_PhaseFunction)
MTS_END_TESTCASE()
/// Adapter to use BSDFs in the chi-square test
class BSDFAdapter {
public:
- BSDFAdapter(const BSDF *bsdf, Random *random, const Vector &wi, int component = -1)
- : m_bsdf(bsdf), m_random(random), m_wi(wi), m_component(component),
+ BSDFAdapter(const BSDF *bsdf, Sampler *sampler, const Vector &wi, int component = -1)
+ : m_bsdf(bsdf), m_sampler(sampler), m_wi(wi), m_component(component),
m_largestWeight(0) { }
std::pair generateSample() {
- Point2 sample(m_random->nextFloat(), m_random->nextFloat());
+ Point2 sample(m_sampler->next2D());
Intersection its;
BSDFQueryRecord bRec(its);
bRec.component = m_component;
@@ -108,19 +109,85 @@ public:
inline Float getLargestWeight() const { return m_largestWeight; }
private:
ref m_bsdf;
- ref m_random;
+ ref m_sampler;
Vector m_wi;
int m_component;
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 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 m_phase;
+ ref m_sampler;
+ Vector m_wi;
+ Float m_largestWeight;
+ };
+
void test01_BSDF() {
/* Load a set of BSDF instances to be tested from the following XML file */
ref scene = loadScene("data/tests/test_bsdf.xml");
const std::vector objects = scene->getReferencedObjects();
size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
- ref random = new Random();
+ ref sampler = static_cast (PluginManager::getInstance()->
+ createObject(MTS_CLASS(Sampler), Properties("independent")));
ProgressReporter *progress = new ProgressReporter("Checking", wiSamples, NULL);
Log(EInfo, "Verifying BSDF sampling routines ..");
@@ -140,11 +207,11 @@ public:
Vector wi;
if (bsdf->getType() & BSDF::EBackSide)
- wi = squareToSphere(Point2(random->nextFloat(), random->nextFloat()));
+ wi = squareToSphere(sampler->next2D());
else
- wi = squareToHemispherePSA(Point2(random->nextFloat(), random->nextFloat()));
+ wi = squareToHemispherePSA(sampler->next2D());
- BSDFAdapter adapter(bsdf, random, wi);
+ BSDFAdapter adapter(bsdf, sampler, wi);
ref chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
chiSqr->setLogLevel(EDebug);
@@ -180,11 +247,11 @@ public:
Vector wi;
if (bsdf->getType(comp) & BSDF::EBackSide)
- wi = squareToSphere(Point2(random->nextFloat(), random->nextFloat()));
+ wi = squareToSphere(sampler->next2D());
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 chiSqr = new ChiSquare(thetaBins, 2*thetaBins, wiSamples);
chiSqr->setLogLevel(EDebug);
@@ -219,6 +286,70 @@ public:
Log(EInfo, "%i/%i BSDF checks succeeded", testCount-failureCount, testCount);
delete progress;
}
+
+ void test02_PhaseFunction() {
+ /* Load a set of BSDF instances to be tested from the following XML file */
+ ref scene = loadScene("data/tests/test_phase.xml");
+
+ const std::vector objects = scene->getReferencedObjects();
+ size_t thetaBins = 10, wiSamples = 20, failureCount = 0, testCount = 0;
+ ref sampler = static_cast (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; igetClass()->derivesFrom(MTS_CLASS(PhaseFunction)))
+ continue;
+
+ const PhaseFunction *phase = static_cast(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; jnext2D());
+
+ PhaseFunctionAdapter adapter(mRec, phase, sampler, wi);
+ ref 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")