Compare commits

...

2 Commits

Author SHA1 Message Date
dvdrw 294cd72ee7
tweak lights to make scene more interesting 2023-12-03 21:56:53 +01:00
dvdrw 3827be61ac
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.
2023-12-03 21:20:49 +01:00
4 changed files with 95 additions and 31 deletions

View File

@ -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
View File

@ -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.

View File

@ -11,8 +11,8 @@
#include <algorithm> #include <algorithm>
static std::vector<Renderable*> lights { static std::vector<Renderable*> lights {
new Light(v3{0, 0, 19}, 2, v3{1, 1, 1}), new Light(v3{0, 0, 19}, 2, v3{2, 1, 1}),
new Light(v3{-7, 15, 0}, 2, v3{1, 1, 1}), new Light(v3{-7, 15, 0}, 2, v3{1, 2, 2}),
new Light(v3{17, -9, 16}, 2, v3{1, 1, 1}), new Light(v3{17, -9, 16}, 2, v3{1, 1, 1}),
// new Light(v3{0, 0, 19}, 2, v3{0.8, 0.8, 0.9}), // new Light(v3{0, 0, 19}, 2, v3{0.8, 0.8, 0.9}),
@ -22,17 +22,17 @@ static std::vector<Renderable*> lights {
static const Material PLANE_MAT = { static const Material PLANE_MAT = {
.specular = 0.12, .specular = 0.12,
.roughness = 0.55, .roughness = 0.75,
.ior = 2, .ior = 2,
.transparency = 0, .transparency = 0,
.colour = {1, 1, 1}, .colour = {1, 0, 1},
}; };
static const Material SPHERE_MAT_TRANS = { static const Material SPHERE_MAT_TRANS = {
.specular = 0.85, .specular = 0.85,
.roughness = 0.5, .roughness = 0.5,
.ior = 1.2, .ior = 1.2,
.transparency = 0.95, .transparency = 0.75,
.colour = {0, 1, 0}, .colour = {0, 1, 0},
}; };
@ -60,8 +60,6 @@ static std::vector<Renderable*> objects
static std::vector<Renderable*> scene; static std::vector<Renderable*> scene;
// static constexpr int W = 294*2, H = 82*2;
// static constexpr int W = 2 * 640 * 2.9 * 1.8, H = 2 * 480 * 1.5 * 2.5;
static constexpr int W = 2*80, H = 2*24; static constexpr int W = 2*80, H = 2*24;
static constexpr choice_t ASPECT_RATIO = W/(H * 1.9); static constexpr choice_t ASPECT_RATIO = W/(H * 1.9);
@ -152,7 +150,8 @@ int main()
camera.zoom /= 1.015; camera.zoom /= 1.015;
break; break;
case 'p': case 'p':
printf("pos: " V3_FMT "\nrot: " V3_FMT, V3_ARG(camera.pos), V3_ARG(camera.rotation)); printf("pos: " V3_FMT, V3_ARG(camera.pos));
printf("\nrot: " V3_FMT, V3_ARG(camera.rotation));
break; break;
case '\n': case '\n':
case 'q': case 'q':

View File

@ -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;