1
Fork 0

Reformat code

This commit is contained in:
Joshua Goins 2022-08-16 07:41:12 -04:00
parent f0713452fc
commit 81d5045037
13 changed files with 370 additions and 319 deletions

33
.clang-format Normal file
View file

@ -0,0 +1,33 @@
---
AllowShortIfStatementsOnASingleLine: Never
CompactNamespaces: 'false'
DisableFormat: 'false'
IndentCaseLabels: 'true'
IndentPPDirectives: BeforeHash
IndentWidth: '4'
Language: Cpp
NamespaceIndentation: All
PointerAlignment: Left
ReflowComments: 'true'
SortIncludes: 'true'
SortUsingDeclarations: 'true'
SpacesInCStyleCastParentheses: 'false'
Standard: Cpp11
TabWidth: '0'
UseTab: Never
AllowShortEnumsOnASingleLine: false
BraceWrapping:
AfterEnum: true
AccessModifierOffset: -4
SpaceAfterTemplateKeyword: 'false'
AllowAllParametersOfDeclarationOnNextLine: false
AlignAfterOpenBracket: AlwaysBreak
BinPackArguments: false
BinPackParameters: false
ColumnLimit: 120
AllowShortBlocksOnASingleLine: 'false'
AllowShortCaseLabelsOnASingleLine: 'false'
AllowShortFunctionsOnASingleLine: 'Empty'
AllowShortLambdasOnASingleLine: 'Empty'
AllowShortLoopsOnASingleLine: 'false'
SeparateDefinitionBlocks : 'Always'

8
.editorconfig Normal file
View file

@ -0,0 +1,8 @@
root = true
[*]
insert_final_newline = true
indent_style = space
indent_size = 4
tab_width = 4
max_line_length = 120

View file

@ -26,4 +26,4 @@ set_target_properties(raytracer PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED YES
CXX_EXTENSIONS NO
)
)

View file

@ -1,6 +1,8 @@
# Raytracer
A multi-threaded raytracer using glm, tinyobjloader and C++17. The UI is written in imgui and image display is
rendered using OpenGL. I tried to write this to not be insanely fast or compact like other raytracers, but to be readable and understandable.
rendered using OpenGL. I tried to write this to not be insanely fast or compact like other raytracers, but to be
readable and understandable.
![example result](misc/output.png)

View file

