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 bool belowSurface =
false;
126 for (
int i=0; i<m_sampleCount; ++i) {
127 Point2f sample(random.nextFloat(), random.nextFloat());
128 Color3f result = bsdf->sample(bRec, sample);
130 if ((result.array() == 0).all())
137 int cosThetaBin = std::min(std::max(0, (
int) std::floor((bRec.
wo.z()*0.5f+0.5f)
138 * m_cosThetaResolution)), m_cosThetaResolution-1);
140 float scaledPhi = std::atan2(bRec.
wo.y(), bRec.
wo.x()) * INV_TWOPI;
144 int phiBin = std::min(std::max(0,
145 (
int) std::floor(scaledPhi * m_phiResolution)), m_phiResolution-1);
146 obsFrequencies[cosThetaBin * m_phiResolution + phiBin] += 1;
149 cerr <<
"Warning: some samples are below the surface (wo.z < 0). "
150 <<
"Make sure the `sample` method returns Color3f(0) in such cases to reject these bad samples!" << endl;
151 cout <<
"done." << endl;
155 double *ptr = expFrequencies.get();
156 cout <<
"Integrating expected frequencies .. ";
158 for (
int i=0; i<m_cosThetaResolution; ++i) {
159 double cosThetaStart = -1.0 + i * 2.0 / m_cosThetaResolution;
160 double cosThetaEnd = -1.0 + (i+1) * 2.0 / m_cosThetaResolution;
161 for (
int j=0; j<m_phiResolution; ++j) {
162 double phiStart = j * 2*M_PI / m_phiResolution;
163 double phiEnd = (j+1) * 2*M_PI / m_phiResolution;
165 auto integrand = [&](
double cosTheta,
double phi) ->
double {
166 double sinTheta = std::sqrt(1 - cosTheta * cosTheta);
167 double sinPhi = std::sin(phi), cosPhi = std::cos(phi);
169 Vector3f wo((
float) (sinTheta * cosPhi),
170 (
float) (sinTheta * sinPhi),
174 return bsdf->pdf(bRec);
177 double integral = hypothesis::adaptiveSimpson2D(
178 integrand, cosThetaStart, phiStart, cosThetaEnd,
181 *ptr++ = integral * m_sampleCount;
184 cout <<
"done." << endl;
187 hypothesis::chi2_dump(m_cosThetaResolution, m_phiResolution, obsFrequencies.get(), expFrequencies.get(),
188 tfm::format(
"chi2test_%i.py", total));
191 std::pair<bool, std::string> result =
192 hypothesis::chi2_test(m_cosThetaResolution*m_phiResolution, obsFrequencies.get(), expFrequencies.get(),
193 m_sampleCount, m_minExpFrequency, m_significanceLevel, m_testCount * (
int) m_bsdfs.size());
198 cout << result.second << endl;
202 cout <<
"Passed " << passed <<
"/" << total <<
" tests." << endl;
204 if (passed < total) {
205 throw std::runtime_error(
"Failed some of the tests");
210 return tfm::format(
"ChiSquareTest[\n"
211 " thetaResolution = %i,\n"
212 " phiResolution = %i,\n"
213 " minExpFrequency = %i,\n"
214 " sampleCount = %i,\n"
216 " significanceLevel = %f\n"
218 m_cosThetaResolution,
229 int m_cosThetaResolution;
231 int m_minExpFrequency;
234 float m_significanceLevel;
235 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.