microflake bugfixes
parent
d3ad21f0da
commit
9f58f35623
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue