19 #include <nori/bsdf.h>
20 #include <nori/warp.h>
22 #include <hypothesis.h>
47 m_significanceLevel = propList.
getFloat(
"significanceLevel", 0.01f);
51 m_cosThetaResolution = propList.
getInteger(
"resolution", 10);
59 m_minExpFrequency = propList.
getInteger(
"minExpFrequency", 5);
62 m_sampleCount = propList.
getInteger(
"sampleCount", -1);
67 m_testCount = propList.
getInteger(
"testCount", 5);
69 m_phiResolution = 2 * m_cosThetaResolution;
71 if (m_sampleCount < 0)
72 m_sampleCount = m_cosThetaResolution * m_phiResolution * 5000;
76 for (
auto bsdf : m_bsdfs)
83 m_bsdfs.push_back(
static_cast<BSDF *
>(obj));
87 throw NoriException(
"ChiSquareTest::addChild(<%s>) is not supported!",
94 int passed = 0, total = 0, res = m_cosThetaResolution*m_phiResolution;
97 std::unique_ptr<double[]> obsFrequencies(
new double[res]);
98 std::unique_ptr<double[]> expFrequencies(
new double[res]);
102 for (
auto bsdf : m_bsdfs) {
104 for (
int l = 0; l<m_testCount; ++l) {
105 memset(obsFrequencies.get(), 0, res*
sizeof(
double));
106 memset(expFrequencies.get(), 0, res*
sizeof(
double));
108 cout <<
"------------------------------------------------------" << endl;
109 cout <<
"Testing: " << bsdf->toString() << endl;
112 float cosTheta = random.nextFloat();
113 float sinTheta = std::sqrt(std::max((
float) 0, 1-cosTheta*cosTheta));
114 float sinPhi, cosPhi;
115 sincosf(2.0f * M_PI * random.nextFloat(), &sinPhi, &cosPhi);
116 Vector3f wi(cosPhi * sinTheta, sinPhi * sinTheta, cosTheta);
118 cout <<
"Accumulating " << m_sampleCount <<
" samples into a " << m_cosThetaResolution
119 <<
"x" << m_phiResolution <<
" contingency table .. ";
125 for (
int i=0; i<m_sampleCount; ++i) {
126 Point2f sample(random.nextFloat(), random.nextFloat());
127 Color3f result = bsdf->sample(bRec, sample);
129 if ((result.array() == 0).all())
132 int cosThetaBin = std::min(std::max(0, (
int) std::floor((bRec.
wo.z()*0.5f+0.5f)
133 * m_cosThetaResolution)), m_cosThetaResolution-1);
135 float scaledPhi = std::atan2(bRec.
wo.y(), bRec.
wo.x()) * INV_TWOPI;
139 int phiBin = std::min(std::max(0,
140 (
int) std::floor(scaledPhi * m_phiResolution)), m_phiResolution-1);
141 obsFrequencies[cosThetaBin * m_phiResolution + phiBin] += 1;
143 cout <<
"done." << endl;
147 double *ptr = expFrequencies.get();
148 cout <<
"Integrating expected frequencies .. ";
150 for (
int i=0; i<m_cosThetaResolution; ++i) {
151 double cosThetaStart = -1.0 + i * 2.0 / m_cosThetaResolution;
152 double cosThetaEnd = -1.0 + (i+1) * 2.0 / m_cosThetaResolution;
153 for (
int j=0; j<m_phiResolution; ++j) {
154 double phiStart = j * 2*M_PI / m_phiResolution;
155 double phiEnd = (j+1) * 2*M_PI / m_phiResolution;
157 auto integrand = [&](
double cosTheta,
double phi) ->
double {
158 double sinTheta = std::sqrt(1 - cosTheta * cosTheta);
159 double sinPhi = std::sin(phi), cosPhi = std::cos(phi);
161 Vector3f wo((
float) (sinTheta * cosPhi),
162 (
float) (sinTheta * sinPhi),
166 return bsdf->pdf(bRec);
169 double integral = hypothesis::adaptiveSimpson2D(
170 integrand, cosThetaStart, phiStart, cosThetaEnd,
173 *ptr++ = integral * m_sampleCount;
176 cout <<
"done." << endl;
179 hypothesis::chi2_dump(m_cosThetaResolution, m_phiResolution, obsFrequencies.get(), expFrequencies.get(),
180 tfm::format(
"chi2test_%i.m", total));
183 std::pair<bool, std::string> result =
184 hypothesis::chi2_test(m_cosThetaResolution*m_phiResolution, obsFrequencies.get(), expFrequencies.get(),
185 m_sampleCount, m_minExpFrequency, m_significanceLevel, m_testCount * (
int) m_bsdfs.size());
190 cout << result.second << endl;
194 cout <<
"Passed " << passed <<
"/" << total <<
" tests." << endl;
196 if (passed < total) {
197 throw std::runtime_error(
"Failed some of the tests");
202 return tfm::format(
"ChiSquareTest[\n"
203 " thetaResolution = %i,\n"
204 " phiResolution = %i,\n"
205 " minExpFrequency = %i,\n"
206 " sampleCount = %i,\n"
208 " significanceLevel = %f\n"
210 m_cosThetaResolution,
221 int m_cosThetaResolution;
223 int m_minExpFrequency;
226 float m_significanceLevel;
227 std::vector<BSDF *> m_bsdfs;
Superclass of all bidirectional scattering distribution functions.
Statistical test for validating that an importance sampling routine (e.g. from a BSDF) produces a dis...
virtual EClassType getClassType() const override
Return the type of object (i.e. Mesh/BSDF/etc.) provided by this instance.
virtual std::string toString() const override
Return a brief string summary of the instance (for debugging purposes)
virtual void addChild(NoriObject *obj) override
Add a child object to the current instance.
virtual void activate() override
Execute the chi-square test.
Simple exception class, which stores a human-readable error description.
Base class of all objects.
static std::string classTypeName(EClassType type)
Turn a class type into a human-readable string.
virtual EClassType getClassType() const =0
Return the type of object (i.e. Mesh/BSDF/etc.) provided by this instance.
This is an associative container used to supply the constructors of NoriObject subclasses with parame...
float getFloat(const std::string &name) const
Get a float property, and throw an exception if it does not exist.
int getInteger(const std::string &name) const
Get an integer property, and throw an exception if it does not exist.
Convenience data structure used to pass multiple parameters to the evaluation and sampling routines i...
Vector3f wo
Outgoing direction (in the local frame)
Represents a linear RGB color value.