@ -16,12 +16,9 @@ struct AABB {
}
bool inside(const AABB extent) const {
return(max.x > extent.min.x &&
min.x < extent.max.x &&
max.y > extent.min.y &&
min.y < extent.max.y &&
max.z > extent.min.z &&
min.z < extent.max.z);
return (
max.x > extent.min.x && min.x < extent.max.x && max.y > extent.min.y && min.y < extent.max.y &&
max.z > extent.min.z && min.z < extent.max.z);
}
bool contains(const Ray& ray) const {
@ -36,14 +33,14 @@ struct AABB {
const float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
// if tmax < 0, ray (line) is intersecting AABB, but whole AABB is behing us
//if(tmax < 0)
// if(tmax < 0)
// return false;
// if tmin > tmax, ray doesn't intersect AABB
if(tmin > tmax)
if (tmin > tmax)
return false;
if(tmin < 0.0f)
if (tmin < 0.0f)
return true;
return true;

View file

@ -5,6 +5,7 @@
class Camera {
public:
Camera() : position(glm::vec3(0)), direction(glm::vec3(0)) {}
Camera(glm::vec3 position, glm::vec3 direction) : position(position), direction(direction) {}
void look_at(glm::vec3 eye, glm::vec3 target) {
@ -19,7 +20,8 @@ public:
const float h2 = height / 2.0f;
const float w2 = width / 2.0f;
const glm::vec3 ray_dir = position + (h2 / tan(glm::radians(fov) / 2)) * direction + (y - h2) * up + static_cast<float>(x - w2) * right;
const glm::vec3 ray_dir = position + (h2 / tan(glm::radians(fov) / 2)) * direction + (y - h2) * up +
static_cast<float>(x - w2) * right;
return Ray(position, ray_dir);
}

View file

@ -1,10 +1,9 @@
#pragma once
#include <glm/glm.hpp>
#include <array>
#include <glm/glm.hpp>
template<class T, int Width, int Height>
class Image {
template<class T, int Width, int Height> class Image {
public:
void reset() {
array = {};
@ -18,5 +17,5 @@ public:
return array[y * Width + x];
}
std::array<T, Width * Height> array = {};
std::array<T, Width* Height> array = {};
};

View file

@ -22,7 +22,8 @@ namespace intersections {
return false;
}
inline float ray_triangle(const Ray ray,
inline float ray_triangle(
const Ray ray,
const glm::vec3 v0,
const glm::vec3 v1,
const glm::vec3 v2,
@ -37,8 +38,9 @@ namespace intersections {
// if determinant is zero then ray is
// parallel with the triangle plane
if (det > -epsilon && det < epsilon) return false;
float invdet = 1.0/det;
if (det > -epsilon && det < epsilon)
return false;
float invdet = 1.0 / det;
// calculate distance from m[0] to origin
glm::vec3 tvec = ray.origin - v0;
@ -48,17 +50,19 @@ namespace intersections {
u = glm::dot(tvec, pvec) * invdet;
// check against one edge and opposite point
if (u < 0.0 || u > 1.0) return false;
if (u < 0.0 || u > 1.0)
return false;
glm::vec3 qvec = glm::cross(tvec, e1);
v = glm::dot(ray.direction, qvec) * invdet;
// check against other edges
if (v < 0.0 || u + v > 1.0) return false;
if (v < 0.0 || u + v > 1.0)
return false;
//distance along the ray, i.e. intersect at o + t * d
// distance along the ray, i.e. intersect at o + t * d
t = glm::dot(e2, qvec) * invdet;
return true;
}
};
}; // namespace intersections

View file

@ -9,4 +9,4 @@ namespace lighting {
return n_dot_l;
}
};
}; // namespace lighting

View file

@ -18,8 +18,7 @@ constexpr auto child_pattern = {
glm::vec3(-1, +1, +1),
};
template<typename UnderlyingType>
struct Node {
template<typename UnderlyingType> struct Node {
using NodeType = Node<UnderlyingType>;
AABB extent;
@ -31,15 +30,15 @@ struct Node {
bool is_split = false;
void add(UnderlyingType& object, const AABB extent) {
if(is_split) {
for(auto& child : children) {
if(extent.inside(child->extent))
if (is_split) {
for (auto& child : children) {
if (extent.inside(child->extent))
child->add(object, extent);
}
} else {
contained_objects.push_back(object);
if(contained_objects.size() >= max_contained_types && depth < max_octree_depth)
if (contained_objects.size() >= max_contained_types && depth < max_octree_depth)
split();
}
}
@ -51,7 +50,7 @@ struct Node {
const auto split_size = extent.extent().x / 2.0f;
int i = 0;
for(auto& point : child_pattern) {
for (auto& point : child_pattern) {
auto child = std::make_unique<NodeType>();
child->depth = depth + 1;
@ -62,9 +61,9 @@ struct Node {
children[i++] = std::move(child);
}
for(auto& object : contained_objects) {
for(auto& child : children) {
if(object.extent.inside(child->extent))
for (auto& object : contained_objects) {
for (auto& child : children) {
if (object.extent.inside(child->extent))
child->add(object, extent);
}
}
@ -73,8 +72,7 @@ struct Node {
}
};
template<typename UnderlyingType>
struct Octree {
template<typename UnderlyingType> struct Octree {
using NodeType = Node<UnderlyingType>;
NodeType root;

View file

@ -1,17 +1,17 @@
#pragma once
#include <optional>
#include <glm/glm.hpp>
#include <array>
#include <glm/glm.hpp>
#include <iostream>
#include <optional>
#include <random>
#include <tiny_obj_loader.h>
#include "ray.h"
#include "intersections.h"
#include "lighting.h"
#include "octree.h"
#include "ray.h"
constexpr glm::vec3 light_position = glm::vec3(5);
constexpr float light_bias = 0.01f;
@ -42,8 +42,8 @@ struct Object {
void create_octree() {
octree = std::make_unique<Octree<TriangleBox>>(glm::vec3(-2), glm::vec3(2));
for(auto& shape : shapes) {
for(size_t i = 0; i < shape.mesh.num_face_vertices.size(); i++) {
for (auto& shape : shapes) {
for (size_t i = 0; i < shape.mesh.num_face_vertices.size(); i++) {
const glm::vec3 v0 = fetch_position(*this, shape.mesh, i, 0);
const glm::vec3 v1 = fetch_position(*this, shape.mesh, i, 1);
const glm::vec3 v2 = fetch_position(*this, shape.mesh, i, 2);
@ -83,14 +83,14 @@ struct Scene {
auto o = std::make_unique<Object>();
std::string err;
if(!tinyobj::LoadObj(&o->attrib, &o->shapes, &o->materials, &err, path.data()))
if (!tinyobj::LoadObj(&o->attrib, &o->shapes, &o->materials, &err, path.data()))
std::cerr << "Could not load obj: " << err;
return *objects.emplace_back(std::move(o));
}
void generate_acceleration() {
for(auto& object : objects) {
for (auto& object : objects) {
object->create_octree();
}
}
@ -111,4 +111,3 @@ struct SceneResult {
};
std::optional<SceneResult> cast_scene(const Ray ray, Scene& scene, const bool use_bvh, const int depth = 0);

View file

@ -1,23 +1,23 @@
#include <iostream>
#include <limits>
#include <future>
#include <vector>
#include <glm/glm.hpp>
#include <SDL.h>
#include <array>
#include <future>
#include <glm/glm.hpp>
#include <iostream>
#include <limits>
#include <vector>
#define STB_IMAGE_WRITE_IMPLEMENTATION
#include <stb_image_write.h>
#include "intersections.h"
#include "camera.h"
#include "glad/glad.h"
#include "image.h"
#include "imgui.h"
#include "imgui_impl_opengl3.h"
#include "imgui_impl_sdl.h"
#include "intersections.h"
#include "lighting.h"
#include "scene.h"
#include "glad/glad.h"
#include "imgui.h"
#include "imgui_impl_sdl.h"
#include "imgui_impl_opengl3.h"
#define TINYOBJLOADER_IMPLEMENTATION
#include <tiny_obj_loader.h>
@ -50,23 +50,18 @@ enum class DisplayMode {
Reflect
};
const std::array diplay_mode_strings = {
"Combined",
"Direct",
"Indirect",
"Reflect"
};
const std::array diplay_mode_strings = {"Combined", "Direct", "Indirect", "Reflect"};
inline DisplayMode display_mode;
bool calculate_tile(const int32_t from_x, const int32_t to_width, const int32_t from_y, const int32_t to_height) {
for(int32_t y = from_y; y < (from_y + to_height); y++) {
for(int32_t x = from_x; x < (from_x + to_width); x++) {
for (int32_t y = from_y; y < (from_y + to_height); y++) {
for (int32_t x = from_x; x < (from_x + to_width); x++) {
Ray ray_camera = camera.get_ray(x, y, width, height);
if(auto result = cast_scene(ray_camera, scene, use_bvh)) {
if (auto result = cast_scene(ray_camera, scene, use_bvh)) {
glm::vec3 chosen_display;
switch(display_mode) {
switch (display_mode) {
case DisplayMode::Combined:
chosen_display = result->combined;
break;
@ -98,16 +93,12 @@ GLuint pixels_texture = 0;
void setup_gfx() {
// create quad for pixel rendering
constexpr std::array vertices = {
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, -0.5f, 0.0f, 1.0f, -1.0f,
-0.5f, -0.5f, 0.0f, 0.0f, -1.0f // we render it upside down (to opengl) so we flip our tex coord
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.5f,
-0.5f, 0.0f, 1.0f, -1.0f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f // we render it upside down (to opengl) so we flip our
// tex coord
};
constexpr std::array elements = {
0, 1, 2,
2, 3, 0
};
constexpr std::array elements = {0, 1, 2, 2, 3, 0};
glGenVertexArrays(1, &quad_vao);
glBindVertexArray(quad_vao);
@ -128,8 +119,7 @@ void setup_gfx() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(uint32_t), elements.data(), GL_STATIC_DRAW);
constexpr std::string_view vertex_glsl =
"#version 330 core\n"
constexpr std::string_view vertex_glsl = "#version 330 core\n"
"layout (location = 0) in vec3 in_position;\n"
"layout (location = 1) in vec2 in_uv;\n"
"out vec2 uv;\n"
@ -140,8 +130,7 @@ void setup_gfx() {
"}\n";
const char* vertex_src = vertex_glsl.data();
constexpr std::string_view fragment_glsl =
"#version 330 core\n"
constexpr std::string_view fragment_glsl = "#version 330 core\n"
"in vec2 uv;\n"
"out vec4 out_color;\n"
"uniform sampler2D pixel_texture;\n"
@ -188,8 +177,8 @@ void render() {
futures.clear();
colors.reset();
for(int32_t y = 0; y < num_tiles_y; y++) {
for(int32_t x = 0; x < num_tiles_x; x++) {
for (int32_t y = 0; y < num_tiles_y; y++) {
for (int32_t x = 0; x < num_tiles_x; x++) {
auto f = std::async(std::launch::async, calculate_tile, x * tile_size, tile_size, y * tile_size, tile_size);
futures.push_back(std::move(f));
}
@ -200,8 +189,8 @@ void dump_to_file() {
uint8_t pixels[width * height * 3] = {};
int i = 0;
for(int32_t y = height - 1; y >= 0; y--) {
for(int32_t x = 0; x < width; x++) {
for (int32_t y = height - 1; y >= 0; y--) {
for (int32_t x = 0; x < width; x++) {
const glm::ivec4 c = colors.get(x, y);
pixels[i++] = c.r;
pixels[i++] = c.g;
@ -212,14 +201,21 @@ void dump_to_file() {
stbi_write_png("output.png", width, height, 3, pixels, width * 3);
}
template<typename UnderlyingType>
void walk_node(Node<UnderlyingType>& node) {
if(ImGui::TreeNode(&node, "min: (%f %f %f)\n max: (%f %f %f)", node.extent.min.x, node.extent.min.y, node.extent.min.z, node.extent.max.x, node.extent.max.y, node.extent.max.z)) {
template<typename UnderlyingType> void walk_node(Node<UnderlyingType>& node) {
if (ImGui::TreeNode(
&node,
"min: (%f %f %f)\n max: (%f %f %f)",
node.extent.min.x,
node.extent.min.y,
node.extent.min.z,
node.extent.max.x,
node.extent.max.y,
node.extent.max.z)) {
ImGui::Text("Is split: %i", node.is_split);
ImGui::Text("Contained triangles: %lu", node.contained_objects.size());
if(node.is_split) {
for(auto& child : node.children)
if (node.is_split) {
for (auto& child : node.children)
walk_node(*child);
}
@ -239,7 +235,8 @@ int main(int, char*[]) {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
SDL_Window* window = SDL_CreateWindow("raytracer",
SDL_Window* window = SDL_CreateWindow(
"raytracer",
SDL_WINDOWPOS_CENTERED,
SDL_WINDOWPOS_CENTERED,
800,
@ -260,12 +257,12 @@ int main(int, char*[]) {
ImGui_ImplOpenGL3_Init("#version 330 core");
bool running = true;
while(running) {
while (running) {
SDL_Event event = {};
while(SDL_PollEvent(&event)) {
while (SDL_PollEvent(&event)) {
ImGui_ImplSDL2_ProcessEvent(&event);
if(event.type == SDL_QUIT)
if (event.type == SDL_QUIT)
running = false;
}
@ -274,9 +271,9 @@ int main(int, char*[]) {
ImGui::NewFrame();
if(ImGui::BeginMainMenuBar()) {
if(ImGui::BeginMenu("File")) {
if(ImGui::Button("Load Example Models")) {
if (ImGui::BeginMainMenuBar()) {
if (ImGui::BeginMenu("File")) {
if (ImGui::Button("Load Example Models")) {
auto& sphere = scene.load_from_file("misc/sphere.obj");
sphere.color = {1, 1, 1};
@ -298,37 +295,37 @@ int main(int, char*[]) {
ImGui::Checkbox("Use BVH", &use_bvh);
ImGui::InputInt("Indirect Samples", &num_indirect_samples);
if(ImGui::BeginCombo("Channel Selection", diplay_mode_strings[static_cast<int>(display_mode)])) {
if(ImGui::Selectable("Combined"))
if (ImGui::BeginCombo("Channel Selection", diplay_mode_strings[static_cast<int>(display_mode)])) {
if (ImGui::Selectable("Combined"))
display_mode = DisplayMode::Combined;
if(ImGui::Selectable("Direct"))
if (ImGui::Selectable("Direct"))
display_mode = DisplayMode::Direct;
if(ImGui::Selectable("Indirect"))
if (ImGui::Selectable("Indirect"))
display_mode = DisplayMode::Indirect;
if(ImGui::Selectable("Reflect"))
if (ImGui::Selectable("Reflect"))
display_mode = DisplayMode::Reflect;
ImGui::EndCombo();
}
if(ImGui::Button("Render"))
if (ImGui::Button("Render"))
render();
ImGui::SameLine();
if(ImGui::Button("Save Output"))
if (ImGui::Button("Save Output"))
dump_to_file();
if(image_dirty) {
if (image_dirty) {
update_texture();
image_dirty = false;
}
for(auto& object : scene.objects) {
if(ImGui::TreeNode("Object")) {
for (auto& object : scene.objects) {
if (ImGui::TreeNode("Object")) {
walk_object(*object);
ImGui::TreePop();

View file

@ -1,7 +1,7 @@
#include "scene.h"
#include <glm/gtx/perpendicular.hpp>
#include <functional>
#include <glm/gtx/perpendicular.hpp>
constexpr double pi = 3.14159265358979323846l;
@ -25,14 +25,21 @@ glm::vec3 fetch_normal(const Object& object, const tinyobj::mesh_t& mesh, const
return glm::vec3(nx, ny, nz);
}
bool test_triangle(const Ray ray, const Object& object, const tinyobj::mesh_t& mesh, const size_t i, float& tClosest, bool& intersection, HitResult& result) {
bool test_triangle(
const Ray ray,
const Object& object,
const tinyobj::mesh_t& mesh,
const size_t i,
float& tClosest,
bool& intersection,
HitResult& result) {
const glm::vec3 v0 = fetch_position(object, mesh, i, 0) + object.position;
const glm::vec3 v1 = fetch_position(object, mesh, i, 1) + object.position;
const glm::vec3 v2 = fetch_position(object, mesh, i, 2) + object.position;
float t = std::numeric_limits<float>::infinity(), u, v;
if(intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if(t < tClosest && t > epsilon) {
if (intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if (t < tClosest && t > epsilon) {
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
@ -52,14 +59,14 @@ std::optional<HitResult> test_mesh(const Ray ray, const Object& object, const ti
bool intersection = false;
HitResult result = {};
for(size_t i = 0; i < mesh.num_face_vertices.size(); i++) {
for (size_t i = 0; i < mesh.num_face_vertices.size(); i++) {
const glm::vec3 v0 = fetch_position(object, mesh, i, 0) + object.position;
const glm::vec3 v1 = fetch_position(object, mesh, i, 1) + object.position;
const glm::vec3 v2 = fetch_position(object, mesh, i, 2) + object.position;
float t = std::numeric_limits<float>::infinity(), u, v;
if(intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if(t < tClosest && t > epsilon) {
if (intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
if (t < tClosest && t > epsilon) {
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
@ -74,7 +81,7 @@ std::optional<HitResult> test_mesh(const Ray ray, const Object& object, const ti
}
}
if(intersection)
if (intersection)
return result;
else
return {};
@ -85,11 +92,11 @@ std::optional<HitResult> test_scene(const Ray ray, const Scene& scene) {
HitResult result = {};
float tClosest = std::numeric_limits<float>::infinity();
for(auto& object : scene.objects) {
for(uint32_t i = 0; i < object->shapes.size(); i++) {
for (auto& object : scene.objects) {
for (uint32_t i = 0; i < object->shapes.size(); i++) {
auto mesh = object->shapes[i].mesh;
if(const auto hit = test_mesh(ray, *object, mesh, tClosest)) {
if (const auto hit = test_mesh(ray, *object, mesh, tClosest)) {
intersection = true;
result = hit.value();
result.object = object.get();
@ -97,17 +104,16 @@ std::optional<HitResult> test_scene(const Ray ray, const Scene& scene) {
}
}
if(intersection)
if (intersection)
return result;
else
return {};
}
template<typename T>
Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>* out) {
if(node.extent.contains(ray)) {
if(node.is_split) {
for(auto& child : node.children)
template<typename T> Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>* out) {
if (node.extent.contains(ray)) {
if (node.is_split) {
for (auto& child : node.children)
find_hit_ray_search(*child, ray, out);
} else {
out->push_back(&node);
@ -117,8 +123,7 @@ Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>
return nullptr;
}
template<typename T>
std::vector<Node<T>*> find_hit_ray(Node<T>& node, const Ray ray) {
template<typename T> std::vector<Node<T>*> find_hit_ray(Node<T>& node, const Ray ray) {
std::vector<Node<T>*> vec;
find_hit_ray_search(node, ray, &vec);
@ -131,16 +136,23 @@ std::optional<HitResult> test_scene_octree(const Ray ray, const Scene& scene) {
HitResult result = {};
float tClosest = std::numeric_limits<float>::infinity();
for(auto& object : scene.objects) {
for(auto& node : find_hit_ray(object->octree->root, ray)) {
for(auto& triangle_object : node->contained_objects) {
if(test_triangle(ray, *object, *triangle_object.mesh, triangle_object.vertice_index, tClosest, intersection, result))
for (auto& object : scene.objects) {
for (auto& node : find_hit_ray(object->octree->root, ray)) {
for (auto& triangle_object : node->contained_objects) {
if (test_triangle(
ray,
*object,
*triangle_object.mesh,
triangle_object.vertice_index,
tClosest,
intersection,
result))
result.object = object.get();
}
}
}
if(intersection)
if (intersection)
return result;
else
return {};
@ -149,7 +161,7 @@ std::optional<HitResult> test_scene_octree(const Ray ray, const Scene& scene) {
// methods adapated from https://users.cg.tuwien.ac.at/zsolnai/gfx/smallpaint/
std::tuple<glm::vec3, glm::vec3> orthogonal_system(const glm::vec3& v1) {
glm::vec3 v2;
if(glm::abs(v1.x) > glm::abs(v1.y)) {
if (glm::abs(v1.x) > glm::abs(v1.y)) {
// project to the y = 0 plane and construct a normalized orthogonal vector in this plane
const float inverse_length = 1.0f / sqrtf(v1.x * v1.x + v1.z * v1.z);
v2 = glm::vec3(-v1.z * inverse_length, 0.0f, v1.x * inverse_length);
@ -174,18 +186,18 @@ glm::vec3 reflect(const glm::vec3& I, const glm::vec3& N) {
}
std::optional<SceneResult> cast_scene(const Ray ray, Scene& scene, const bool use_bvh, const int depth) {
if(depth > max_depth)
if (depth > max_depth)
return {};
const std::function<decltype(test_scene)> scene_func = use_bvh ? test_scene_octree : test_scene;
if(auto hit = scene_func(ray, scene)) {
if (auto hit = scene_func(ray, scene)) {
const float diffuse = lighting::point_light(hit->position, light_position, hit->normal);
SceneResult result = {};
// direct lighting calculation
// currently only supports only one light (directional)
if(glm::dot(light_position - hit->position, hit->normal) > 0) {
if (glm::dot(light_position - hit->position, hit->normal) > 0) {
const glm::vec3 light_dir = glm::normalize(light_position - hit->position);
const Ray shadow_ray(hit->position + (hit->normal * light_bias), light_dir);
@ -195,14 +207,15 @@ std::optional<SceneResult> cast_scene(const Ray ray, Scene& scene, const bool us
result.direct = hit->object->color * diffuse * shadow;
}
if(auto reflect_result = cast_scene(Ray(hit->position, glm::reflect(ray.direction, hit->normal)), scene, use_bvh, depth + 1))
if (auto reflect_result =
cast_scene(Ray(hit->position, glm::reflect(ray.direction, hit->normal)), scene, use_bvh, depth + 1))
result.reflect = reflect_result->combined;
// indirect lighting calculation
// we take a hemisphere orthogonal to the normal, and take a constant number of num_indirect_samples
// and naive monte carlo without PDF
if(num_indirect_samples > 0) {
for(int i = 0; i < num_indirect_samples; i++) {
if (num_indirect_samples > 0) {
for (int i = 0; i < num_indirect_samples; i++) {
const float theta = scene.distribution() * pi;
const float cos_theta = cos(theta);
const float sin_theta = sin(theta);
@ -213,10 +226,9 @@ std::optional<SceneResult> cast_scene(const Ray ray, Scene& scene, const bool us
const glm::vec3 rotated_dir = {
glm::dot({rotX.x, rotY.x, hit->normal.x}, sampled_dir),
glm::dot({rotX.y, rotY.y, hit->normal.y}, sampled_dir),
glm::dot({rotX.z, rotY.z, hit->normal.z}, sampled_dir)
};
glm::dot({rotX.z, rotY.z, hit->normal.z}, sampled_dir)};
if(const auto indirect_result = cast_scene(Ray(ray.origin, rotated_dir), scene, use_bvh, depth + 1))
if (const auto indirect_result = cast_scene(Ray(ray.origin, rotated_dir), scene, use_bvh, depth + 1))
result.indirect += indirect_result->combined * cos_theta;
}