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")