feat: impl random sampling instead of point light algorithm
Random sampling is available when building with `-DRANDOM_SAMPLING', or with `make randsampl'. The sample counts are rather low, so the output is both slow and noisy, which is why it's disabled by default.
This commit is contained in:
8
Makefile
8
Makefile
@@ -19,11 +19,15 @@ bin/raytracer: $(OBJ_FILES)
|
|||||||
@mkdir -p $(OBJ_DIR)
|
@mkdir -p $(OBJ_DIR)
|
||||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||||
|
|
||||||
|
.PHONY: randsampl
|
||||||
|
randsampl: CXXFLAGS += -DRANDOM_SAMPLING
|
||||||
|
randsampl: bin/raytracer
|
||||||
|
|
||||||
.PHONY: debug
|
.PHONY: debug
|
||||||
debug: CXXFLAGS += -O0 -g
|
debug: CXXFLAGS += -O0 -g
|
||||||
debug: clean bin/raytracer
|
debug: clean bin/raytracer
|
||||||
|
|
||||||
.PHONY: clean
|
.PHONY: clean
|
||||||
clean:
|
clean:
|
||||||
$(RM) -r obj/*.o
|
$(RM) obj/*.o
|
||||||
$(RM) bin/*
|
$(RM) bin/raytracer
|
||||||
|
1
README
1
README
@@ -9,6 +9,7 @@ The raytracer implements the following:
|
|||||||
- IOR-based refraction
|
- IOR-based refraction
|
||||||
- Cook-Torrance BRDF (a few other models are left in the code)
|
- Cook-Torrance BRDF (a few other models are left in the code)
|
||||||
- Primitive object materials
|
- Primitive object materials
|
||||||
|
- Random sampling (build with `make randsampl' to add fireflies :)
|
||||||
|
|
||||||
You can move around with WASD and SPC/Z; zoom with I/O, change the exposure with
|
You can move around with WASD and SPC/Z; zoom with I/O, change the exposure with
|
||||||
+/-, and quit with Q.
|
+/-, and quit with Q.
|
||||||
|
102
src/raytrace.cpp
102
src/raytrace.cpp
@@ -1,7 +1,36 @@
|
|||||||
#include "../include/raytrace.hpp"
|
#include "../include/raytrace.hpp"
|
||||||
#include "../include/objects.hpp"
|
#include "../include/objects.hpp"
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
|
||||||
|
#ifdef RANDOM_SAMPLING
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef RANDOM_SAMPLING
|
||||||
|
#define MAX(l, r) ((l) < (r)) ? (r) : (l)
|
||||||
|
#define MIN(l, r) ((r) < (l)) ? (r) : (l)
|
||||||
|
|
||||||
|
static inline v3 generate_normal(const v3 &v)
|
||||||
|
{
|
||||||
|
struct h {
|
||||||
|
choice_t x;
|
||||||
|
unsigned n;
|
||||||
|
bool operator<(const h& r) { return x < r.x; }
|
||||||
|
};
|
||||||
|
|
||||||
|
h one{v.x, 1}, two{v.y, 2}, three{v.z, 3};
|
||||||
|
auto max = MAX(one, MAX(two, three));
|
||||||
|
auto min = MIN(one, MAX(two, three));
|
||||||
|
unsigned middle = 6 - max.n - min.n;
|
||||||
|
|
||||||
|
v3 normal;
|
||||||
|
normal.c[max.n] = normal.c[middle];
|
||||||
|
normal.c[middle] = -max.x;
|
||||||
|
normal.c[min.n] = 0;
|
||||||
|
|
||||||
|
return normal;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
Renderable::Renderable(const Material& specs) : specs(specs) {}
|
Renderable::Renderable(const Material& specs) : specs(specs) {}
|
||||||
|
|
||||||
@@ -33,6 +62,12 @@ Renderable* shoot_ray_into_objects(const v3& eye, const v3& dir, const std::vect
|
|||||||
return hit_object;
|
return hit_object;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef RANDOM_SAMPLING
|
||||||
|
std::random_device rd;
|
||||||
|
std::mt19937 gen(rd());
|
||||||
|
std::uniform_real_distribution<> dis(0.0, 1.0);
|
||||||
|
#endif
|
||||||
|
|
||||||
v3 ray_trace(const v3& eye, const v3& dir,
|
v3 ray_trace(const v3& eye, const v3& dir,
|
||||||
const std::vector<Renderable*>& scene,
|
const std::vector<Renderable*>& scene,
|
||||||
const std::vector<Renderable*>& objects,
|
const std::vector<Renderable*>& objects,
|
||||||
@@ -56,7 +91,6 @@ v3 ray_trace(const v3& eye, const v3& dir,
|
|||||||
colour += ((Light*)hit_obj)->specs.colour * 8 * kDistance;
|
colour += ((Light*)hit_obj)->specs.colour * 8 * kDistance;
|
||||||
if(depth > 0) colour += ray_trace(eye, dir, scene, objects, lights, depth - 1, l) * (1 - kDistance);
|
if(depth > 0) colour += ray_trace(eye, dir, scene, objects, lights, depth - 1, l) * (1 - kDistance);
|
||||||
} else if (hit_type > 0) {
|
} else if (hit_type > 0) {
|
||||||
// Figure out which lights are visible from the hit location
|
|
||||||
choice_t dist;
|
choice_t dist;
|
||||||
v3 loc, normal;
|
v3 loc, normal;
|
||||||
v3 light_dir;
|
v3 light_dir;
|
||||||
@@ -64,22 +98,47 @@ v3 ray_trace(const v3& eye, const v3& dir,
|
|||||||
|
|
||||||
Light *l = nullptr;
|
Light *l = nullptr;
|
||||||
|
|
||||||
|
// Figure out which lights are visible from the hit location
|
||||||
for (auto* _l : lights) {
|
for (auto* _l : lights) {
|
||||||
l = (Light *)_l;
|
l = (Light *)_l;
|
||||||
const v3 to_light = l->o - hit_loc;
|
const v3 to_light = l->o - hit_loc;
|
||||||
light_dir = to_light.norm();
|
light_dir = to_light.norm();
|
||||||
dist = to_light.len();
|
dist = to_light.len();
|
||||||
shoot_ray_into_objects(hit_loc, light_dir, objects,
|
|
||||||
dist, loc, normal, type, hit_obj);
|
|
||||||
|
|
||||||
// We hit the light directly: shade the pixel.
|
v3 sample_dir = light_dir;
|
||||||
if (type == -1) {
|
#ifndef RANDOM_SAMPLING
|
||||||
const choice_t kDistance = to_light.lensquared() / 1000;
|
constexpr const int SAMPLE_COUNT = 1;
|
||||||
colour += cook_torrance(dir, hit_normal, light_dir, hit_obj->specs.specular,
|
#endif
|
||||||
hit_obj->specs.roughness, hit_obj->specs.ior,
|
|
||||||
hit_obj->specs.colour, l->specs.colour)
|
#ifdef RANDOM_SAMPLING
|
||||||
* (1 - hit_obj->specs.transparency) / kDistance;
|
v3 disc_n1 = generate_normal(light_dir).norm();
|
||||||
|
v3 disc_n2 = disc_n1.cross(light_dir);
|
||||||
|
choice_t cone_angle = asin(l->r / sqrt(l->r*l->r + dist*dist));
|
||||||
|
|
||||||
|
constexpr const int SAMPLE_COUNT = 5;
|
||||||
|
|
||||||
|
for(int i = 0; i < SAMPLE_COUNT; ++i) {
|
||||||
|
choice_t phi = dis(gen) * 2 * M_PI;
|
||||||
|
choice_t r = sqrt(dis(gen)) * l->r;
|
||||||
|
|
||||||
|
sample_dir = (((disc_n1 * cos(phi) + disc_n2 * sin(phi)) * r) + to_light).norm();
|
||||||
|
#endif
|
||||||
|
|
||||||
|
shoot_ray_into_objects(hit_loc, sample_dir, objects,
|
||||||
|
dist, loc, normal, type, hit_obj);
|
||||||
|
|
||||||
|
// We hit the light: shade the pixel.
|
||||||
|
if (type == -1) {
|
||||||
|
const choice_t kDistance = to_light.lensquared() / 1000;
|
||||||
|
colour += cook_torrance(dir, hit_normal, light_dir, hit_obj->specs.specular,
|
||||||
|
hit_obj->specs.roughness, hit_obj->specs.ior,
|
||||||
|
hit_obj->specs.colour, l->specs.colour)
|
||||||
|
* (1 - hit_obj->specs.transparency) / (kDistance * SAMPLE_COUNT);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef RANDOM_SAMPLING
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add the colour from the reflection, if we have enough depth
|
// Add the colour from the reflection, if we have enough depth
|
||||||
@@ -99,17 +158,18 @@ v3 ray_trace(const v3& eye, const v3& dir,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Random sample
|
|
||||||
// std::random_device rd;
|
#ifdef RANDOM_SAMPLING
|
||||||
// std::mt19937 gen(rd());
|
if(kReflectionAtt > 1e-4 && depth > 0) {
|
||||||
// std::uniform_real_distribution<> dis(0.0, 1.0);
|
const v3 y = hit_normal.cross(dir);
|
||||||
// const v3 y = hit_normal.cross(dir);
|
for(int i = 0; i < 3; ++i) {
|
||||||
// for(int i = 0; i < 2; ++i) {
|
const v3 sample_dir = (hit_normal + dir * dis(gen) + y * dis(gen)).norm();
|
||||||
// const v3 sample_dir = (hit_normal + dir * dis(gen) + y * dis(gen)).norm();
|
const v3 hit_colour = ray_trace(hit_loc, sample_dir, scene, objects, lights, 0, hit_obj, &dist);
|
||||||
// choice_t dist = 1;
|
colour += hit_colour * kReflectionAtt / 3;
|
||||||
// const v3 hit_colour = ray_trace(hit_loc, sample_dir, scene, objects, lights, 0, hit_obj, &dist);
|
}
|
||||||
// colour += hit_colour / (dist * dist);
|
}
|
||||||
// }
|
#endif
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return colour;
|
return colour;
|
||||||
|
Reference in New Issue
Block a user