Commit ae2b9dc5 authored by TheNumbat's avatar TheNumbat
Browse files

Release new version

Features:
    - Particle systems can now specify a maximum dt per step
    - Animation key-framing & timing system now supports objects with simulation
    - Mixture/multiple importance sampling for correct low-variance direct lighting
        - New BSDF, point light, and environment light APIs that separate sampling, evaluation, and pdf
        - Area light sampling infrastructure
        - Removed rectangle area lights; all area lights are now emissive meshes
        - Reworked PathTracer tasks 4-6, adjusted/improved instructions for the other tasks

Bug fixes:
    - Use full rgb/srgb conversion equation instead of approximation
    - Material albedo now specified in srgb (matching the displayed color)
    - ImGui input fields becoming inactive no longer apply to a newly selected object
    - Rendering animations with path tracing correctly steps simulations each frame
    - Rasterization based renderer no longer inherits projection matrix from window
    - Scene file format no longer corrupts particle emitter enable states
    - Documentation videos no longer autoplay
    - Misc. refactoring
    - Misc. documentation website improvements
parent afa3f68f
...@@ -24,11 +24,7 @@ bool Simulate::keydown(Widgets& widgets, Undo& undo, SDL_Keysym key) { ...@@ -24,11 +24,7 @@ bool Simulate::keydown(Widgets& widgets, Undo& undo, SDL_Keysym key) {
} }
void Simulate::step(Scene& scene, float dt) { void Simulate::step(Scene& scene, float dt) {
scene.for_items([this, dt](Scene_Item& item) { scene.for_items([this, dt](Scene_Item& item) { item.step(scene_obj, dt); });
if(item.is<Scene_Particles>()) {
item.get<Scene_Particles>().step(scene_bvh, dt);
}
});
} }
void Simulate::update_time() { void Simulate::update_time() {
...@@ -48,14 +44,7 @@ void Simulate::update(Scene& scene, Undo& undo) { ...@@ -48,14 +44,7 @@ void Simulate::update(Scene& scene, Undo& undo) {
float dt = clamp((float)(udt / freq), 0.0f, 0.05f); float dt = clamp((float)(udt / freq), 0.0f, 0.05f);
last_update = time; last_update = time;
scene.for_items([this, dt](Scene_Item& item) { step(scene, dt);
if(item.is<Scene_Particles>()) {
Scene_Particles& particles = item.get<Scene_Particles>();
if(particles.opt.enabled) {
particles.step(scene_bvh, dt);
}
}
});
} }
void Simulate::render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam) { void Simulate::render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam) {
...@@ -68,52 +57,46 @@ void Simulate::render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam) { ...@@ -68,52 +57,46 @@ void Simulate::render(Scene_Maybe obj_opt, Widgets& widgets, Camera& cam) {
if(light.is_env()) return; if(light.is_env()) return;
} }
Pose& pose = item.pose();
float scale = std::min((cam.pos() - pose.pos).norm() / 5.5f, 10.0f);
Mat4 view = cam.get_view(); Mat4 view = cam.get_view();
item.render(view); item.render(view);
Renderer::get().outline(view, item); Renderer::get().outline(view, item);
Pose& pose = item.pose();
float scale = std::min((cam.pos() - pose.pos).norm() / 5.5f, 10.0f);
widgets.render(view, pose.pos, scale); widgets.render(view, pose.pos, scale);
} }
void Simulate::build_scene(Scene& scene) { void Simulate::build_scene(Scene& scene) {
if(!scene.has_particles()) return; if(!scene.has_sim()) return;
std::mutex obj_mut;
std::vector<PT::Object> obj_list; std::vector<PT::Object> obj_list;
std::vector<std::future<PT::Object>> futures;
scene.for_items([&, this](Scene_Item& item) { scene.for_items([&, this](Scene_Item& item) {
if(item.is<Scene_Object>()) { if(item.is<Scene_Object>()) {
Scene_Object& obj = item.get<Scene_Object>(); Scene_Object& obj = item.get<Scene_Object>();
thread_pool.enqueue([&]() { futures.push_back(thread_pool.enqueue([&]() {
if(obj.is_shape()) { if(obj.is_shape()) {
PT::Shape shape(obj.opt.shape); PT::Shape shape(obj.opt.shape);
std::lock_guard<std::mutex> lock(obj_mut); return PT::Object(std::move(shape), obj.id(), 0, obj.pose.transform());
obj_list.push_back(
PT::Object(std::move(shape), obj.id(), 0, obj.pose.transform()));
} else { } else {
PT::Tri_Mesh mesh(obj.posed_mesh()); PT::Tri_Mesh mesh(obj.posed_mesh(), use_bvh);
std::lock_guard<std::mutex> lock(obj_mut); return PT::Object(std::move(mesh), obj.id(), 0, obj.pose.transform());
obj_list.push_back(
PT::Object(std::move(mesh), obj.id(), 0, obj.pose.transform()));
} }
}); }));
} else if(item.is<Scene_Light>()) {
Scene_Light& light = item.get<Scene_Light>();
if(light.opt.type != Light_Type::rectangle) return;
PT::Tri_Mesh mesh(Util::quad_mesh(light.opt.size.x, light.opt.size.y));
std::lock_guard<std::mutex> lock(obj_mut);
obj_list.push_back(PT::Object(std::move(mesh), light.id(), 0, light.pose.transform()));
} }
}); });
thread_pool.wait(); for(auto& f : futures) {
scene_bvh.build(std::move(obj_list)); obj_list.push_back(f.get());
}
if(use_bvh) {
scene_obj = PT::Object(PT::BVH<PT::Object>(std::move(obj_list)));
} else {
scene_obj = PT::Object(PT::List<PT::Object>(std::move(obj_list)));
}
} }
void Simulate::clear_particles(Scene& scene) { void Simulate::clear_particles(Scene& scene) {
...@@ -143,6 +126,13 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi ...@@ -143,6 +126,13 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi
update_bvh(scene, undo); update_bvh(scene, undo);
ImGui::Text("Simulation");
if(ImGui::Checkbox("Use BVH", &use_bvh)) {
clear_particles(scene);
build_scene(scene);
}
if(ImGui::CollapsingHeader("Add New Emitter")) { if(ImGui::CollapsingHeader("Add New Emitter")) {
ImGui::PushID(0); ImGui::PushID(0);
...@@ -196,7 +186,7 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi ...@@ -196,7 +186,7 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi
mesh = Util::sphere_mesh(1.0f, 1); mesh = Util::sphere_mesh(1.0f, 1);
} break; } break;
case Solid_Type::custom: { case Solid_Type::custom: {
mesh = scene.get_obj(ids[name_idx]).mesh().copy(); mesh = scene.get<Scene_Object>(ids[name_idx]).mesh().copy();
} break; } break;
default: break; default: break;
} }
...@@ -208,17 +198,12 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi ...@@ -208,17 +198,12 @@ Mode Simulate::UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& wi
particles.opt.lifetime = gui_opt.lifetime; particles.opt.lifetime = gui_opt.lifetime;
particles.opt.pps = gui_opt.pps; particles.opt.pps = gui_opt.pps;
particles.opt.enabled = gui_opt.enabled; particles.opt.enabled = gui_opt.enabled;
undo.add_particles(std::move(particles)); undo.add(std::move(particles));
} }
ImGui::PopID(); ImGui::PopID();
} }
if(ImGui::Button("Generate BVH")) {
clear_particles(scene);
build_scene(scene);
}
return mode; return mode;
} }
......
...@@ -35,7 +35,9 @@ public: ...@@ -35,7 +35,9 @@ public:
Mode UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& widgets, Scene_Maybe obj); Mode UIsidebar(Manager& manager, Scene& scene, Undo& undo, Widgets& widgets, Scene_Maybe obj);
private: private:
PT::BVH<PT::Object> scene_bvh; PT::Object scene_obj;
bool use_bvh = true;
Thread_Pool thread_pool; Thread_Pool thread_pool;
Pose old_pose; Pose old_pose;
size_t cur_actions = 0; size_t cur_actions = 0;
......
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include "manager.h" #include "manager.h"
#include "widgets.h" #include "widgets.h"
#include "../app.h"
#include "../geometry/util.h" #include "../geometry/util.h"
#include "../platform/platform.h" #include "../platform/platform.h"
#include "../scene/renderer.h" #include "../scene/renderer.h"
...@@ -157,7 +158,7 @@ void Widgets::render(const Mat4& view, Vec3 pos, float scl) { ...@@ -157,7 +158,7 @@ void Widgets::render(const Mat4& view, Vec3 pos, float scl) {
z_scl.pose.scale = scale; z_scl.pose.scale = scale;
z_scl.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl); z_scl.pose.pos = pos + Vec3(0.0f, 0.0f, 0.15f * scl);
z_scl.render(view, true); z_scl.render(view, true);
xyz_scl.pose.scale = scale; xyz_scl.pose.scale = scale;
xyz_scl.pose.pos = pos; xyz_scl.pose.pos = pos;
xyz_scl.render(view, true); xyz_scl.render(view, true);
...@@ -186,11 +187,11 @@ Pose Widgets::apply_action(const Pose& pose) { ...@@ -186,11 +187,11 @@ Pose Widgets::apply_action(const Pose& pose) {
result.scale = Vec3{1.0f}; result.scale = Vec3{1.0f};
result.scale[(int)axis] = drag_end[(int)axis]; result.scale[(int)axis] = drag_end[(int)axis];
Mat4 rot = pose.rotation_mat(); Mat4 rot = pose.rotation_mat();
Mat4 trans = Mat4 trans =
Mat4::transpose(rot) * Mat4::scale(result.scale) * rot * Mat4::scale(pose.scale); Mat4::transpose(rot) * Mat4::scale(result.scale) * rot * Mat4::scale(pose.scale);
result.scale = Vec3(trans[0][0], trans[1][1], trans[2][2]); result.scale = Vec3(trans[0][0], trans[1][1], trans[2][2]);
} }
} break; } break;
case Widget_Type::bevel: { case Widget_Type::bevel: {
Vec2 off = bevel_start - bevel_end; Vec2 off = bevel_start - bevel_end;
...@@ -240,7 +241,7 @@ bool Widgets::to_axis3(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3& hit) { ...@@ -240,7 +241,7 @@ bool Widgets::to_axis3(Vec3 obj_pos, Vec3 cam_pos, Vec3 dir, Vec3& hit) {
Line select(cam_pos, dir); Line select(cam_pos, dir);
Line target(obj_pos, axis1); Line target(obj_pos, axis1);
Plane k(obj_pos, axis1); Plane k(obj_pos, axis1);
Plane l(obj_pos, axis2); Plane l(obj_pos, axis2);
Plane r(obj_pos, axis3); Plane r(obj_pos, axis3);
...@@ -300,7 +301,7 @@ void Widgets::start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir) { ...@@ -300,7 +301,7 @@ void Widgets::start_drag(Vec3 pos, Vec3 cam, Vec2 spos, Vec3 dir) {
good = to_plane(pos, cam, dir, norm, hit); good = to_plane(pos, cam, dir, norm, hit);
else if(univ_scl) else if(univ_scl)
good = to_axis3(pos, cam, dir, hit); good = to_axis3(pos, cam, dir, hit);
else else
good = to_axis(pos, cam, dir, hit); good = to_axis(pos, cam, dir, hit);
if(!good) return; if(!good) return;
...@@ -639,7 +640,6 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) { ...@@ -639,7 +640,6 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) {
if(method == 1) { if(method == 1) {
ImGui::InputInt("Samples", &out_samples, 1, 100); ImGui::InputInt("Samples", &out_samples, 1, 100);
ImGui::InputInt("Area Light Samples", &out_area_samples, 1, 100);
ImGui::InputInt("Max Ray Depth", &out_depth, 1, 32); ImGui::InputInt("Max Ray Depth", &out_depth, 1, 32);
ImGui::SliderFloat("Exposure", &exposure, 0.01f, 10.0f, "%.2f", 2.5f); ImGui::SliderFloat("Exposure", &exposure, 0.01f, 10.0f, "%.2f", 2.5f);
} else { } else {
...@@ -650,7 +650,6 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) { ...@@ -650,7 +650,6 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) {
out_w = std::max(1, out_w); out_w = std::max(1, out_w);
out_h = std::max(1, out_h); out_h = std::max(1, out_h);
out_samples = std::max(1, out_samples); out_samples = std::max(1, out_samples);
out_area_samples = std::max(1, out_area_samples);
out_depth = std::max(1, out_depth); out_depth = std::max(1, out_depth);
if(ImGui::Button("Set Width via AR")) { if(ImGui::Button("Set Width via AR")) {
...@@ -660,6 +659,8 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) { ...@@ -660,6 +659,8 @@ void Widget_Render::begin(Scene& scene, Widget_Camera& cam, Camera& user_cam) {
if(ImGui::Button("Set AR via W/H")) { if(ImGui::Button("Set AR via W/H")) {
cam.ar(user_cam, (float)out_w / (float)out_h); cam.ar(user_cam, (float)out_w / (float)out_h);
} }
ImGui::SameLine();
ImGui::Checkbox("Use BVH", &use_bvh);
} }
std::string Widget_Render::step(Animate& animate, Scene& scene) { std::string Widget_Render::step(Animate& animate, Scene& scene) {
...@@ -676,9 +677,10 @@ std::string Widget_Render::step(Animate& animate, Scene& scene) { ...@@ -676,9 +677,10 @@ std::string Widget_Render::step(Animate& animate, Scene& scene) {
} }
Camera cam = animate.set_time(scene, (float)next_frame); Camera cam = animate.set_time(scene, (float)next_frame);
animate.step_sim(scene);
if(method == 0) { if(method == 0) {
animate.step_sim(scene);
std::vector<unsigned char> data; std::vector<unsigned char> data;
Renderer::get().save(scene, cam, out_w, out_h, out_samples); Renderer::get().save(scene, cam, out_w, out_h, out_samples);
...@@ -726,6 +728,7 @@ std::string Widget_Render::step(Animate& animate, Scene& scene) { ...@@ -726,6 +728,7 @@ std::string Widget_Render::step(Animate& animate, Scene& scene) {
return "Failed to write output!"; return "Failed to write output!";
} }
animate.step_sim(scene);
pathtracer.begin_render(scene, cam); pathtracer.begin_render(scene, cam);
next_frame++; next_frame++;
} }
...@@ -778,7 +781,7 @@ void Widget_Render::animate(Scene& scene, Widget_Camera& cam, Camera& user_cam, ...@@ -778,7 +781,7 @@ void Widget_Render::animate(Scene& scene, Widget_Camera& cam, Camera& user_cam,
if(method == 1) { if(method == 1) {
init = true; init = true;
ray_log.clear(); ray_log.clear();
pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth); pathtracer.set_params(out_w, out_h, out_samples, out_depth, use_bvh);
} }
} }
} }
...@@ -831,7 +834,7 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std:: ...@@ -831,7 +834,7 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::
has_rendered = true; has_rendered = true;
ret = true; ret = true;
ray_log.clear(); ray_log.clear();
pathtracer.set_sizes(out_w, out_h, out_samples, out_area_samples, out_depth); pathtracer.set_params(out_w, out_h, out_samples, out_depth, use_bvh);
pathtracer.begin_render(scene, cam.get()); pathtracer.begin_render(scene, cam.get());
} else { } else {
Renderer::get().save(scene, cam.get(), out_w, out_h, out_samples); Renderer::get().save(scene, cam.get(), out_w, out_h, out_samples);
...@@ -871,6 +874,7 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std:: ...@@ -871,6 +874,7 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::
if(method == 1 && has_rendered) { if(method == 1 && has_rendered) {
ImGui::SameLine(); ImGui::SameLine();
if(ImGui::Button("Add Samples")) { if(ImGui::Button("Add Samples")) {
pathtracer.set_samples((int)out_samples);
pathtracer.begin_render(scene, cam.get(), true); pathtracer.begin_render(scene, cam.get(), true);
} }
} }
...@@ -897,21 +901,20 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std:: ...@@ -897,21 +901,20 @@ bool Widget_Render::UI(Scene& scene, Widget_Camera& cam, Camera& user_cam, std::
} }
std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera& cam, std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera& cam,
std::string output, bool a, int w, int h, int s, int ls, int d, const Launch_Settings& set) {
float exp) {
info("Render settings:"); info("Render settings:");
info("\twidth: %d", w); info("\twidth: %d", set.w);
info("\theight: %d", h); info("\theight: %d", set.h);
info("\tsamples: %d", s); info("\tsamples: %d", set.s);
info("\tlight samples: %d", ls); info("\tmax depth: %d", set.d);
info("\tmax depth: %d", d); info("\texposure: %f", set.exp);
info("\texposure: %f", exp);
info("\trender threads: %u", std::thread::hardware_concurrency()); info("\trender threads: %u", std::thread::hardware_concurrency());
if(set.no_bvh) info("\tusing object list instead of BVH");
out_w = w; out_w = set.w;
out_h = h; out_h = set.h;
pathtracer.set_sizes(w, h, s, ls, d); pathtracer.set_params(set.w, set.h, set.s, set.d, !set.no_bvh);
auto print_progress = [](float f) { auto print_progress = [](float f) {
std::cout << "Progress: ["; std::cout << "Progress: [";
...@@ -931,14 +934,14 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera ...@@ -931,14 +934,14 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera
}; };
std::cout << std::fixed << std::setw(2) << std::setprecision(2) << std::setfill('0'); std::cout << std::fixed << std::setw(2) << std::setprecision(2) << std::setfill('0');
if(a) { if(set.animate) {
method = 1; method = 1;
init = true; init = true;
animating = true; animating = true;
max_frame = animate.n_frames(); max_frame = animate.n_frames();
next_frame = 0; next_frame = 0;
folder = output; folder = set.output_file;
while(next_frame < max_frame) { while(next_frame < max_frame) {
std::string err = step(animate, scene); std::string err = step(animate, scene);
if(!err.empty()) return err; if(!err.empty()) return err;
...@@ -957,8 +960,8 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera ...@@ -957,8 +960,8 @@ std::string Widget_Render::headless(Animate& animate, Scene& scene, const Camera
std::cout << std::endl; std::cout << std::endl;
std::vector<unsigned char> data; std::vector<unsigned char> data;
pathtracer.get_output().tonemap_to(data, exp); pathtracer.get_output().tonemap_to(data, set.exp);
if(!stbi_write_png(output.c_str(), w, h, 4, data.data(), w * 4)) { if(!stbi_write_png(set.output_file.c_str(), set.w, set.h, 4, data.data(), set.w * 4)) {
return "Failed to write output!"; return "Failed to write output!";
} }
} }
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
#include "../scene/scene.h" #include "../scene/scene.h"
class Undo; class Undo;
struct Launch_Settings;
namespace Gui { namespace Gui {
...@@ -84,8 +85,8 @@ public: ...@@ -84,8 +85,8 @@ public:
void animate(Scene& scene, Widget_Camera& cam, Camera& user_cam, int max_frame); void animate(Scene& scene, Widget_Camera& cam, Camera& user_cam, int max_frame);
std::string step(Animate& animate, Scene& scene); std::string step(Animate& animate, Scene& scene);
std::string headless(Animate& animate, Scene& scene, const Camera& cam, std::string output, std::string headless(Animate& animate, Scene& scene, const Camera& cam,
bool a, int w, int h, int s, int ls, int d, float exp); const Launch_Settings& set);
void log_ray(const Ray& ray, float t, Spectrum color = Spectrum{1.0f}); void log_ray(const Ray& ray, float t, Spectrum color = Spectrum{1.0f});
void render_log(const Mat4& view) const; void render_log(const Mat4& view) const;
...@@ -100,7 +101,7 @@ public: ...@@ -100,7 +101,7 @@ public:
return pathtracer.completion_time(); return pathtracer.completion_time();
} }
bool in_progress() const { bool in_progress() const {
return pathtracer.in_progress(); return pathtracer.in_progress() || animating;
} }
float wh_ar() const { float wh_ar() const {
return (float)out_w / (float)out_h; return (float)out_w / (float)out_h;
...@@ -112,8 +113,9 @@ private: ...@@ -112,8 +113,9 @@ private:
mutable std::mutex log_mut; mutable std::mutex log_mut;
GL::Lines ray_log; GL::Lines ray_log;
int out_w, out_h, out_samples = 32, out_area_samples = 8, out_depth = 4; int out_w, out_h, out_samples = 32, out_depth = 8;
float exposure = 1.0f; float exposure = 1.0f;
bool use_bvh = true;
bool has_rendered = false; bool has_rendered = false;
bool render_window = false, render_window_focus = false; bool render_window = false, render_window_focus = false;
......
...@@ -22,6 +22,16 @@ inline std::string last_file(std::string path) { ...@@ -22,6 +22,16 @@ inline std::string last_file(std::string path) {
return path.substr(p, path.size() - p); return path.substr(p, path.size() - p);
} }
#ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak()
#elif defined(__GNUC__)
#define DEBUG_BREAK __builtin_trap()
#elif defined(__clang__)
#define DEBUG_BREAK __builtin_debugtrap()
#else
#error Unsupported compiler.
#endif
/// Log informational message /// Log informational message
#define info(fmt, ...) \ #define info(fmt, ...) \
(void)(log("%s:%u [info] " fmt "\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__)) (void)(log("%s:%u [info] " fmt "\n", last_file(__FILE__).c_str(), __LINE__, ##__VA_ARGS__))
...@@ -35,17 +45,7 @@ inline std::string last_file(std::string path) { ...@@ -35,17 +45,7 @@ inline std::string last_file(std::string path) {
#define die(fmt, ...) \ #define die(fmt, ...) \
(void)(log("\033[0;31m%s:%u [fatal] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, \ (void)(log("\033[0;31m%s:%u [fatal] " fmt "\033[0m\n", last_file(__FILE__).c_str(), __LINE__, \
##__VA_ARGS__), \ ##__VA_ARGS__), \
std::exit(__LINE__)); DEBUG_BREAK, std::exit(__LINE__));
#ifdef _MSC_VER
#define DEBUG_BREAK __debugbreak()
#elif defined(__GNUC__)
#define DEBUG_BREAK __builtin_trap()
#elif defined(__clang__)
#define DEBUG_BREAK __builtin_debugtrap()
#else
#error Unsupported compiler.
#endif
#define fail_assert(msg, file, line) \ #define fail_assert(msg, file, line) \
(void)(log("\033[1;31m%s:%u [ASSERT] " msg "\033[0m\n", file, line), DEBUG_BREAK, \ (void)(log("\033[1;31m%s:%u [ASSERT] " msg "\033[0m\n", file, line), DEBUG_BREAK, \
......
...@@ -165,15 +165,18 @@ struct Mat4 { ...@@ -165,15 +165,18 @@ struct Mat4 {
bool single = true; bool single = true;
static const float singularity[] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f, static const float singularity[] = {1.0f, 0.0f, 0.0f, 0.0f, 0.0f, -1.0f,
0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0}; 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0};
for(int i = 0; i < 12 && single; i++) {
single = single && std::abs(data[i] - singularity[i]) < 16.0f * FLT_EPSILON; for(int i = 0; i < 3 && single; i++) {
for(int j = 0; j < 4 && single; j++) {
single = single && std::abs(cols[i][j] - singularity[i * 4 + j]) < EPS_F;
}
} }
if(single) return Vec3{0.0f, 0.0f, 180.0f}; if(single) return Vec3{0.0f, 0.0f, 180.0f};
Vec3 eul1, eul2; Vec3 eul1, eul2;
float cy = std::hypotf(cols[0][0], cols[0][1]); float cy = std::hypotf(cols[0][0], cols[0][1]);
if(cy > 16.0f * FLT_EPSILON) { if(cy > EPS_F) {
eul1[0] = std::atan2(cols[1][2], cols[2][2]); eul1[0] = std::atan2(cols[1][2], cols[2][2]);
eul1[1] = std::atan2(-cols[0][2], cy); eul1[1] = std::atan2(-cols[0][2], cy);
eul1[2] = std::atan2(cols[0][1], cols[0][0]); eul1[2] = std::atan2(cols[0][1], cols[0][0]);
......
...@@ -13,8 +13,9 @@ struct Ray { ...@@ -13,8 +13,9 @@ struct Ray {
Ray() = default; Ray() = default;
/// Create Ray from point and direction /// Create Ray from point and direction
explicit Ray(Vec3 point, Vec3 dir) explicit Ray(Vec3 point, Vec3 dir,
: point(point), dir(dir.unit()), dist_bounds(0.0f, std::numeric_limits<float>::max()) { Vec2 dist_bounds = Vec2{0.0f, std::numeric_limits<float>::max()}, size_t depth = 0)
: point(point), dir(dir.unit()), dist_bounds(dist_bounds), depth(depth) {
} }
Ray(const Ray&) = default; Ray(const Ray&) = default;
......
...@@ -5,8 +5,6 @@ ...@@ -5,8 +5,6 @@
#include <cmath> #include <cmath>
#include <ostream> #include <ostream>
#define GAMMA 2.1f
struct Spectrum { struct Spectrum {
Spectrum() { Spectrum() {
...@@ -49,19 +47,38 @@ struct Spectrum { ...@@ -49,19 +47,38 @@ struct Spectrum {
static Spectrum direction(Vec3 v) { static Spectrum direction(Vec3 v) {
v.normalize(); v.normalize();
Spectrum s(std::abs(v.x), std::abs(v.y), std::abs(v.z)); Spectrum s(std::abs(v.x), std::abs(v.y), std::abs(v.z));
s.make_linear(); s.to_linear();
return s; return s;
} }
void make_srgb() { static float to_linear(float f) {
r = std::pow(r, 1.0f / GAMMA); if(f > 0.04045f) {
g = std::pow(g, 1.0f / GAMMA); return std::pow((f + 0.055f) / 1.055f, 2.4f);
b = std::pow(b, 1.0f / GAMMA); } else {
return f / 12.92f;
}
}
static float to_srgb(float f) {
if(f > 0.0031308f) {
return 1.055f * (std::pow(f, (1.0f / 2.4f))) - 0.055f;
} else {
return f * 12.92f;
}
}
Spectrum to_srgb() const {
Spectrum ret;
ret.r = to_srgb(r);
ret.g = to_srgb(g);
ret.b = to_srgb(b);
return ret;
} }
void make_linear() { Spectrum to_linear() const {
r = std::pow(r, GAMMA); Spectrum ret;
g = std::pow(g, GAMMA); ret.r = to_linear(r);
b = std::pow(b, GAMMA); ret.g = to_linear(g);
ret.b = to_linear(b);
return ret;
} }
Spectrum operator+(Spectrum v) const { Spectrum operator+(Spectrum v) const {
...@@ -96,8 +113,7 @@ struct Spectrum { ...@@ -96,8 +113,7 @@ struct Spectrum {
} }
bool valid() const { bool valid() const {
return !(std::isinf(r) || std::isinf(g) || std::isinf(b) || std::isnan(r) || return std::isfinite(r) && std::isfinite(g) && std::isfinite(b);
std::isnan(g) || std::isnan(b));
} }
Vec3 to_vec() const { Vec3 to_vec() const {
......
...@@ -123,7 +123,7 @@ struct Vec2 { ...@@ -123,7 +123,7 @@ struct Vec2 {
} }
/// Are all members real numbers? /// Are all members real numbers?
bool valid() const { bool valid() const {
return !(std::isinf(x) || std::isinf(y) || std::isnan(x) || std::isnan(y)); return std::isfinite(x) && std::isfinite(y);
} }
/// Modify vec to have unit length /// Modify vec to have unit length
......
...@@ -134,8 +134,7 @@ struct Vec3 { ...@@ -134,8 +134,7 @@ struct Vec3 {
} }
/// Are all members real numbers? /// Are all members real numbers?
bool valid() const { bool valid() const {
return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isnan(x) || return std::isfinite(x) && std::isfinite(y) && std::isfinite(z);
std::isnan(y) || std::isnan(z));
} }
/// Modify vec to have unit length /// Modify vec to have unit length
......
...@@ -152,8 +152,7 @@ struct Vec4 { ...@@ -152,8 +152,7 @@ struct Vec4 {
} }
/// Are all members real numbers? /// Are all members real numbers?
bool valid() const { bool valid() const {
return !(std::isinf(x) || std::isinf(y) || std::isinf(z) || std::isinf(w) || return std::isfinite(x) && std::isfinite(y) && std::isfinite(z) && std::isfinite(w);
std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isnan(w));
} }
/// Modify vec to have unit length /// Modify vec to have unit length
......
...@@ -7,31 +7,31 @@ int main(int argc, char** argv) { ...@@ -7,31 +7,31 @@ int main(int argc, char** argv) {
RNG::seed(); RNG::seed();
App::Settings settings; Launch_Settings set;
CLI::App args{"Scotty3D - 15-462"}; CLI::App args{"Scotty3D - 15-462"};
args.add_option("-s,--scene", settings.scene_file, "Scene file to load"); args.add_option("-s,--scene", set.scene_file, "Scene file to load");
args.add_option("--env_map", settings.env_map_file, "Override scene environment map"); args.add_option("--env_map", set.env_map_file, "Override scene environment map");
args.add_flag("--headless", settings.headless, "Path-trace scene without opening the GUI"); args.add_flag("--headless", set.headless, "Path-trace scene without opening the GUI");
args.add_option("-o,--output", settings.output_file, "Image file to write (if headless)"); args.add_option("-o,--output", set.output_file, "Image file to write (if headless)");
args.add_flag("--animate", settings.animate, "Output animation frames (if headless)"); args.add_flag("--animate", set.animate, "Output animation frames (if headless)");
args.add_option("--width", settings.w, "Output image width (if headless)"); args.add_flag("--no_bvh", set.no_bvh, "Don't use BVH (if headless)");
args.add_option("--height", settings.h, "Output image height (if headless)"); args.add_option("--width", set.w, "Output image width (if headless)");
args.add_flag("--use_ar", settings.w_from_ar, args.add_option("--height", set.h, "Output image height (if headless)");
args.add_flag("--use_ar", set.w_from_ar,
"Compute output image width based on camera AR (if headless)"); "Compute output image width based on camera AR (if headless)");
args.add_option("--depth", settings.d, "Maximum ray depth (if headless)"); args.add_option("--depth", set.d, "Maximum ray depth (if headless)");
args.add_option("--samples", settings.s, "Pixel samples (if headless)"); args.add_option("--samples", set.s, "Pixel samples (if headless)");
args.add_option("--exposure", settings.exp, "Output exposure (if headless)"); args.add_option("--exposure", set.exp, "Output exposure (if headless)");
args.add_option("--area_samples", settings.ls, "Area light samples (if headless)");
CLI11_PARSE(args, argc, argv); CLI11_PARSE(args, argc, argv);
if(!settings.headless) { if(!set.headless) {
Platform plt; Platform plt;
App app(settings, &plt); App app(set, &plt);
plt.loop(app); plt.loop(app);
} else { } else {
App app(settings); App app(set);
} }
return 0; return 0;
} }
...@@ -152,8 +152,8 @@ void Tex2D::image(int w, int h, unsigned char* img) { ...@@ -152,8 +152,8 @@ void Tex2D::image(int w, int h, unsigned char* img) {
if(!id) glGenTextures(1, &id); if(!id) glGenTextures(1, &id);
glBindTexture(GL_TEXTURE_2D, id); glBindTexture(GL_TEXTURE_2D, id);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0); glBindTexture(GL_TEXTURE_2D, 0);
...@@ -1305,7 +1305,7 @@ void main() { ...@@ -1305,7 +1305,7 @@ void main() {
vec3 pos = normalize(f_pos); vec3 pos = normalize(f_pos);
if(pos.y > cosine) { if(pos.y > cosine) {
if(use_texture) { if(use_texture) {
float theta = atan(pos.z,pos.x) / TAU + 0.5f; float theta = atan(pos.z,pos.x) / TAU;
float phi = acos(pos.y) / PI; float phi = acos(pos.y) / PI;
out_col = texture(tex, vec2(theta,phi)); out_col = texture(tex, vec2(theta,phi));
} else { } else {
......
...@@ -11,12 +11,10 @@ ...@@ -11,12 +11,10 @@
namespace PT { namespace PT {
struct BSDF_Sample { struct Scatter {
Spectrum emissive;
Spectrum attenuation; Spectrum attenuation;
Vec3 direction; Vec3 direction;
float pdf;
void transform(const Mat4& T) { void transform(const Mat4& T) {
direction = T.rotate(direction); direction = T.rotate(direction);
...@@ -25,14 +23,15 @@ struct BSDF_Sample { ...@@ -25,14 +23,15 @@ struct BSDF_Sample {
struct BSDF_Lambertian { struct BSDF_Lambertian {
BSDF_Lambertian(Spectrum albedo) : albedo(albedo) { BSDF_Lambertian(Spectrum albedo) : albedo(albedo / PI_F) {
} }
BSDF_Sample sample(Vec3 out_dir) const; Scatter scatter(Vec3 out_dir) const;
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const; Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const;
float pdf(Vec3 out_Dir, Vec3 in_dir) const;
Spectrum albedo; Spectrum albedo;
Samplers::Hemisphere::Uniform sampler; Samplers::Hemisphere::Cosine sampler;
}; };
struct BSDF_Mirror { struct BSDF_Mirror {
...@@ -40,8 +39,7 @@ struct BSDF_Mirror { ...@@ -40,8 +39,7 @@ struct BSDF_Mirror {
BSDF_Mirror(Spectrum reflectance) : reflectance(reflectance) { BSDF_Mirror(Spectrum reflectance) : reflectance(reflectance) {
} }
BSDF_Sample sample(Vec3 out_dir) const; Scatter scatter(Vec3 out_dir) const;
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const;
Spectrum reflectance; Spectrum reflectance;
}; };
...@@ -52,8 +50,7 @@ struct BSDF_Refract { ...@@ -52,8 +50,7 @@ struct BSDF_Refract {
: transmittance(transmittance), index_of_refraction(ior) { : transmittance(transmittance), index_of_refraction(ior) {
} }
BSDF_Sample sample(Vec3 out_dir) const; Scatter scatter(Vec3 out_dir) const;
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const;
Spectrum transmittance; Spectrum transmittance;
float index_of_refraction; float index_of_refraction;
...@@ -65,8 +62,7 @@ struct BSDF_Glass { ...@@ -65,8 +62,7 @@ struct BSDF_Glass {
: transmittance(transmittance), reflectance(reflectance), index_of_refraction(ior) { : transmittance(transmittance), reflectance(reflectance), index_of_refraction(ior) {
} }
BSDF_Sample sample(Vec3 out_dir) const; Scatter scatter(Vec3 out_dir) const;
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const;
Spectrum transmittance; Spectrum transmittance;
Spectrum reflectance; Spectrum reflectance;
...@@ -78,11 +74,9 @@ struct BSDF_Diffuse { ...@@ -78,11 +74,9 @@ struct BSDF_Diffuse {
BSDF_Diffuse(Spectrum radiance) : radiance(radiance) { BSDF_Diffuse(Spectrum radiance) : radiance(radiance) {
} }
BSDF_Sample sample(Vec3 out_dir) const; Spectrum emissive() const;
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const;
Spectrum radiance; Spectrum radiance;
Samplers::Hemisphere::Uniform sampler;
}; };
class BSDF { class BSDF {
...@@ -103,22 +97,41 @@ public: ...@@ -103,22 +97,41 @@ public:
BSDF& operator=(BSDF&& src) = default; BSDF& operator=(BSDF&& src) = default;
BSDF(BSDF&& src) = default; BSDF(BSDF&& src) = default;
BSDF_Sample sample(Vec3 out_dir) const { Scatter scatter(Vec3 out_dir) const {
return std::visit(overloaded{[&out_dir](const auto& b) { return b.sample(out_dir); }}, return std::visit(overloaded{[](const BSDF_Diffuse& d) -> Scatter {
die("You scattered an emissive BSDF!");
},
[out_dir](const auto& b) { return b.scatter(out_dir); }},
underlying); underlying);
} }
Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const { Spectrum evaluate(Vec3 out_dir, Vec3 in_dir) const {
return std::visit( return std::visit(
overloaded{[&out_dir, &in_dir](const auto& b) { return b.evaluate(out_dir, in_dir); }}, overloaded{
[out_dir, in_dir](const BSDF_Lambertian& l) { return l.evaluate(out_dir, in_dir); },
[](const auto&) -> Spectrum { die("You evaluated a delta BSDF!"); }},
underlying);
}
float pdf(Vec3 out_dir, Vec3 in_dir) const {
return std::visit(
overloaded{
[out_dir, in_dir](const BSDF_Lambertian& l) { return l.pdf(out_dir, in_dir); },
[](const auto&) -> float { die("You evaluated the pdf of a delta BSDF!"); }},
underlying); underlying);
} }
Spectrum emissive() const {
return std::visit(overloaded{[](const BSDF_Diffuse& d) { return d.emissive(); },
[](const auto& b) { return Spectrum{}; }},
underlying);
}
bool is_discrete() const { bool is_discrete() const {
return std::visit(overloaded{[](const BSDF_Lambertian&) { return false; }, return std::visit(overloaded{[](const BSDF_Lambertian&) { return false; },
[](const BSDF_Diffuse&) { return false; },
[](const BSDF_Mirror&) { return true; }, [](const BSDF_Mirror&) { return true; },
[](const BSDF_Glass&) { return true; }, [](const BSDF_Glass&) { return true; },
[](const BSDF_Diffuse&) { return false; },
[](const BSDF_Refract&) { return true; }}, [](const BSDF_Refract&) { return true; }},
underlying); underlying);
} }
...@@ -136,7 +149,4 @@ private: ...@@ -136,7 +149,4 @@ private:
std::variant<BSDF_Lambertian, BSDF_Mirror, BSDF_Glass, BSDF_Diffuse, BSDF_Refract> underlying; std::variant<BSDF_Lambertian, BSDF_Mirror, BSDF_Glass, BSDF_Diffuse, BSDF_Refract> underlying;
}; };
Vec3 reflect(Vec3 dir);
Vec3 refract(Vec3 out_dir, float index_of_refraction, bool& was_internal);
} // namespace PT } // namespace PT
...@@ -31,7 +31,7 @@ public: ...@@ -31,7 +31,7 @@ public:
private: private:
class Node { class Node {
BBox bbox; BBox bbox;
size_t start, size, l, r; size_t start, size, l, r;
......
...@@ -17,8 +17,9 @@ struct Env_Hemisphere { ...@@ -17,8 +17,9 @@ struct Env_Hemisphere {
Env_Hemisphere(Spectrum r) : radiance(r) { Env_Hemisphere(Spectrum r) : radiance(r) {
} }
Light_Sample sample() const; Vec3 sample() const;
Spectrum sample_direction(Vec3 dir) const; Spectrum evaluate(Vec3 dir) const;
float pdf(Vec3 dir) const;
Spectrum radiance; Spectrum radiance;
Samplers::Hemisphere::Uniform sampler; Samplers::Hemisphere::Uniform sampler;
...@@ -29,8 +30,9 @@ struct Env_Sphere { ...@@ -29,8 +30,9 @@ struct Env_Sphere {
Env_Sphere(Spectrum r) : radiance(r) { Env_Sphere(Spectrum r) : radiance(r) {
} }
Light_Sample sample() const; Vec3 sample() const;
Spectrum sample_direction(Vec3 dir) const; Spectrum evaluate(Vec3 dir) const;
float pdf(Vec3 dir) const;
Spectrum radiance; Spectrum radiance;
Samplers::Sphere::Uniform sampler; Samplers::Sphere::Uniform sampler;
...@@ -38,14 +40,16 @@ struct Env_Sphere { ...@@ -38,14 +40,16 @@ struct Env_Sphere {
struct Env_Map { struct Env_Map {
Env_Map(HDR_Image&& img) : image(std::move(img)), sampler(image) { Env_Map(HDR_Image&& img) : image(std::move(img)), image_sampler(image) {
} }
Light_Sample sample() const; Vec3 sample() const;
Spectrum sample_direction(Vec3 dir) const; Spectrum evaluate(Vec3 dir) const;
float pdf(Vec3 dir) const;
HDR_Image image; HDR_Image image;
Samplers::Sphere::Image sampler; Samplers::Sphere::Uniform uniform_sampler;
Samplers::Sphere::Image image_sampler;
}; };
class Env_Light { class Env_Light {
...@@ -62,19 +66,16 @@ public: ...@@ -62,19 +66,16 @@ public:
Env_Light& operator=(Env_Light&& src) = default; Env_Light& operator=(Env_Light&& src) = default;
Env_Light(Env_Light&& src) = default; Env_Light(Env_Light&& src) = default;
Light_Sample sample(Vec3) const { Vec3 sample() const {
return std::visit(overloaded{[](const Env_Hemisphere& h) { return h.sample(); }, return std::visit([](const auto& h) { return h.sample(); }, underlying);
[](const Env_Sphere& h) { return h.sample(); },
[](const Env_Map& h) { return h.sample(); }},
underlying);
} }
Spectrum sample_direction(Vec3 dir) const { float pdf(Vec3 dir) const {
return std::visit( return std::visit([dir](const auto& h) { return h.pdf(dir); }, underlying);
overloaded{[&dir](const Env_Hemisphere& h) { return h.sample_direction(dir); }, }
[&dir](const Env_Sphere& h) { return h.sample_direction(dir); },
[&dir](const Env_Map& h) { return h.sample_direction(dir); }}, Spectrum evaluate(Vec3 dir) const {
underlying); return std::visit([&dir](const auto& h) { return h.evaluate(dir); }, underlying);
} }
bool is_discrete() const { bool is_discrete() const {
......
...@@ -7,7 +7,6 @@ Light_Sample Directional_Light::sample(Vec3) const { ...@@ -7,7 +7,6 @@ Light_Sample Directional_Light::sample(Vec3) const {
Light_Sample ret; Light_Sample ret;
ret.direction = Vec3(0.0f, -1.0f, 0.0f); ret.direction = Vec3(0.0f, -1.0f, 0.0f);
ret.distance = std::numeric_limits<float>::infinity(); ret.distance = std::numeric_limits<float>::infinity();
ret.pdf = 1.0f;
ret.radiance = radiance; ret.radiance = radiance;
return ret; return ret;
} }
...@@ -16,7 +15,6 @@ Light_Sample Point_Light::sample(Vec3 from) const { ...@@ -16,7 +15,6 @@ Light_Sample Point_Light::sample(Vec3 from) const {
Light_Sample ret; Light_Sample ret;
ret.direction = -from.unit(); ret.direction = -from.unit();
ret.distance = from.norm(); ret.distance = from.norm();
ret.pdf = 1.0f;
ret.radiance = radiance; ret.radiance = radiance;
return ret; return ret;
} }
...@@ -27,28 +25,9 @@ Light_Sample Spot_Light::sample(Vec3 from) const { ...@@ -27,28 +25,9 @@ Light_Sample Spot_Light::sample(Vec3 from) const {
angle = std::abs(Degrees(angle)); angle = std::abs(Degrees(angle));
ret.direction = -from.unit(); ret.direction = -from.unit();
ret.distance = from.norm(); ret.distance = from.norm();
ret.pdf = 1.0f;
ret.radiance = ret.radiance =
(1.0f - smoothstep(angle_bounds.x / 2.0f, angle_bounds.y / 2.0f, angle)) * radiance; (1.0f - smoothstep(angle_bounds.x / 2.0f, angle_bounds.y / 2.0f, angle)) * radiance;
return ret; return ret;
} }
Light_Sample Rect_Light::sample(Vec3 from) const {
Light_Sample ret;
Vec2 sample = sampler.sample(ret.pdf);
Vec3 point(sample.x - size.x / 2.0f, 0.0f, sample.y - size.y / 2.0f);
Vec3 dir = point - from;
float cos_theta = dir.y;
float squared_dist = dir.norm_squared();
float dist = std::sqrt(squared_dist);
ret.direction = dir / dist;
ret.distance = dist;
ret.pdf *= squared_dist / std::abs(cos_theta);
ret.radiance = cos_theta > 0.0f ? radiance : Spectrum{};
return ret;
}
} // namespace PT } // namespace PT
...@@ -16,8 +16,7 @@ struct Light_Sample { ...@@ -16,8 +16,7 @@ struct Light_Sample {
Spectrum radiance; Spectrum radiance;
Vec3 direction; Vec3 direction;
float distance; float distance = 0.0f;
float pdf;
void transform(const Mat4& T) { void transform(const Mat4& T) {
direction = T.rotate(direction); direction = T.rotate(direction);
...@@ -26,90 +25,59 @@ struct Light_Sample { ...@@ -26,90 +25,59 @@ struct Light_Sample {
struct Directional_Light { struct Directional_Light {
Directional_Light(Spectrum r) : radiance(r), sampler(Vec3(0.0f, 1.0f, 0.0f)) { Directional_Light(Spectrum r) : radiance(r) {
} }
Light_Sample sample(Vec3 from) const; Light_Sample sample(Vec3 from) const;
Spectrum radiance; Spectrum radiance;
Samplers::Direction sampler;
}; };
struct Point_Light { struct Point_Light {
Point_Light(Spectrum r) : radiance(r), sampler(Vec3(0.0f)) { Point_Light(Spectrum r) : radiance(r) {
} }
Light_Sample sample(Vec3 from) const; Light_Sample sample(Vec3 from) const;
Spectrum radiance; Spectrum radiance;
Samplers::Point sampler;
}; };
struct Spot_Light { struct Spot_Light {
Spot_Light(Spectrum r, Vec2 a) : radiance(r), angle_bounds(a), sampler(Vec3(0.0f)) { Spot_Light(Spectrum r, Vec2 a) : radiance(r), angle_bounds(a) {
} }
Light_Sample sample(Vec3 from) const; Light_Sample sample(Vec3 from) const;
Spectrum radiance; Spectrum radiance;
Vec2 angle_bounds; Vec2 angle_bounds;
Samplers::Point sampler;
};
struct Rect_Light {
Rect_Light(Spectrum r, Vec2 s) : radiance(r), size(s), sampler(size) {
}
Light_Sample sample(Vec3 from) const;
Spectrum radiance;
Vec2 size;
Samplers::Rect::Uniform sampler;
}; };
class Light { class Delta_Light {
public: public:
Light(Directional_Light&& l, Scene_ID id, const Mat4& T = Mat4::I) Delta_Light(Directional_Light&& l, Scene_ID id, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) {
has_trans = trans != Mat4::I;
}
Light(Point_Light&& l, Scene_ID id, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) { : trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) {
has_trans = trans != Mat4::I; has_trans = trans != Mat4::I;
} }
Light(Spot_Light&& l, Scene_ID id, const Mat4& T = Mat4::I) Delta_Light(Point_Light&& l, Scene_ID id, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) { : trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) {
has_trans = trans != Mat4::I; has_trans = trans != Mat4::I;
} }
Light(Rect_Light&& l, Scene_ID id, const Mat4& T = Mat4::I) Delta_Light(Spot_Light&& l, Scene_ID id, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) { : trans(T), itrans(T.inverse()), _id(id), underlying(std::move(l)) {
has_trans = trans != Mat4::I; has_trans = trans != Mat4::I;
} }
Light(const Light& src) = delete; Delta_Light(const Delta_Light& src) = delete;
Light& operator=(const Light& src) = delete; Delta_Light& operator=(const Delta_Light& src) = delete;
Light& operator=(Light&& src) = default; Delta_Light& operator=(Delta_Light&& src) = default;
Light(Light&& src) = default; Delta_Light(Delta_Light&& src) = default;
Light_Sample sample(Vec3 from) const { Light_Sample sample(Vec3 from) const {
if(has_trans) from = itrans * from; if(has_trans) from = itrans * from;
Light_Sample ret = Light_Sample ret = std::visit([from](const auto& l) { return l.sample(from); }, underlying);
std::visit(overloaded{[&from](const auto& l) { return l.sample(from); }}, underlying);
if(has_trans) ret.transform(trans); if(has_trans) ret.transform(trans);
return ret; return ret;
} }
bool is_discrete() const {
return std::visit(overloaded{[](const Directional_Light&) { return true; },
[](const Point_Light&) { return true; },
[](const Spot_Light&) { return true; },
[](const Rect_Light&) { return false; }},
underlying);
}
Scene_ID id() const { Scene_ID id() const {
return _id; return _id;
} }
...@@ -123,7 +91,7 @@ private: ...@@ -123,7 +91,7 @@ private:
bool has_trans; bool has_trans;
Mat4 trans, itrans; Mat4 trans, itrans;
Scene_ID _id; Scene_ID _id;
std::variant<Directional_Light, Point_Light, Spot_Light, Rect_Light> underlying; std::variant<Directional_Light, Point_Light, Spot_Light> underlying;
}; };
} // namespace PT } // namespace PT
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
#pragma once #pragma once
#include "../lib/mathlib.h" #include "../lib/mathlib.h"
#include "../util/rand.h"
#include "trace.h" #include "trace.h"
namespace PT { namespace PT {
...@@ -10,7 +11,7 @@ template<typename Primitive> class List { ...@@ -10,7 +11,7 @@ template<typename Primitive> class List {
public: public:
List() { List() {
} }
List(std::vector<Primitive>&& primitives) : prims(primitives) { List(std::vector<Primitive>&& primitives) : prims(std::move(primitives)) {
} }
BBox bbox() const { BBox bbox() const {
...@@ -34,6 +35,32 @@ public: ...@@ -34,6 +35,32 @@ public:
prims.push_back(std::move(prim)); prims.push_back(std::move(prim));
} }
List<Primitive> copy() const {
std::vector<Primitive> prim_copy = prims;
return List<Primitive>(std::move(prim_copy));
}
Vec3 sample(Vec3 from) const {
if(prims.empty()) return {};
int n = RNG::integer(0, (int)prims.size());
return prims[n].sample(from);
}
float pdf(Ray ray, const Mat4& T = Mat4::I, const Mat4& iT = Mat4::I) const {
if(prims.empty()) return 0.0f;
float ret = 0.0f;
for(auto& prim : prims) ret += prim.pdf(ray, T, iT);
return ret / prims.size();
}
void clear() {
prims.clear();
}
bool empty() const {
return prims.empty();
}
private: private:
std::vector<Primitive> prims; std::vector<Primitive> prims;
}; };
......
...@@ -32,38 +32,73 @@ public: ...@@ -32,38 +32,73 @@ public:
has_trans = trans != Mat4::I; has_trans = trans != Mat4::I;
} }
Object() {
}
Object(List<Object>&& list, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), underlying(std::move(list)) {
}
Object(BVH<Object>&& bvh, const Mat4& T = Mat4::I)
: trans(T), itrans(T.inverse()), underlying(std::move(bvh)) {
}
Object(const Object& src) = delete; Object(const Object& src) = delete;
Object& operator=(const Object& src) = delete; Object& operator=(const Object& src) = delete;
Object& operator=(Object&& src) = default; Object& operator=(Object&& src) = default;
Object(Object&& src) = default; Object(Object&& src) = default;
BBox bbox() const { BBox bbox() const {
BBox box = std::visit(overloaded{[](const auto& o) { return o.bbox(); }}, underlying); BBox box = std::visit([](const auto& o) { return o.bbox(); }, underlying);
if(has_trans) box.transform(trans); if(has_trans) box.transform(trans);
return box; return box;
} }
Trace hit(Ray ray) const { Trace hit(Ray ray) const {
if(has_trans) ray.transform(itrans); if(has_trans) ray.transform(itrans);
Trace ret = Trace ret = std::visit([&ray](const auto& o) { return o.hit(ray); }, underlying);
std::visit(overloaded{[&ray](const auto& o) { return o.hit(ray); }}, underlying);
if(ret.hit) { if(ret.hit) {
ret.material = material; if(material != -1) ret.material = material;
if(has_trans) ret.transform(trans, itrans.T()); if(has_trans) ret.transform(trans, itrans.T());
} }
return ret; return ret;
} }
size_t visualize(GL::Lines& lines, GL::Lines& active, size_t level, const Mat4& vtrans) const { size_t visualize(GL::Lines& lines, GL::Lines& active, size_t level, Mat4 vtrans) const {
Mat4 next = has_trans ? vtrans * trans : vtrans; if(has_trans) vtrans = vtrans * trans;
return std::visit( return std::visit(
overloaded{ overloaded{
[&](const BVH<Object>& bvh) { return bvh.visualize(lines, active, level, next); }, [&](const BVH<Object>& bvh) { return bvh.visualize(lines, active, level, vtrans); },
[&](const Tri_Mesh& mesh) { return mesh.visualize(lines, active, level, next); }, [&](const Tri_Mesh& mesh) { return mesh.visualize(lines, active, level, vtrans); },
[](const auto&) { return size_t(0); }}, [](const auto&) { return size_t(0); }},
underlying); underlying);
} }
Vec3 sample(Vec3 from) const {
if(has_trans) from = itrans * from;
Vec3 dir =
std::visit(overloaded{[from](const List<Object>& list) { return list.sample(from); },
[from](const Tri_Mesh& mesh) { return mesh.sample(from); },
[](const auto&) -> Vec3 {
die("Sampling implicit objects/BVHs is not yet supported.");
}},
underlying);
if(has_trans) dir = trans.rotate(dir).unit();
return dir;
}
float pdf(Ray ray, Mat4 T = Mat4::I, Mat4 iT = Mat4::I) const {
if(has_trans) {
T = T * trans;
iT = itrans * iT;
}
return std::visit(
overloaded{[ray, T, iT](const List<Object>& list) { return list.pdf(ray, T, iT); },
[ray, T, iT](const Tri_Mesh& mesh) { return mesh.pdf(ray, T, iT); },
[](const auto&) -> float {
die("Sampling implicit objects/BVHs is not yet supported.");
}},
underlying);
}
Scene_ID id() const { Scene_ID id() const {
return _id; return _id;
} }
...@@ -76,7 +111,7 @@ public: ...@@ -76,7 +111,7 @@ public:
private: private:
bool has_trans; bool has_trans;
Mat4 trans, itrans; Mat4 trans, itrans;
unsigned int material; int material = -1;
Scene_ID _id; Scene_ID _id;
std::variant<Tri_Mesh, Shape, BVH<Object>, List<Object>> underlying; std::variant<Tri_Mesh, Shape, BVH<Object>, List<Object>> underlying;
}; };
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment