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:
parent
e7bc9882e6
commit
3827be61ac
8
Makefile
8
Makefile
|
@ -19,11 +19,15 @@ bin/raytracer: $(OBJ_FILES)
|
|||
@mkdir -p $(OBJ_DIR)
|
||||
$(CXX) $(CXXFLAGS) -o $@ $^
|
||||
|
||||
.PHONY: randsampl
|
||||
randsampl: CXXFLAGS += -DRANDOM_SAMPLING
|
||||
randsampl: bin/raytracer
|
||||
|
||||
.PHONY: debug
|
||||
debug: CXXFLAGS += -O0 -g
|
||||
debug: clean bin/raytracer
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -r obj/*.o
|
||||
$(RM) bin/*
|
||||
$(RM) obj/*.o
|
||||
$(RM) bin/raytracer
|
||||
|
|
1
README
1
README
|
@ -9,6 +9,7 @@ The raytracer implements the following:
|
|||
- IOR-based refraction
|
||||
- Cook-Torrance BRDF (a few other models are left in the code)
|
||||
- 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
|
||||
+/-, and quit with Q.
|
||||
|
|
|
@ -1,7 +1,36 @@
|
|||
#include "../include/raytrace.hpp"
|
||||
#include "../include/objects.hpp"
|
||||
#include <math.h>
|
||||
|
||||
#ifdef RANDOM_SAMPLING
|
||||
#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) {}
|
||||
|
||||
|
@ -33,6 +62,12 @@ Renderable* shoot_ray_into_objects(const v3& eye, const v3& dir, const std::vect
|
|||
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,
|
||||
const std::vector<Renderable*>& scene,
|
||||
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;
|
||||
if(depth > 0) colour += ray_trace(eye, dir, scene, objects, lights, depth - 1, l) * (1 - kDistance);
|
||||
} else if (hit_type > 0) {
|
||||
// Figure out which lights are visible from the hit location
|
||||
choice_t dist;
|
||||
v3 loc, normal;
|
||||
v3 light_dir;
|
||||
|
@ -64,22 +98,47 @@ v3 ray_trace(const v3& eye, const v3& dir,
|
|||
|
||||
Light *l = nullptr;
|
||||
|
||||
// Figure out which lights are visible from the hit location
|
||||
for (auto* _l : lights) {
|
||||
l = (Light *)_l;
|
||||
const v3 to_light = l->o - hit_loc;
|
||||
light_dir = to_light.norm();
|
||||
dist = to_light.len();
|
||||
shoot_ray_into_objects(hit_loc, light_dir, objects,
|
||||
|
||||
v3 sample_dir = light_dir;
|
||||
#ifndef RANDOM_SAMPLING
|
||||
constexpr const int SAMPLE_COUNT = 1;
|
||||
#endif
|
||||
|
||||
#ifdef RANDOM_SAMPLING
|
||||
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 directly: shade the pixel.
|
||||
// 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;
|
||||
* (1 - hit_obj->specs.transparency) / (kDistance * SAMPLE_COUNT);
|
||||
}
|
||||
|
||||
#ifdef RANDOM_SAMPLING
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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;
|
||||
// std::mt19937 gen(rd());
|
||||
// std::uniform_real_distribution<> dis(0.0, 1.0);
|
||||
// const v3 y = hit_normal.cross(dir);
|
||||
// for(int i = 0; i < 2; ++i) {
|
||||
// const v3 sample_dir = (hit_normal + dir * dis(gen) + y * dis(gen)).norm();
|
||||
// choice_t dist = 1;
|
||||
// const v3 hit_colour = ray_trace(hit_loc, sample_dir, scene, objects, lights, 0, hit_obj, &dist);
|
||||
// colour += hit_colour / (dist * dist);
|
||||
// }
|
||||
|
||||
#ifdef RANDOM_SAMPLING
|
||||
if(kReflectionAtt > 1e-4 && depth > 0) {
|
||||
const v3 y = hit_normal.cross(dir);
|
||||
for(int i = 0; i < 3; ++i) {
|
||||
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);
|
||||
colour += hit_colour * kReflectionAtt / 3;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
return colour;
|
||||
|
|
Loading…
Reference in New Issue