microflake bugfixes

metadata
Wenzel Jakob 2011-04-14 14:19:08 +02:00
parent d3ad21f0da
commit 9f58f35623
10 changed files with 131 additions and 65 deletions

View File

@ -22,7 +22,6 @@
#include <mitsuba/core/fresolver.h>
#include <mitsuba/core/quad.h>
#include <mitsuba/core/timer.h>
#include <mitsuba/core/random.h>
#include <boost/bind.hpp>
#include <boost/math/distributions/chi_squared.hpp>
@ -42,10 +41,16 @@ MTS_NAMESPACE_BEGIN
* integrated over the area of each bin. Comparing the actual and reference
* bin counts yields the desired test statistic.
*
* Given a BSDF/phase function with the following interface
* Given a probability distribution with the following interface
*
* <code>
* class MyDistribution {
* Vector sample(const Point2 &uniformSample) const;
* // Sample a (optionally weighted) direction. A non-unity weight
* // in the return value is needed when the sampling distribution
* // doesn't exactly match the implementation in pdf()
* std::pair<Vector, Float> generateSample() const;
*
* /// Compute the probability density for the specified direction
* Float pdf(const Vector &direction) const;
* };
* </code>
@ -58,7 +63,7 @@ MTS_NAMESPACE_BEGIN
*
* // Initialize the tables used by the chi-square test
* chiSqr.fill(
* boost::bind(&MyDistribution::sample, myDistrInstance, _1),
* boost::bind(&MyDistribution::generateSample, myDistrInstance),
* boost::bind(&MyDistribution::pdf, myDistrInstance, _1)
* );
*
@ -85,7 +90,7 @@ public:
*
* \param sampleCount
* Number of samples to be used when computing the bin
* values. The default is \c thetaBins*phiBins*1000
* values. The default is \c thetaBins*phiBins*5000
*/
ChiSquareTest(int thetaBins = 10, int phiBins = 0, size_t sampleCount = 0)
: m_thetaBins(thetaBins), m_phiBins(phiBins), m_sampleCount(sampleCount) {
@ -93,7 +98,7 @@ public:
m_phiBins = 2*m_thetaBins;
if (m_sampleCount == 0)
m_sampleCount = m_thetaBins * m_phiBins * 1000;
m_table = new uint32_t[m_thetaBins*m_phiBins];
m_table = new Float[m_thetaBins*m_phiBins];
m_refTable = new Float[m_thetaBins*m_phiBins];
}
@ -110,10 +115,9 @@ public:
* on how to invoke this function
*/
void fill(
const boost::function<Vector (const Point2 &)> &sampleFn,
const boost::function<std::pair<Vector, Float>()> &sampleFn,
const boost::function<Float (const Vector &)> &pdfFn) {
ref<Random> random = new Random();
memset(m_table, 0, m_thetaBins*m_phiBins*sizeof(uint32_t));
memset(m_table, 0, m_thetaBins*m_phiBins*sizeof(Float));
SLog(EInfo, "Accumulating " SIZE_T_FMT " samples into a %ix%i"
" contingency table", m_sampleCount, m_thetaBins, m_phiBins);
@ -121,15 +125,15 @@ public:
ref<Timer> timer = new Timer();
for (size_t i=0; i<m_sampleCount; ++i) {
Point2 sample(random->nextFloat(), random->nextFloat());
Point2 sphCoords = toSphericalCoordinates(sampleFn(sample));
std::pair<Vector, Float> sample = sampleFn();
Point2 sphCoords = toSphericalCoordinates(sample.first);
int thetaBin = std::min(std::max(0,
floorToInt(sphCoords.x * factor.x)), m_thetaBins-1);
int phiBin = std::min(std::max(0,
floorToInt(sphCoords.y * factor.y)), m_phiBins-1);
++m_table[thetaBin * m_phiBins + phiBin];
m_table[thetaBin * m_phiBins + phiBin] += sample.second;
}
SLog(EInfo, "Done, took %i ms.", timer->getMilliseconds());
factor = Point2(M_PI / m_thetaBins, (2*M_PI) / m_phiBins);
@ -139,7 +143,7 @@ public:
Float min[2], max[2];
size_t idx = 0;
NDIntegrator integrator(1, 2, 50000, 0, 1e-6f);
NDIntegrator integrator(1, 2, 100000, 0, 1e-6f);
Float maxError = 0, integral = 0;
for (int i=0; i<m_thetaBins; ++i) {
min[0] = i * factor.x;
@ -269,7 +273,7 @@ protected:
private:
int m_thetaBins, m_phiBins;
size_t m_sampleCount;
uint32_t *m_table;
Float *m_table;
Float *m_refTable;
};

View File

@ -72,7 +72,7 @@ public:
* \brief Evaluate the phase function for an outward-pointing
* pair of directions (wi, wo)
*/
virtual Spectrum f(const PhaseFunctionQueryRecord &pRec) const = 0;
virtual Float f(const PhaseFunctionQueryRecord &pRec) const = 0;
/**
* \brief Importance sample the phase function.
@ -83,7 +83,7 @@ public:
* Weight value equal to the throughput divided by
* the probability of the sampled direction.
*/
virtual Spectrum sample(PhaseFunctionQueryRecord &pRec,
virtual Float sample(PhaseFunctionQueryRecord &pRec,
Sampler *sampler) const = 0;
/**
@ -95,7 +95,7 @@ public:
* \return
* Phase function value for the direction pair (wi, wo)
*/
virtual Spectrum sample(PhaseFunctionQueryRecord &pRec,
virtual Float sample(PhaseFunctionQueryRecord &pRec,
Float &pdf, Sampler *sampler) const = 0;
/**

View File

@ -81,9 +81,9 @@ public:
if (rRec.type & RadianceQueryRecord::EDirectMediumRadiance &&
scene->sampleAttenuatedLuminaire(mRec.p, ray.time, rRec.medium, lRec, rRec.nextSample2D())) {
/* Evaluate the phase function */
Spectrum phaseVal = phase->f(PhaseFunctionQueryRecord(mRec, -ray.d, -lRec.d));
Float phaseVal = phase->f(PhaseFunctionQueryRecord(mRec, -ray.d, -lRec.d));
if (!phaseVal.isZero()) {
if (phaseVal != 0) {
/* Calculate prob. of having sampled that direction using
phase function sampling */
Float phasePdf = (lRec.luminaire->isIntersectable()
@ -102,8 +102,8 @@ public:
Float phasePdf;
PhaseFunctionQueryRecord pRec(mRec, -ray.d);
Spectrum phaseVal = phase->sample(pRec, phasePdf, rRec.sampler);
if (phaseVal.isZero())
Float phaseVal = phase->sample(pRec, phasePdf, rRec.sampler);
if (phaseVal == 0)
break;
phaseVal /= phasePdf;

View File

@ -89,8 +89,8 @@ public:
/* ==================================================================== */
PhaseFunctionQueryRecord pRec(mRec, -ray.d);
Spectrum phaseVal = phase->sample(pRec, rRec.sampler);
if (phaseVal.isZero())
Float phaseVal = phase->sample(pRec, rRec.sampler);
if (phaseVal == 0)
break;
/* Trace a ray in this direction */

View File

@ -15,7 +15,7 @@ std::string PhaseFunctionQueryRecord::toString() const {
}
Float PhaseFunction::pdf(const PhaseFunctionQueryRecord &pRec) const {
return f(pRec)[0];
return f(pRec);
}
MTS_IMPLEMENT_CLASS(PhaseFunction, true, ConfigurableObject)

View File

@ -50,7 +50,7 @@ public:
stream->writeFloat(m_g);
}
inline Spectrum sample(PhaseFunctionQueryRecord &pRec,
inline Float sample(PhaseFunctionQueryRecord &pRec,
Sampler *sampler) const {
Point2 sample(sampler->next2D());
@ -71,21 +71,19 @@ public:
cosTheta);
pRec.wo = Frame(-pRec.wi).toWorld(dir);
return Spectrum(1.0f);
return 1.0f;
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Float sample(PhaseFunctionQueryRecord &pRec,
Float &pdf, Sampler *sampler) const {
HGPhaseFunction::sample(pRec, sampler);
pdf = HGPhaseFunction::f(pRec)[0];
return Spectrum(pdf);
return HGPhaseFunction::f(pRec);
}
Spectrum f(const PhaseFunctionQueryRecord &pRec) const {
return Spectrum(1/(4*M_PI) * (1 - m_g*m_g) /
std::pow(1.f + m_g*m_g - 2.f * m_g * dot(-pRec.wi, pRec.wo), (Float) 1.5f));
Float f(const PhaseFunctionQueryRecord &pRec) const {
return 1/(4*M_PI) * (1 - m_g*m_g) /
std::pow(1.f + m_g*m_g - 2.f * m_g * dot(-pRec.wi, pRec.wo), (Float) 1.5f);
}
std::string toString() const {

View File

@ -41,22 +41,22 @@ public:
PhaseFunction::serialize(stream, manager);
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Float sample(PhaseFunctionQueryRecord &pRec,
Sampler *sampler) const {
Point2 sample(sampler->next2D());
pRec.wo = squareToSphere(sample);
return Spectrum(1.0f);
return 1.0f;
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Float sample(PhaseFunctionQueryRecord &pRec,
Float &pdf, Sampler *sampler) const {
pRec.wo = squareToSphere(sampler->next2D());
pdf = 1/(4 * (Float) M_PI);
return Spectrum(pdf);
return pdf;
}
Spectrum f(const PhaseFunctionQueryRecord &pRec) const {
return Spectrum(1/(4 * (Float) M_PI));
Float f(const PhaseFunctionQueryRecord &pRec) const {
return 1/(4 * (Float) M_PI);
}
std::string toString() const {

View File

@ -79,13 +79,13 @@ public:
stream->writeFloat(m_exponent);
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Float sample(PhaseFunctionQueryRecord &pRec,
Sampler *sampler) const {
pRec.wo = squareToSphere(sampler->next2D());
return f(pRec) * (4 * M_PI);
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Float sample(PhaseFunctionQueryRecord &pRec,
Float &pdf, Sampler *sampler) const {
pRec.wo = squareToSphere(sampler->next2D());
pdf = 1/(4 * M_PI);
@ -96,9 +96,9 @@ public:
return 1/(4 * M_PI);
}
Spectrum f(const PhaseFunctionQueryRecord &pRec) const {
Float f(const PhaseFunctionQueryRecord &pRec) const {
if (pRec.mRec.orientation.length() == 0)
return Spectrum(m_kd / (4*M_PI));
return m_kd / (4*M_PI);
Frame frame(normalize(pRec.mRec.orientation));
Vector reflectedLocal = frame.toLocal(pRec.wo);
@ -110,8 +110,8 @@ public:
reflectedLocal.x *= a;
Vector R = frame.toWorld(reflectedLocal);
return Spectrum((std::pow(std::max((Float) 0, dot(R, pRec.wo)), m_exponent))
* m_normalization * m_ks + m_kd / (4*M_PI));
return std::pow(std::max((Float) 0, dot(R, pRec.wo)), m_exponent)
* m_normalization * m_ks + m_kd / (4*M_PI);
}
std::string toString() const {

View File

@ -17,20 +17,33 @@
*/
#include <mitsuba/core/chisquare.h>
#include <mitsuba/core/frame.h>
#include <mitsuba/render/phase.h>
#include <mitsuba/render/medium.h>
#include <mitsuba/render/sampler.h>
#define MICROFLAKE_STATISTICS 1
#include "microflake_fiber.h"
#include <mitsuba/core/plugin.h>///XXX
MTS_NAMESPACE_BEGIN
#if defined(MICROFLAKE_STATISTICS)
static StatsCounter avgSampleIterations("Micro-flake model",
"Average rejection sampling iterations", EAverage);
#endif
class MicroflakePhaseFunction : public PhaseFunction {
public:
MicroflakePhaseFunction(const Properties &props) : PhaseFunction(props) {
m_fiberDistr = GaussianFiberDistribution(props.getFloat("stddev"));
ChiSquareTest test(7);
Sampler *sampler = static_cast<Sampler *> (PluginManager::getInstance()->
createObject(MTS_CLASS(Sampler), Properties("independent")));
test.fill(
boost::bind(&GaussianFiberDistribution::sample, m_fiberDistr, _1),
boost::bind(&GaussianFiberDistribution::pdf, m_fiberDistr, _1)
boost::bind(&MicroflakePhaseFunction::testSample, this, sampler),
boost::bind(&MicroflakePhaseFunction::testF, this, _1)
);
test.dumpTables("test.m");
test.runTest(1);
@ -49,22 +62,69 @@ public:
void configure() {
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
Sampler *sampler) const {
Point2 sample(sampler->next2D());
pRec.wo = squareToSphere(sample);
return Spectrum(1.0f);
Float f(const PhaseFunctionQueryRecord &pRec) const {
if (pRec.mRec.orientation.isZero())
return 0.0f;
Frame frame(pRec.mRec.orientation);
Vector wi = frame.toLocal(pRec.wi);
Vector wo = frame.toLocal(pRec.wo);
Vector H = wi + wo;
Float length = H.length();
if (length == 0)
return 0.0f;
Float cosThetaH = H.z/length;
return 0.5 * m_fiberDistr.pdfCosTheta(cosThetaH)
/ m_fiberDistr.sigmaT(Frame::cosTheta(wi));
}
Spectrum sample(PhaseFunctionQueryRecord &pRec,
inline Float sample(PhaseFunctionQueryRecord &pRec, Sampler *sampler) const {
if (pRec.mRec.orientation.isZero())
return 0.0f;
Frame frame(pRec.mRec.orientation);
Vector wi = frame.toLocal(pRec.wi);
#if defined(MICROFLAKE_STATISTICS)
avgSampleIterations.incrementBase();
#endif
int iterations = 0, maxIterations = 1000;
while (true) {
Vector H = m_fiberDistr.sample(sampler->next2D());
#if defined(MICROFLAKE_STATISTICS)
++avgSampleIterations;
#endif
++iterations;
if (sampler->next1D() < absDot(wi, H)) {
Vector wo = H*(2*dot(wi, H)) - wi;
pRec.wo = frame.toWorld(wo);
break;
}
if (iterations >= maxIterations) {
Log(EWarn, "Sample generation unsuccessful after %i iterations"
" (dp=%f, fiberOrientation=%s, wi=%s)", iterations,
absDot(pRec.wi, pRec.mRec.orientation),
pRec.mRec.orientation.toString().c_str(),
pRec.wi.toString().c_str());
return 0.0f;
}
}
return 1.0f;
}
Float sample(PhaseFunctionQueryRecord &pRec,
Float &pdf, Sampler *sampler) const {
pRec.wo = squareToSphere(sampler->next2D());
pdf = 1/(4 * (Float) M_PI);
return Spectrum(pdf);
}
Spectrum f(const PhaseFunctionQueryRecord &pRec) const {
return Spectrum(1/(4 * (Float) M_PI));
if (sample(pRec, sampler) == 0) {
pdf = 0;
return 0.0f;
}
pdf = f(pRec);
return pdf;
}
std::string toString() const {

View File

@ -181,10 +181,10 @@ double sigmaT_fiberDist(double stddev, double sinTheta) {
return result;
}
static StatsCounter brentSolves("Micro-flake model",
"Brent solver calls");
#if defined(MICROFLAKE_STATISTICS)
static StatsCounter avgBrentFunEvals("Micro-flake model",
"Average Brent solver function evaluations", EAverage);
#endif
class GaussianFiberDistribution {
public:
@ -250,8 +250,10 @@ public:
boost::bind(&GaussianFiberDistribution::cdfFunctor,
this, sample.x, _1), -1, 1);
SAssert(result.success);
avgBrentFunEvals.incrementBase();
++brentSolves;
#if defined(MICROFLAKE_STATISTICS)
avgBrentFunEvals.incrementBase();
#endif
Float cosTheta = result.x,
sinTheta = std::sqrt(std::max((Float) 0, 1-cosTheta*cosTheta)),
@ -275,7 +277,9 @@ protected:
}
Float cdfFunctor(Float xi, Float cosTheta) const {
++avgBrentFunEvals;
#if defined(MICROFLAKE_STATISTICS)
++avgBrentFunEvals;
#endif
return cdf(cosTheta)-xi;
}