19 #include <nori/scene.h>
20 #include <nori/bsdf.h>
21 #include <nori/camera.h>
22 #include <nori/integrator.h>
23 #include <nori/sampler.h>
24 #include <hypothesis.h>
66 m_significanceLevel = propList.
getFloat(
"significanceLevel", 0.01f);
69 std::vector<std::string> angles = tokenize(propList.
getString(
"angles",
""));
70 for (
auto angle : angles)
71 m_angles.push_back(toFloat(angle));
74 std::vector<std::string> distances = tokenize(propList.
getString(
"distances",
""));
75 for (
auto distance : distances)
76 m_distances.push_back(toFloat(distance));
79 std::vector<std::string> references = tokenize(propList.
getString(
"references",
""));
80 for (
auto angle : references)
81 m_references.push_back(toFloat(angle));
84 m_sampleCount = propList.
getInteger(
"sampleCount", 100000);
88 for (
auto bsdf : m_bsdfs)
90 for (
auto emitter : m_emitters)
92 for (
auto scene : m_scenes)
99 m_bsdfs.push_back(
static_cast<BSDF *
>(obj));
103 m_emitters.push_back(
static_cast<Emitter *
>(obj));
108 m_scenes.push_back(
static_cast<Scene *
>(obj));
112 throw NoriException(
"StudentsTTest::addChild(<%s>) is not supported!",
119 int total = 0, passed = 0;
122 if (!m_bsdfs.empty()) {
123 if (m_references.size() != m_bsdfs.size() * m_angles.size())
124 throw NoriException(
"Specified a different number of angles and reference values! %d != %d x %d",
125 m_references.size(), m_bsdfs.size(), m_angles.size());
126 if (!m_emitters.empty() || !m_scenes.empty())
127 throw NoriException(
"Cannot test BSDFs, emitters, and scenes at the same time!");
131 for (
auto bsdf : m_bsdfs) {
132 for (
float angle : m_angles) {
133 float reference = m_references[ctr++];
135 cout <<
"------------------------------------------------------" << endl;
136 cout <<
"Testing (angle=" << angle <<
"): " << bsdf->toString() << endl;
141 cout <<
"Drawing " << m_sampleCount <<
" samples .. " << endl;
142 double mean=0, variance = 0;
143 for (
int k=0; k<m_sampleCount; ++k) {
144 Point2f sample(random.nextFloat(), random.nextFloat());
145 double result = (double) bsdf->sample(bRec, sample).getLuminance();
149 double delta = result - mean;
150 mean += delta / (double) (k+1);
151 variance += delta * (result - mean);
153 variance /= m_sampleCount - 1;
154 std::pair<bool, std::string>
155 result = hypothesis::students_t_test(mean, variance, reference,
156 m_sampleCount, m_significanceLevel, (
int) m_references.size());
160 cout << result.second << endl;
163 }
else if (!m_emitters.empty()) {
164 if (m_references.size() != m_emitters.size() * m_distances.size())
165 throw NoriException(
"Specified a different number of distances and reference values! %d != %d x %d",
166 m_references.size(), m_emitters.size(), m_distances.size());
167 if (!m_bsdfs.empty() || !m_scenes.empty())
168 throw NoriException(
"Cannot test BSDFs, emitters, and scenes at the same time!");
172 for (
auto emitter : m_emitters) {
173 for (
float distance : m_distances) {
174 float reference = m_references[ctr++];
176 cout <<
"------------------------------------------------------" << endl;
177 cout <<
"Testing (distance=" << distance <<
"): " << emitter->toString() << endl;
183 cout <<
"Drawing " << m_sampleCount <<
" samples .. " << endl;
184 double mean=0, variance = 0;
185 bool fields_correct =
true;
186 bool eval_correct =
true;
187 bool pdf_correct =
true;
189 for (
int k=0; k<m_sampleCount; ++k) {
190 Point2f sample(random.nextFloat(), random.nextFloat());
191 double result = (double) emitter->sample(lRec, sample).getLuminance();
197 return (a - b).squaredNorm() < 1e-5f;
199 if (!vectorEqual(ref, lRec.
ref)) {
200 fields_correct =
false;
201 reason =
"`ref` is incorrect";
203 else if (!vectorEqual(wi, lRec.
wi)) {
204 fields_correct =
false;
205 reason =
"`wi` is incorrect";
208 fields_correct =
false;
209 reason =
"`shadowRay.o` or `shadowRay.d` is incorrect";
212 fields_correct =
false;
213 reason =
"`shadowRay.maxt` - `shadowRay.mint` should equal to the distance between `ref` and `p`";
215 if (std::abs((
double) emitter->eval(lRec).getLuminance() / lRec.
pdf - result) > 1e-6) {
216 eval_correct =
false;
218 else if (std::abs((
double) emitter->pdf(lRec) - lRec.
pdf) > 1e-6) {
224 double delta = result - mean;
225 mean += delta / (double) (k+1);
226 variance += delta * (result - mean);
229 variance /= m_sampleCount - 1;
230 std::pair<bool, std::string>
231 result = hypothesis::students_t_test(mean, variance, reference,
232 m_sampleCount, m_significanceLevel, (
int) m_references.size());
234 cout << result.second << endl;
237 cout <<
"The return of `sample` method is incorrect" << endl;
240 if (!fields_correct) {
241 cout <<
"EmitterQueryRecord fields are not set correctly during sampling: " << reason << endl;
245 cout <<
"Either `eval` method is wrongly implemented or `pdf` field is incorrectly set" << endl;
249 cout <<
"Either `pdf` method is wrongly implemented or `pdf` field is incorrectly set" << endl;
257 if (m_references.size() != m_scenes.size())
258 throw NoriException(
"Specified a different number of scenes and reference values!");
259 if (!m_bsdfs.empty() || !m_emitters.empty())
260 throw NoriException(
"Cannot test BSDFs, emitters, and scenes at the same time!");
266 for (
auto scene : m_scenes) {
267 const Integrator *integrator = scene->getIntegrator();
268 const Camera *camera = scene->getCamera();
269 float reference = m_references[ctr++];
271 cout <<
"------------------------------------------------------" << endl;
272 cout <<
"Testing scene: " << scene->toString() << endl;
275 cout <<
"Generating " << m_sampleCount <<
" paths.. " << endl;
277 double mean = 0, variance = 0;
278 for (
int k=0; k<m_sampleCount; ++k) {
286 value *= integrator->
Li(scene, sampler, ray);
291 double delta = result - mean;
292 mean += delta / (double) (k+1);
293 variance += delta * (result - mean);
295 variance /= m_sampleCount - 1;
297 std::pair<bool, std::string>
298 result = hypothesis::students_t_test(mean, variance, reference,
299 m_sampleCount, m_significanceLevel, (
int) m_references.size());
303 cout << result.second << endl;
306 cout <<
"Passed " << passed <<
"/" << total <<
" tests." << endl;
308 if (passed < total) {
309 throw std::runtime_error(
"Failed some of the tests");
316 " significanceLevel = %f,\n"
326 std::vector<BSDF *> m_bsdfs;
327 std::vector<Emitter *> m_emitters;
328 std::vector<Scene *> m_scenes;
329 std::vector<float> m_angles;
330 std::vector<float> m_distances;
331 std::vector<float> m_references;
332 float m_significanceLevel;
Superclass of all bidirectional scattering distribution functions.
Generic camera interface.
const Vector2i & getOutputSize() const
Return the size of the output image in pixels.
virtual Color3f sampleRay(Ray3f &ray, const Point2f &samplePosition, const Point2f &apertureSample) const =0
Importance sample a ray according to the camera's response function.
Superclass of all emitters.
Abstract integrator (i.e. a rendering technique)
virtual Color3f Li(const Scene *scene, Sampler *sampler, const Ray3f &ray) const =0
Sample the incident radiance along a ray.
Simple exception class, which stores a human-readable error description.
static NoriObject * createInstance(const std::string &name, const PropertyList &propList)
Construct an instance from the class of the given name.
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.
std::string getString(const std::string &name) const
Get a string 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.
Abstract sample generator.
virtual Point2f next2D()=0
Retrieve the next two component values from the current sample.
Main scene data structure.
virtual void activate() override
Invoke a series of t-tests on the provided input.
virtual void addChild(NoriObject *obj) override
Add a child object to the current instance.
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)
Convenience data structure used to pass multiple parameters to the evaluation and sampling routines i...
Represents a linear RGB color value.
float getLuminance() const
Return the associated luminance.
Data record for conveniently querying and sampling the direct illumination technique implemented by a...
Vector3f wi
Direction between the hit point and the emitter point.
Point3f ref
Origin point from which we sample the emitter.
Ray3f shadowRay
Shadow ray.
Point3f p
Sampled point on the emitter.
VectorType d
Ray direction.
Scalar mint
Minimum position on the ray segment.
Scalar maxt
Maximum position on the ray segment.