Reformat code
This commit is contained in:
parent
f0713452fc
commit
81d5045037
13 changed files with 370 additions and 319 deletions
33
.clang-format
Normal file
33
.clang-format
Normal 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
8
.editorconfig
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
insert_final_newline = true
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 4
|
||||||
|
tab_width = 4
|
||||||
|
max_line_length = 120
|
|
@ -9,21 +9,21 @@ find_package(SDL2 REQUIRED)
|
||||||
add_subdirectory(extern)
|
add_subdirectory(extern)
|
||||||
|
|
||||||
add_executable(raytracer
|
add_executable(raytracer
|
||||||
include/camera.h
|
include/camera.h
|
||||||
include/intersections.h
|
include/intersections.h
|
||||||
include/lighting.h
|
include/lighting.h
|
||||||
include/ray.h
|
include/ray.h
|
||||||
include/image.h
|
include/image.h
|
||||||
include/tiny_obj_loader.h
|
include/tiny_obj_loader.h
|
||||||
include/scene.h
|
include/scene.h
|
||||||
include/aabb.h
|
include/aabb.h
|
||||||
include/octree.h
|
include/octree.h
|
||||||
src/main.cpp
|
src/main.cpp
|
||||||
src/scene.cpp)
|
src/scene.cpp)
|
||||||
target_include_directories(raytracer PUBLIC include PRIVATE ${GLM_INCLUDE_DIR})
|
target_include_directories(raytracer PUBLIC include PRIVATE ${GLM_INCLUDE_DIR})
|
||||||
target_link_libraries(raytracer PUBLIC stb SDL2::Main imgui glad)
|
target_link_libraries(raytracer PUBLIC stb SDL2::Main imgui glad)
|
||||||
set_target_properties(raytracer PROPERTIES
|
set_target_properties(raytracer PROPERTIES
|
||||||
CXX_STANDARD 17
|
CXX_STANDARD 17
|
||||||
CXX_STANDARD_REQUIRED YES
|
CXX_STANDARD_REQUIRED YES
|
||||||
CXX_EXTENSIONS NO
|
CXX_EXTENSIONS NO
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# Raytracer
|
# Raytracer
|
||||||
|
|
||||||
A multi-threaded raytracer using glm, tinyobjloader and C++17. The UI is written in imgui and image display is
|
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.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
|
@ -2,28 +2,25 @@
|
||||||
|
|
||||||
struct AABB {
|
struct AABB {
|
||||||
glm::vec3 min, max;
|
glm::vec3 min, max;
|
||||||
|
|
||||||
glm::vec3 center() const {
|
glm::vec3 center() const {
|
||||||
return 0.5f * (max + min);
|
return 0.5f * (max + min);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 extent() const {
|
glm::vec3 extent() const {
|
||||||
return max - center();
|
return max - center();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(const glm::vec3 point) const {
|
bool contains(const glm::vec3 point) const {
|
||||||
return glm::all(glm::lessThan(point, max)) && glm::all(glm::greaterThan(point, min));
|
return glm::all(glm::lessThan(point, max)) && glm::all(glm::greaterThan(point, min));
|
||||||
}
|
}
|
||||||
|
|
||||||
bool inside(const AABB extent) const {
|
bool inside(const AABB extent) const {
|
||||||
return(max.x > extent.min.x &&
|
return (
|
||||||
min.x < extent.max.x &&
|
max.x > extent.min.x && min.x < extent.max.x && max.y > extent.min.y && min.y < extent.max.y &&
|
||||||
max.y > extent.min.y &&
|
max.z > extent.min.z && min.z < extent.max.z);
|
||||||
min.y < extent.max.y &&
|
|
||||||
max.z > extent.min.z &&
|
|
||||||
min.z < extent.max.z);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool contains(const Ray& ray) const {
|
bool contains(const Ray& ray) const {
|
||||||
const float t1 = (min.x - ray.origin.x) / ray.direction.x;
|
const float t1 = (min.x - ray.origin.x) / ray.direction.x;
|
||||||
const float t2 = (max.x - ray.origin.x) / ray.direction.x;
|
const float t2 = (max.x - ray.origin.x) / ray.direction.x;
|
||||||
|
@ -31,19 +28,19 @@ struct AABB {
|
||||||
const float t4 = (max.y - ray.origin.y) / ray.direction.y;
|
const float t4 = (max.y - ray.origin.y) / ray.direction.y;
|
||||||
const float t5 = (min.z - ray.origin.z) / ray.direction.z;
|
const float t5 = (min.z - ray.origin.z) / ray.direction.z;
|
||||||
const float t6 = (max.z - ray.origin.z) / ray.direction.z;
|
const float t6 = (max.z - ray.origin.z) / ray.direction.z;
|
||||||
|
|
||||||
const float tmin = std::min(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
|
const float tmin = std::min(std::max(std::min(t1, t2), std::min(t3, t4)), std::min(t5, t6));
|
||||||
const float tmax = std::min(std::min(std::max(t1, t2), std::max(t3, t4)), std::max(t5, t6));
|
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, ray (line) is intersecting AABB, but whole AABB is behing us
|
||||||
//if(tmax < 0)
|
// if(tmax < 0)
|
||||||
// return false;
|
// return false;
|
||||||
|
|
||||||
// if tmin > tmax, ray doesn't intersect AABB
|
// if tmin > tmax, ray doesn't intersect AABB
|
||||||
if(tmin > tmax)
|
if (tmin > tmax)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if(tmin < 0.0f)
|
if (tmin < 0.0f)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
|
@ -5,24 +5,26 @@
|
||||||
class Camera {
|
class Camera {
|
||||||
public:
|
public:
|
||||||
Camera() : position(glm::vec3(0)), direction(glm::vec3(0)) {}
|
Camera() : position(glm::vec3(0)), direction(glm::vec3(0)) {}
|
||||||
|
|
||||||
Camera(glm::vec3 position, glm::vec3 direction) : position(position), direction(direction) {}
|
Camera(glm::vec3 position, glm::vec3 direction) : position(position), direction(direction) {}
|
||||||
|
|
||||||
void look_at(glm::vec3 eye, glm::vec3 target) {
|
void look_at(glm::vec3 eye, glm::vec3 target) {
|
||||||
position = eye;
|
position = eye;
|
||||||
direction = glm::normalize(target - eye);
|
direction = glm::normalize(target - eye);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ray get_ray(const int32_t x, const int32_t y, const int32_t width, const int32_t height) const {
|
Ray get_ray(const int32_t x, const int32_t y, const int32_t width, const int32_t height) const {
|
||||||
const glm::vec3 up = glm::vec3(0, 1, 0);
|
const glm::vec3 up = glm::vec3(0, 1, 0);
|
||||||
const glm::vec3 right = glm::normalize(glm::cross(direction, up));
|
const glm::vec3 right = glm::normalize(glm::cross(direction, up));
|
||||||
|
|
||||||
const float h2 = height / 2.0f;
|
const float h2 = height / 2.0f;
|
||||||
const float w2 = width / 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);
|
return Ray(position, ray_dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 position, direction;
|
glm::vec3 position, direction;
|
||||||
float fov = 45.0f;
|
float fov = 45.0f;
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,22 +1,21 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
template<class T, int Width, int Height>
|
template<class T, int Width, int Height> class Image {
|
||||||
class Image {
|
|
||||||
public:
|
public:
|
||||||
void reset() {
|
void reset() {
|
||||||
array = {};
|
array = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
T& get(const int32_t x, const int32_t y) {
|
T& get(const int32_t x, const int32_t y) {
|
||||||
return array[y * Width + x];
|
return array[y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
T get(const int32_t x, const int32_t y) const {
|
T get(const int32_t x, const int32_t y) const {
|
||||||
return array[y * Width + x];
|
return array[y * Width + x];
|
||||||
}
|
}
|
||||||
|
|
||||||
std::array<T, Width * Height> array = {};
|
std::array<T, Width* Height> array = {};
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,53 +12,57 @@ namespace intersections {
|
||||||
const float b = glm::dot(ray.direction, diff);
|
const float b = glm::dot(ray.direction, diff);
|
||||||
const float c = glm::dot(diff, diff) - sphere.w * sphere.w;
|
const float c = glm::dot(diff, diff) - sphere.w * sphere.w;
|
||||||
float t = b * b - c;
|
float t = b * b - c;
|
||||||
|
|
||||||
if (t > 0.0) {
|
if (t > 0.0) {
|
||||||
t = -b - glm::sqrt(t);
|
t = -b - glm::sqrt(t);
|
||||||
if (t > 0.0)
|
if (t > 0.0)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline float ray_triangle(const Ray ray,
|
inline float ray_triangle(
|
||||||
const glm::vec3 v0,
|
const Ray ray,
|
||||||
const glm::vec3 v1,
|
const glm::vec3 v0,
|
||||||
const glm::vec3 v2,
|
const glm::vec3 v1,
|
||||||
float& t,
|
const glm::vec3 v2,
|
||||||
float& u,
|
float& t,
|
||||||
float& v) {
|
float& u,
|
||||||
|
float& v) {
|
||||||
glm::vec3 e1 = v1 - v0;
|
glm::vec3 e1 = v1 - v0;
|
||||||
glm::vec3 e2 = v2 - v0;
|
glm::vec3 e2 = v2 - v0;
|
||||||
|
|
||||||
glm::vec3 pvec = glm::cross(ray.direction, e2);
|
glm::vec3 pvec = glm::cross(ray.direction, e2);
|
||||||
float det = glm::dot(e1, pvec);
|
float det = glm::dot(e1, pvec);
|
||||||
|
|
||||||
// if determinant is zero then ray is
|
// if determinant is zero then ray is
|
||||||
// parallel with the triangle plane
|
// parallel with the triangle plane
|
||||||
if (det > -epsilon && det < epsilon) return false;
|
if (det > -epsilon && det < epsilon)
|
||||||
float invdet = 1.0/det;
|
return false;
|
||||||
|
float invdet = 1.0 / det;
|
||||||
|
|
||||||
// calculate distance from m[0] to origin
|
// calculate distance from m[0] to origin
|
||||||
glm::vec3 tvec = ray.origin - v0;
|
glm::vec3 tvec = ray.origin - v0;
|
||||||
|
|
||||||
// u and v are the barycentric coordinates
|
// u and v are the barycentric coordinates
|
||||||
// in triangle if u >= 0, v >= 0 and u + v <= 1
|
// in triangle if u >= 0, v >= 0 and u + v <= 1
|
||||||
u = glm::dot(tvec, pvec) * invdet;
|
u = glm::dot(tvec, pvec) * invdet;
|
||||||
|
|
||||||
// check against one edge and opposite point
|
// 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);
|
glm::vec3 qvec = glm::cross(tvec, e1);
|
||||||
v = glm::dot(ray.direction, qvec) * invdet;
|
v = glm::dot(ray.direction, qvec) * invdet;
|
||||||
|
|
||||||
// check against other edges
|
// 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;
|
t = glm::dot(e2, qvec) * invdet;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
};
|
}; // namespace intersections
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace lighting {
|
||||||
inline float point_light(const glm::vec3 pos, const glm::vec3 light, const glm::vec3 normal) {
|
inline float point_light(const glm::vec3 pos, const glm::vec3 light, const glm::vec3 normal) {
|
||||||
const glm::vec3 dir = glm::normalize(light - pos);
|
const glm::vec3 dir = glm::normalize(light - pos);
|
||||||
const float n_dot_l = glm::max(glm::dot(normal, dir), 0.0f);
|
const float n_dot_l = glm::max(glm::dot(normal, dir), 0.0f);
|
||||||
|
|
||||||
return n_dot_l;
|
return n_dot_l;
|
||||||
}
|
}
|
||||||
};
|
}; // namespace lighting
|
||||||
|
|
|
@ -18,73 +18,71 @@ constexpr auto child_pattern = {
|
||||||
glm::vec3(-1, +1, +1),
|
glm::vec3(-1, +1, +1),
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename UnderlyingType>
|
template<typename UnderlyingType> struct Node {
|
||||||
struct Node {
|
|
||||||
using NodeType = Node<UnderlyingType>;
|
using NodeType = Node<UnderlyingType>;
|
||||||
|
|
||||||
AABB extent;
|
AABB extent;
|
||||||
int depth = 0;
|
int depth = 0;
|
||||||
|
|
||||||
std::vector<UnderlyingType> contained_objects;
|
std::vector<UnderlyingType> contained_objects;
|
||||||
|
|
||||||
std::array<std::unique_ptr<NodeType>, 8> children;
|
std::array<std::unique_ptr<NodeType>, 8> children;
|
||||||
bool is_split = false;
|
bool is_split = false;
|
||||||
|
|
||||||
void add(UnderlyingType& object, const AABB extent) {
|
void add(UnderlyingType& object, const AABB extent) {
|
||||||
if(is_split) {
|
if (is_split) {
|
||||||
for(auto& child : children) {
|
for (auto& child : children) {
|
||||||
if(extent.inside(child->extent))
|
if (extent.inside(child->extent))
|
||||||
child->add(object, extent);
|
child->add(object, extent);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
contained_objects.push_back(object);
|
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();
|
split();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void split() {
|
void split() {
|
||||||
is_split = true;
|
is_split = true;
|
||||||
|
|
||||||
const auto center = extent.center();
|
const auto center = extent.center();
|
||||||
const auto split_size = extent.extent().x / 2.0f;
|
const auto split_size = extent.extent().x / 2.0f;
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(auto& point : child_pattern) {
|
for (auto& point : child_pattern) {
|
||||||
auto child = std::make_unique<NodeType>();
|
auto child = std::make_unique<NodeType>();
|
||||||
child->depth = depth + 1;
|
child->depth = depth + 1;
|
||||||
|
|
||||||
const auto position = center + point * split_size;
|
const auto position = center + point * split_size;
|
||||||
child->extent.min = glm::vec3(position.x - split_size, position.y - split_size, position.z - split_size);
|
child->extent.min = glm::vec3(position.x - split_size, position.y - split_size, position.z - split_size);
|
||||||
child->extent.max = glm::vec3(position.x + split_size, position.y + split_size, position.z + split_size);
|
child->extent.max = glm::vec3(position.x + split_size, position.y + split_size, position.z + split_size);
|
||||||
|
|
||||||
children[i++] = std::move(child);
|
children[i++] = std::move(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& object : contained_objects) {
|
for (auto& object : contained_objects) {
|
||||||
for(auto& child : children) {
|
for (auto& child : children) {
|
||||||
if(object.extent.inside(child->extent))
|
if (object.extent.inside(child->extent))
|
||||||
child->add(object, extent);
|
child->add(object, extent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
contained_objects.clear();
|
contained_objects.clear();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
template<typename UnderlyingType>
|
template<typename UnderlyingType> struct Octree {
|
||||||
struct Octree {
|
|
||||||
using NodeType = Node<UnderlyingType>;
|
using NodeType = Node<UnderlyingType>;
|
||||||
|
|
||||||
NodeType root;
|
NodeType root;
|
||||||
|
|
||||||
Octree(const glm::vec3 min, const glm::vec3 max) {
|
Octree(const glm::vec3 min, const glm::vec3 max) {
|
||||||
root = NodeType();
|
root = NodeType();
|
||||||
root.extent.min = min;
|
root.extent.min = min;
|
||||||
root.extent.max = max;
|
root.extent.max = max;
|
||||||
}
|
}
|
||||||
|
|
||||||
void add(UnderlyingType& object, const AABB extent) {
|
void add(UnderlyingType& object, const AABB extent) {
|
||||||
root.add(object, extent);
|
root.add(object, extent);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +1,17 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
|
||||||
#include <tiny_obj_loader.h>
|
#include <tiny_obj_loader.h>
|
||||||
|
|
||||||
#include "ray.h"
|
|
||||||
#include "intersections.h"
|
#include "intersections.h"
|
||||||
#include "lighting.h"
|
#include "lighting.h"
|
||||||
#include "octree.h"
|
#include "octree.h"
|
||||||
|
#include "ray.h"
|
||||||
|
|
||||||
constexpr glm::vec3 light_position = glm::vec3(5);
|
constexpr glm::vec3 light_position = glm::vec3(5);
|
||||||
constexpr float light_bias = 0.01f;
|
constexpr float light_bias = 0.01f;
|
||||||
|
@ -32,34 +32,34 @@ glm::vec3 fetch_normal(const Object& object, const tinyobj::mesh_t& mesh, const
|
||||||
struct Object {
|
struct Object {
|
||||||
glm::vec3 position = glm::vec3(0);
|
glm::vec3 position = glm::vec3(0);
|
||||||
glm::vec3 color = glm::vec3(1);
|
glm::vec3 color = glm::vec3(1);
|
||||||
|
|
||||||
tinyobj::attrib_t attrib;
|
tinyobj::attrib_t attrib;
|
||||||
std::vector<tinyobj::shape_t> shapes;
|
std::vector<tinyobj::shape_t> shapes;
|
||||||
std::vector<tinyobj::material_t> materials;
|
std::vector<tinyobj::material_t> materials;
|
||||||
|
|
||||||
std::unique_ptr<Octree<TriangleBox>> octree;
|
std::unique_ptr<Octree<TriangleBox>> octree;
|
||||||
|
|
||||||
void create_octree() {
|
void create_octree() {
|
||||||
octree = std::make_unique<Octree<TriangleBox>>(glm::vec3(-2), glm::vec3(2));
|
octree = std::make_unique<Octree<TriangleBox>>(glm::vec3(-2), glm::vec3(2));
|
||||||
|
|
||||||
for(auto& shape : shapes) {
|
for (auto& shape : shapes) {
|
||||||
for(size_t i = 0; i < shape.mesh.num_face_vertices.size(); i++) {
|
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 v0 = fetch_position(*this, shape.mesh, i, 0);
|
||||||
const glm::vec3 v1 = fetch_position(*this, shape.mesh, i, 1);
|
const glm::vec3 v1 = fetch_position(*this, shape.mesh, i, 1);
|
||||||
const glm::vec3 v2 = fetch_position(*this, shape.mesh, i, 2);
|
const glm::vec3 v2 = fetch_position(*this, shape.mesh, i, 2);
|
||||||
|
|
||||||
AABB extent;
|
AABB extent;
|
||||||
extent.min = glm::min(v0, v1);
|
extent.min = glm::min(v0, v1);
|
||||||
extent.min = glm::min(extent.min, v2);
|
extent.min = glm::min(extent.min, v2);
|
||||||
|
|
||||||
extent.max = glm::max(v0, v1);
|
extent.max = glm::max(v0, v1);
|
||||||
extent.max = glm::max(extent.max, v2);
|
extent.max = glm::max(extent.max, v2);
|
||||||
|
|
||||||
TriangleBox box = {};
|
TriangleBox box = {};
|
||||||
box.vertice_index = i;
|
box.vertice_index = i;
|
||||||
box.extent = extent;
|
box.extent = extent;
|
||||||
box.mesh = &shape.mesh;
|
box.mesh = &shape.mesh;
|
||||||
|
|
||||||
octree->add(box, box.extent);
|
octree->add(box, box.extent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,29 +68,29 @@ struct Object {
|
||||||
|
|
||||||
struct Scene {
|
struct Scene {
|
||||||
std::vector<std::unique_ptr<Object>> objects;
|
std::vector<std::unique_ptr<Object>> objects;
|
||||||
|
|
||||||
std::random_device rd;
|
std::random_device rd;
|
||||||
std::mt19937 gen;
|
std::mt19937 gen;
|
||||||
std::uniform_real_distribution<> dis;
|
std::uniform_real_distribution<> dis;
|
||||||
|
|
||||||
Scene() : gen(rd()), dis(0.0, 1.0) {}
|
Scene() : gen(rd()), dis(0.0, 1.0) {}
|
||||||
|
|
||||||
float distribution() {
|
float distribution() {
|
||||||
return dis(gen);
|
return dis(gen);
|
||||||
}
|
}
|
||||||
|
|
||||||
Object& load_from_file(const std::string_view path) {
|
Object& load_from_file(const std::string_view path) {
|
||||||
auto o = std::make_unique<Object>();
|
auto o = std::make_unique<Object>();
|
||||||
|
|
||||||
std::string err;
|
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;
|
std::cerr << "Could not load obj: " << err;
|
||||||
|
|
||||||
return *objects.emplace_back(std::move(o));
|
return *objects.emplace_back(std::move(o));
|
||||||
}
|
}
|
||||||
|
|
||||||
void generate_acceleration() {
|
void generate_acceleration() {
|
||||||
for(auto& object : objects) {
|
for (auto& object : objects) {
|
||||||
object->create_octree();
|
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);
|
std::optional<SceneResult> cast_scene(const Ray ray, Scene& scene, const bool use_bvh, const int depth = 0);
|
||||||
|
|
||||||
|
|
249
src/main.cpp
249
src/main.cpp
|
@ -1,23 +1,23 @@
|
||||||
#include <iostream>
|
|
||||||
#include <limits>
|
|
||||||
#include <future>
|
|
||||||
#include <vector>
|
|
||||||
#include <glm/glm.hpp>
|
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <future>
|
||||||
|
#include <glm/glm.hpp>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
||||||
#include <stb_image_write.h>
|
#include <stb_image_write.h>
|
||||||
|
|
||||||
#include "intersections.h"
|
|
||||||
#include "camera.h"
|
#include "camera.h"
|
||||||
|
#include "glad/glad.h"
|
||||||
#include "image.h"
|
#include "image.h"
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#include "imgui_impl_sdl.h"
|
||||||
|
#include "intersections.h"
|
||||||
#include "lighting.h"
|
#include "lighting.h"
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
#include "glad/glad.h"
|
|
||||||
#include "imgui.h"
|
|
||||||
#include "imgui_impl_sdl.h"
|
|
||||||
#include "imgui_impl_opengl3.h"
|
|
||||||
|
|
||||||
#define TINYOBJLOADER_IMPLEMENTATION
|
#define TINYOBJLOADER_IMPLEMENTATION
|
||||||
#include <tiny_obj_loader.h>
|
#include <tiny_obj_loader.h>
|
||||||
|
@ -29,7 +29,7 @@ bool use_bvh = true;
|
||||||
const Camera camera = [] {
|
const Camera camera = [] {
|
||||||
Camera camera;
|
Camera camera;
|
||||||
camera.look_at(glm::vec3(0, 0, 3), glm::vec3(0));
|
camera.look_at(glm::vec3(0, 0, 3), glm::vec3(0));
|
||||||
|
|
||||||
return camera;
|
return camera;
|
||||||
}();
|
}();
|
||||||
|
|
||||||
|
@ -50,23 +50,18 @@ enum class DisplayMode {
|
||||||
Reflect
|
Reflect
|
||||||
};
|
};
|
||||||
|
|
||||||
const std::array diplay_mode_strings = {
|
const std::array diplay_mode_strings = {"Combined", "Direct", "Indirect", "Reflect"};
|
||||||
"Combined",
|
|
||||||
"Direct",
|
|
||||||
"Indirect",
|
|
||||||
"Reflect"
|
|
||||||
};
|
|
||||||
|
|
||||||
inline DisplayMode display_mode;
|
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) {
|
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 y = from_y; y < (from_y + to_height); y++) {
|
||||||
for(int32_t x = from_x; x < (from_x + to_width); x++) {
|
for (int32_t x = from_x; x < (from_x + to_width); x++) {
|
||||||
Ray ray_camera = camera.get_ray(x, y, width, height);
|
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;
|
glm::vec3 chosen_display;
|
||||||
switch(display_mode) {
|
switch (display_mode) {
|
||||||
case DisplayMode::Combined:
|
case DisplayMode::Combined:
|
||||||
chosen_display = result->combined;
|
chosen_display = result->combined;
|
||||||
break;
|
break;
|
||||||
|
@ -80,14 +75,14 @@ bool calculate_tile(const int32_t from_x, const int32_t to_width, const int32_t
|
||||||
chosen_display = result->reflect;
|
chosen_display = result->reflect;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
colors.get(x, y) = glm::vec4(chosen_display, 1.0f);
|
colors.get(x, y) = glm::vec4(chosen_display, 1.0f);
|
||||||
|
|
||||||
image_dirty = true;
|
image_dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,76 +93,70 @@ GLuint pixels_texture = 0;
|
||||||
void setup_gfx() {
|
void setup_gfx() {
|
||||||
// create quad for pixel rendering
|
// create quad for pixel rendering
|
||||||
constexpr std::array vertices = {
|
constexpr std::array vertices = {
|
||||||
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f,
|
-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.5f, 0.0f, 1.0f, 0.0f,
|
-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
|
||||||
0.5f, -0.5f, 0.0f, 1.0f, -1.0f,
|
// tex coord
|
||||||
-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 = {
|
constexpr std::array elements = {0, 1, 2, 2, 3, 0};
|
||||||
0, 1, 2,
|
|
||||||
2, 3, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
glGenVertexArrays(1, &quad_vao);
|
glGenVertexArrays(1, &quad_vao);
|
||||||
glBindVertexArray(quad_vao);
|
glBindVertexArray(quad_vao);
|
||||||
|
|
||||||
GLuint vbo = 0;
|
GLuint vbo = 0;
|
||||||
glGenBuffers(1, &vbo);
|
glGenBuffers(1, &vbo);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
||||||
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
|
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), nullptr);
|
||||||
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float)));
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), reinterpret_cast<void*>(3 * sizeof(float)));
|
||||||
|
|
||||||
glEnableVertexAttribArray(0);
|
glEnableVertexAttribArray(0);
|
||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
|
|
||||||
GLuint ebo = 0;
|
GLuint ebo = 0;
|
||||||
glGenBuffers(1, &ebo);
|
glGenBuffers(1, &ebo);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(uint32_t), elements.data(), GL_STATIC_DRAW);
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(uint32_t), elements.data(), GL_STATIC_DRAW);
|
||||||
|
|
||||||
constexpr std::string_view vertex_glsl =
|
constexpr std::string_view vertex_glsl = "#version 330 core\n"
|
||||||
"#version 330 core\n"
|
"layout (location = 0) in vec3 in_position;\n"
|
||||||
"layout (location = 0) in vec3 in_position;\n"
|
"layout (location = 1) in vec2 in_uv;\n"
|
||||||
"layout (location = 1) in vec2 in_uv;\n"
|
"out vec2 uv;\n"
|
||||||
"out vec2 uv;\n"
|
"void main()\n"
|
||||||
"void main()\n"
|
"{\n"
|
||||||
"{\n"
|
" gl_Position = vec4(in_position.xyz, 1.0);\n"
|
||||||
" gl_Position = vec4(in_position.xyz, 1.0);\n"
|
" uv = in_uv;\n"
|
||||||
" uv = in_uv;\n"
|
"}\n";
|
||||||
"}\n";
|
|
||||||
const char* vertex_src = vertex_glsl.data();
|
const char* vertex_src = vertex_glsl.data();
|
||||||
|
|
||||||
constexpr std::string_view fragment_glsl =
|
constexpr std::string_view fragment_glsl = "#version 330 core\n"
|
||||||
"#version 330 core\n"
|
"in vec2 uv;\n"
|
||||||
"in vec2 uv;\n"
|
"out vec4 out_color;\n"
|
||||||
"out vec4 out_color;\n"
|
"uniform sampler2D pixel_texture;\n"
|
||||||
"uniform sampler2D pixel_texture;\n"
|
"void main()\n"
|
||||||
"void main()\n"
|
"{\n"
|
||||||
"{\n"
|
" out_color = texture(pixel_texture, uv);\n"
|
||||||
" out_color = texture(pixel_texture, uv);\n"
|
"}\n";
|
||||||
"}\n";
|
|
||||||
const char* fragment_src = fragment_glsl.data();
|
const char* fragment_src = fragment_glsl.data();
|
||||||
|
|
||||||
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
||||||
glShaderSource(vertex_shader, 1, &vertex_src, nullptr);
|
glShaderSource(vertex_shader, 1, &vertex_src, nullptr);
|
||||||
glCompileShader(vertex_shader);
|
glCompileShader(vertex_shader);
|
||||||
|
|
||||||
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
||||||
glShaderSource(fragment_shader, 1, &fragment_src, nullptr);
|
glShaderSource(fragment_shader, 1, &fragment_src, nullptr);
|
||||||
glCompileShader(fragment_shader);
|
glCompileShader(fragment_shader);
|
||||||
|
|
||||||
pixel_program = glCreateProgram();
|
pixel_program = glCreateProgram();
|
||||||
|
|
||||||
glAttachShader(pixel_program, vertex_shader);
|
glAttachShader(pixel_program, vertex_shader);
|
||||||
glAttachShader(pixel_program, fragment_shader);
|
glAttachShader(pixel_program, fragment_shader);
|
||||||
glLinkProgram(pixel_program);
|
glLinkProgram(pixel_program);
|
||||||
|
|
||||||
glDeleteShader(vertex_shader);
|
glDeleteShader(vertex_shader);
|
||||||
glDeleteShader(fragment_shader);
|
glDeleteShader(fragment_shader);
|
||||||
|
|
||||||
glGenTextures(1, &pixels_texture);
|
glGenTextures(1, &pixels_texture);
|
||||||
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
||||||
|
@ -187,9 +176,9 @@ std::vector<std::future<bool>> futures;
|
||||||
void render() {
|
void render() {
|
||||||
futures.clear();
|
futures.clear();
|
||||||
colors.reset();
|
colors.reset();
|
||||||
|
|
||||||
for(int32_t y = 0; y < num_tiles_y; y++) {
|
for (int32_t y = 0; y < num_tiles_y; y++) {
|
||||||
for(int32_t x = 0; x < num_tiles_x; x++) {
|
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);
|
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));
|
futures.push_back(std::move(f));
|
||||||
}
|
}
|
||||||
|
@ -198,10 +187,10 @@ void render() {
|
||||||
|
|
||||||
void dump_to_file() {
|
void dump_to_file() {
|
||||||
uint8_t pixels[width * height * 3] = {};
|
uint8_t pixels[width * height * 3] = {};
|
||||||
|
|
||||||
int i = 0;
|
int i = 0;
|
||||||
for(int32_t y = height - 1; y >= 0; y--) {
|
for (int32_t y = height - 1; y >= 0; y--) {
|
||||||
for(int32_t x = 0; x < width; x++) {
|
for (int32_t x = 0; x < width; x++) {
|
||||||
const glm::ivec4 c = colors.get(x, y);
|
const glm::ivec4 c = colors.get(x, y);
|
||||||
pixels[i++] = c.r;
|
pixels[i++] = c.r;
|
||||||
pixels[i++] = c.g;
|
pixels[i++] = c.g;
|
||||||
|
@ -212,17 +201,24 @@ void dump_to_file() {
|
||||||
stbi_write_png("output.png", width, height, 3, pixels, width * 3);
|
stbi_write_png("output.png", width, height, 3, pixels, width * 3);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename UnderlyingType>
|
template<typename UnderlyingType> void walk_node(Node<UnderlyingType>& node) {
|
||||||
void walk_node(Node<UnderlyingType>& node) {
|
if (ImGui::TreeNode(
|
||||||
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)) {
|
&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("Is split: %i", node.is_split);
|
||||||
ImGui::Text("Contained triangles: %lu", node.contained_objects.size());
|
ImGui::Text("Contained triangles: %lu", node.contained_objects.size());
|
||||||
|
|
||||||
if(node.is_split) {
|
if (node.is_split) {
|
||||||
for(auto& child : node.children)
|
for (auto& child : node.children)
|
||||||
walk_node(*child);
|
walk_node(*child);
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -233,104 +229,105 @@ void walk_object(Object& object) {
|
||||||
|
|
||||||
int main(int, char*[]) {
|
int main(int, char*[]) {
|
||||||
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
||||||
|
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_FORWARD_COMPATIBLE_FLAG);
|
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_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||||
|
|
||||||
SDL_Window* window = SDL_CreateWindow("raytracer",
|
SDL_Window* window = SDL_CreateWindow(
|
||||||
SDL_WINDOWPOS_CENTERED,
|
"raytracer",
|
||||||
SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED,
|
||||||
800,
|
SDL_WINDOWPOS_CENTERED,
|
||||||
600,
|
800,
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
600,
|
||||||
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
||||||
|
|
||||||
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
||||||
SDL_GL_MakeCurrent(window, gl_context);
|
SDL_GL_MakeCurrent(window, gl_context);
|
||||||
SDL_GL_SetSwapInterval(1);
|
SDL_GL_SetSwapInterval(1);
|
||||||
|
|
||||||
gladLoadGL();
|
gladLoadGL();
|
||||||
setup_gfx();
|
setup_gfx();
|
||||||
|
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
||||||
ImGui_ImplOpenGL3_Init("#version 330 core");
|
ImGui_ImplOpenGL3_Init("#version 330 core");
|
||||||
|
|
||||||
bool running = true;
|
bool running = true;
|
||||||
while(running) {
|
while (running) {
|
||||||
SDL_Event event = {};
|
SDL_Event event = {};
|
||||||
while(SDL_PollEvent(&event)) {
|
while (SDL_PollEvent(&event)) {
|
||||||
ImGui_ImplSDL2_ProcessEvent(&event);
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
||||||
|
|
||||||
if(event.type == SDL_QUIT)
|
if (event.type == SDL_QUIT)
|
||||||
running = false;
|
running = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplSDL2_NewFrame(window);
|
ImGui_ImplSDL2_NewFrame(window);
|
||||||
|
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
if(ImGui::BeginMainMenuBar()) {
|
if (ImGui::BeginMainMenuBar()) {
|
||||||
if(ImGui::BeginMenu("File")) {
|
if (ImGui::BeginMenu("File")) {
|
||||||
if(ImGui::Button("Load Example Models")) {
|
if (ImGui::Button("Load Example Models")) {
|
||||||
auto& sphere = scene.load_from_file("misc/sphere.obj");
|
auto& sphere = scene.load_from_file("misc/sphere.obj");
|
||||||
sphere.color = {1, 1, 1};
|
sphere.color = {1, 1, 1};
|
||||||
|
|
||||||
auto& plane = scene.load_from_file("misc/plane.obj");
|
auto& plane = scene.load_from_file("misc/plane.obj");
|
||||||
plane.position.y = -1;
|
plane.position.y = -1;
|
||||||
plane.color = {1, 0, 0};
|
plane.color = {1, 0, 0};
|
||||||
|
|
||||||
scene.generate_acceleration();
|
scene.generate_acceleration();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::EndMainMenuBar();
|
ImGui::EndMainMenuBar();
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::Begin("Render Options");
|
ImGui::Begin("Render Options");
|
||||||
|
|
||||||
ImGui::Checkbox("Use BVH", &use_bvh);
|
ImGui::Checkbox("Use BVH", &use_bvh);
|
||||||
ImGui::InputInt("Indirect Samples", &num_indirect_samples);
|
ImGui::InputInt("Indirect Samples", &num_indirect_samples);
|
||||||
|
|
||||||
if(ImGui::BeginCombo("Channel Selection", diplay_mode_strings[static_cast<int>(display_mode)])) {
|
if (ImGui::BeginCombo("Channel Selection", diplay_mode_strings[static_cast<int>(display_mode)])) {
|
||||||
if(ImGui::Selectable("Combined"))
|
if (ImGui::Selectable("Combined"))
|
||||||
display_mode = DisplayMode::Combined;
|
display_mode = DisplayMode::Combined;
|
||||||
|
|
||||||
if(ImGui::Selectable("Direct"))
|
if (ImGui::Selectable("Direct"))
|
||||||
display_mode = DisplayMode::Direct;
|
display_mode = DisplayMode::Direct;
|
||||||
|
|
||||||
if(ImGui::Selectable("Indirect"))
|
if (ImGui::Selectable("Indirect"))
|
||||||
display_mode = DisplayMode::Indirect;
|
display_mode = DisplayMode::Indirect;
|
||||||
|
|
||||||
if(ImGui::Selectable("Reflect"))
|
if (ImGui::Selectable("Reflect"))
|
||||||
display_mode = DisplayMode::Reflect;
|
display_mode = DisplayMode::Reflect;
|
||||||
|
|
||||||
ImGui::EndCombo();
|
ImGui::EndCombo();
|
||||||
}
|
}
|
||||||
|
|
||||||
if(ImGui::Button("Render"))
|
if (ImGui::Button("Render"))
|
||||||
render();
|
render();
|
||||||
|
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
if(ImGui::Button("Save Output"))
|
if (ImGui::Button("Save Output"))
|
||||||
dump_to_file();
|
dump_to_file();
|
||||||
|
|
||||||
if(image_dirty) {
|
if (image_dirty) {
|
||||||
update_texture();
|
update_texture();
|
||||||
image_dirty = false;
|
image_dirty = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
for(auto& object : scene.objects) {
|
for (auto& object : scene.objects) {
|
||||||
if(ImGui::TreeNode("Object")) {
|
if (ImGui::TreeNode("Object")) {
|
||||||
walk_object(*object);
|
walk_object(*object);
|
||||||
|
|
||||||
ImGui::TreePop();
|
ImGui::TreePop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -338,18 +335,18 @@ int main(int, char*[]) {
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
auto& io = ImGui::GetIO();
|
auto& io = ImGui::GetIO();
|
||||||
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
|
||||||
glUseProgram(pixel_program);
|
glUseProgram(pixel_program);
|
||||||
glBindVertexArray(quad_vao);
|
glBindVertexArray(quad_vao);
|
||||||
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
||||||
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
||||||
|
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
SDL_GL_SwapWindow(window);
|
SDL_GL_SwapWindow(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
152
src/scene.cpp
152
src/scene.cpp
|
@ -1,48 +1,55 @@
|
||||||
#include "scene.h"
|
#include "scene.h"
|
||||||
|
|
||||||
#include <glm/gtx/perpendicular.hpp>
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
#include <glm/gtx/perpendicular.hpp>
|
||||||
|
|
||||||
constexpr double pi = 3.14159265358979323846l;
|
constexpr double pi = 3.14159265358979323846l;
|
||||||
|
|
||||||
glm::vec3 fetch_position(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
|
glm::vec3 fetch_position(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
|
||||||
const tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
|
const tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
|
||||||
|
|
||||||
const auto vx = object.attrib.vertices[3 * idx.vertex_index];
|
const auto vx = object.attrib.vertices[3 * idx.vertex_index];
|
||||||
const auto vy = object.attrib.vertices[3 * idx.vertex_index + 1];
|
const auto vy = object.attrib.vertices[3 * idx.vertex_index + 1];
|
||||||
const auto vz = object.attrib.vertices[3 * idx.vertex_index + 2];
|
const auto vz = object.attrib.vertices[3 * idx.vertex_index + 2];
|
||||||
|
|
||||||
return glm::vec3(vx, vy, vz);
|
return glm::vec3(vx, vy, vz);
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 fetch_normal(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
|
glm::vec3 fetch_normal(const Object& object, const tinyobj::mesh_t& mesh, const int32_t index, const int32_t vertex) {
|
||||||
const tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
|
const tinyobj::index_t idx = mesh.indices[(index * 3) + vertex];
|
||||||
|
|
||||||
const auto nx = object.attrib.normals[3 * idx.normal_index];
|
const auto nx = object.attrib.normals[3 * idx.normal_index];
|
||||||
const auto ny = object.attrib.normals[3 * idx.normal_index + 1];
|
const auto ny = object.attrib.normals[3 * idx.normal_index + 1];
|
||||||
const auto nz = object.attrib.normals[3 * idx.normal_index + 2];
|
const auto nz = object.attrib.normals[3 * idx.normal_index + 2];
|
||||||
|
|
||||||
return glm::vec3(nx, ny, nz);
|
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 v0 = fetch_position(object, mesh, i, 0) + object.position;
|
||||||
const glm::vec3 v1 = fetch_position(object, mesh, i, 1) + 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;
|
const glm::vec3 v2 = fetch_position(object, mesh, i, 2) + object.position;
|
||||||
|
|
||||||
float t = std::numeric_limits<float>::infinity(), u, v;
|
float t = std::numeric_limits<float>::infinity(), u, v;
|
||||||
if(intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
|
if (intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
|
||||||
if(t < tClosest && t > epsilon) {
|
if (t < tClosest && t > epsilon) {
|
||||||
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
|
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
|
||||||
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
|
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
|
||||||
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
|
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
|
||||||
|
|
||||||
result.normal = (1 - u - v) * n0 + u * n1 + v * n2;
|
result.normal = (1 - u - v) * n0 + u * n1 + v * n2;
|
||||||
result.position = ray.origin + ray.direction * t;
|
result.position = ray.origin + ray.direction * t;
|
||||||
|
|
||||||
tClosest = t;
|
tClosest = t;
|
||||||
intersection = true;
|
intersection = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,30 +58,30 @@ bool test_triangle(const Ray ray, const Object& object, const tinyobj::mesh_t& m
|
||||||
std::optional<HitResult> test_mesh(const Ray ray, const Object& object, const tinyobj::mesh_t& mesh, float& tClosest) {
|
std::optional<HitResult> test_mesh(const Ray ray, const Object& object, const tinyobj::mesh_t& mesh, float& tClosest) {
|
||||||
bool intersection = false;
|
bool intersection = false;
|
||||||
HitResult result = {};
|
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 v0 = fetch_position(object, mesh, i, 0) + object.position;
|
||||||
const glm::vec3 v1 = fetch_position(object, mesh, i, 1) + 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;
|
const glm::vec3 v2 = fetch_position(object, mesh, i, 2) + object.position;
|
||||||
|
|
||||||
float t = std::numeric_limits<float>::infinity(), u, v;
|
float t = std::numeric_limits<float>::infinity(), u, v;
|
||||||
if(intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
|
if (intersections::ray_triangle(ray, v0, v1, v2, t, u, v)) {
|
||||||
if(t < tClosest && t > epsilon) {
|
if (t < tClosest && t > epsilon) {
|
||||||
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
|
const glm::vec3 n0 = fetch_normal(object, mesh, i, 0);
|
||||||
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
|
const glm::vec3 n1 = fetch_normal(object, mesh, i, 1);
|
||||||
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
|
const glm::vec3 n2 = fetch_normal(object, mesh, i, 2);
|
||||||
|
|
||||||
result.normal = (1 - u - v) * n0 + u * n1 + v * n2;
|
result.normal = (1 - u - v) * n0 + u * n1 + v * n2;
|
||||||
result.position = ray.origin + ray.direction * t;
|
result.position = ray.origin + ray.direction * t;
|
||||||
|
|
||||||
tClosest = t;
|
tClosest = t;
|
||||||
|
|
||||||
intersection = true;
|
intersection = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(intersection)
|
if (intersection)
|
||||||
return result;
|
return result;
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
|
@ -84,30 +91,29 @@ std::optional<HitResult> test_scene(const Ray ray, const Scene& scene) {
|
||||||
bool intersection = false;
|
bool intersection = false;
|
||||||
HitResult result = {};
|
HitResult result = {};
|
||||||
float tClosest = std::numeric_limits<float>::infinity();
|
float tClosest = std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
for(auto& object : scene.objects) {
|
for (auto& object : scene.objects) {
|
||||||
for(uint32_t i = 0; i < object->shapes.size(); i++) {
|
for (uint32_t i = 0; i < object->shapes.size(); i++) {
|
||||||
auto mesh = object->shapes[i].mesh;
|
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;
|
intersection = true;
|
||||||
result = hit.value();
|
result = hit.value();
|
||||||
result.object = object.get();
|
result.object = object.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(intersection)
|
if (intersection)
|
||||||
return result;
|
return result;
|
||||||
else
|
else
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T> Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>* out) {
|
||||||
Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>* out) {
|
if (node.extent.contains(ray)) {
|
||||||
if(node.extent.contains(ray)) {
|
if (node.is_split) {
|
||||||
if(node.is_split) {
|
for (auto& child : node.children)
|
||||||
for(auto& child : node.children)
|
|
||||||
find_hit_ray_search(*child, ray, out);
|
find_hit_ray_search(*child, ray, out);
|
||||||
} else {
|
} else {
|
||||||
out->push_back(&node);
|
out->push_back(&node);
|
||||||
|
@ -117,12 +123,11 @@ Node<T>* find_hit_ray_search(Node<T>& node, const Ray ray, std::vector<Node<T>*>
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T> std::vector<Node<T>*> find_hit_ray(Node<T>& node, const Ray ray) {
|
||||||
std::vector<Node<T>*> find_hit_ray(Node<T>& node, const Ray ray) {
|
|
||||||
std::vector<Node<T>*> vec;
|
std::vector<Node<T>*> vec;
|
||||||
|
|
||||||
find_hit_ray_search(node, ray, &vec);
|
find_hit_ray_search(node, ray, &vec);
|
||||||
|
|
||||||
return vec;
|
return vec;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,17 +135,24 @@ std::optional<HitResult> test_scene_octree(const Ray ray, const Scene& scene) {
|
||||||
bool intersection = false;
|
bool intersection = false;
|
||||||
HitResult result = {};
|
HitResult result = {};
|
||||||
float tClosest = std::numeric_limits<float>::infinity();
|
float tClosest = std::numeric_limits<float>::infinity();
|
||||||
|
|
||||||
for(auto& object : scene.objects) {
|
for (auto& object : scene.objects) {
|
||||||
for(auto& node : find_hit_ray(object->octree->root, ray)) {
|
for (auto& node : find_hit_ray(object->octree->root, ray)) {
|
||||||
for(auto& triangle_object : node->contained_objects) {
|
for (auto& triangle_object : node->contained_objects) {
|
||||||
if(test_triangle(ray, *object, *triangle_object.mesh, triangle_object.vertice_index, tClosest, intersection, result))
|
if (test_triangle(
|
||||||
|
ray,
|
||||||
|
*object,
|
||||||
|
*triangle_object.mesh,
|
||||||
|
triangle_object.vertice_index,
|
||||||
|
tClosest,
|
||||||
|
intersection,
|
||||||
|
result))
|
||||||
result.object = object.get();
|
result.object = object.get();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if(intersection)
|
if (intersection)
|
||||||
return result;
|
return result;
|
||||||
else
|
else
|
||||||
return {};
|
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/
|
// methods adapated from https://users.cg.tuwien.ac.at/zsolnai/gfx/smallpaint/
|
||||||
std::tuple<glm::vec3, glm::vec3> orthogonal_system(const glm::vec3& v1) {
|
std::tuple<glm::vec3, glm::vec3> orthogonal_system(const glm::vec3& v1) {
|
||||||
glm::vec3 v2;
|
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
|
// 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);
|
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);
|
v2 = glm::vec3(-v1.z * inverse_length, 0.0f, v1.x * inverse_length);
|
||||||
|
@ -158,14 +170,14 @@ std::tuple<glm::vec3, glm::vec3> orthogonal_system(const glm::vec3& v1) {
|
||||||
const float inverse_length = 1.0f / sqrtf(v1.y * v1.y + v1.z * v1.z);
|
const float inverse_length = 1.0f / sqrtf(v1.y * v1.y + v1.z * v1.z);
|
||||||
v2 = glm::vec3(0.0f, v1.z * inverse_length, -v1.y * inverse_length);
|
v2 = glm::vec3(0.0f, v1.z * inverse_length, -v1.y * inverse_length);
|
||||||
}
|
}
|
||||||
|
|
||||||
return {v2, glm::cross(v1, v2)};
|
return {v2, glm::cross(v1, v2)};
|
||||||
}
|
}
|
||||||
|
|
||||||
glm::vec3 hemisphere(const double u1, const double u2) {
|
glm::vec3 hemisphere(const double u1, const double u2) {
|
||||||
const double r = sqrt(1.0 - u1 * u1);
|
const double r = sqrt(1.0 - u1 * u1);
|
||||||
const double phi = 2 * pi * u2;
|
const double phi = 2 * pi * u2;
|
||||||
|
|
||||||
return glm::vec3(cos(phi) * r, sin(phi) * r, u1);
|
return glm::vec3(cos(phi) * r, sin(phi) * r, u1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,58 +186,58 @@ 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) {
|
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 {};
|
return {};
|
||||||
|
|
||||||
const std::function<decltype(test_scene)> scene_func = use_bvh ? test_scene_octree : test_scene;
|
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);
|
const float diffuse = lighting::point_light(hit->position, light_position, hit->normal);
|
||||||
SceneResult result = {};
|
SceneResult result = {};
|
||||||
|
|
||||||
// direct lighting calculation
|
// direct lighting calculation
|
||||||
// currently only supports only one light (directional)
|
// 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 glm::vec3 light_dir = glm::normalize(light_position - hit->position);
|
||||||
|
|
||||||
const Ray shadow_ray(hit->position + (hit->normal * light_bias), light_dir);
|
const Ray shadow_ray(hit->position + (hit->normal * light_bias), light_dir);
|
||||||
|
|
||||||
const float shadow = scene_func(shadow_ray, scene) ? 0.0f : 1.0f;
|
const float shadow = scene_func(shadow_ray, scene) ? 0.0f : 1.0f;
|
||||||
|
|
||||||
result.direct = hit->object->color * diffuse * shadow;
|
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;
|
result.reflect = reflect_result->combined;
|
||||||
|
|
||||||
// indirect lighting calculation
|
// indirect lighting calculation
|
||||||
// we take a hemisphere orthogonal to the normal, and take a constant number of num_indirect_samples
|
// we take a hemisphere orthogonal to the normal, and take a constant number of num_indirect_samples
|
||||||
// and naive monte carlo without PDF
|
// and naive monte carlo without PDF
|
||||||
if(num_indirect_samples > 0) {
|
if (num_indirect_samples > 0) {
|
||||||
for(int i = 0; i < num_indirect_samples; i++) {
|
for (int i = 0; i < num_indirect_samples; i++) {
|
||||||
const float theta = scene.distribution() * pi;
|
const float theta = scene.distribution() * pi;
|
||||||
const float cos_theta = cos(theta);
|
const float cos_theta = cos(theta);
|
||||||
const float sin_theta = sin(theta);
|
const float sin_theta = sin(theta);
|
||||||
|
|
||||||
const auto [rotX, rotY] = orthogonal_system(hit->normal);
|
const auto [rotX, rotY] = orthogonal_system(hit->normal);
|
||||||
|
|
||||||
const glm::vec3 sampled_dir = hemisphere(cos_theta, sin_theta);
|
const glm::vec3 sampled_dir = hemisphere(cos_theta, sin_theta);
|
||||||
const glm::vec3 rotated_dir = {
|
const glm::vec3 rotated_dir = {
|
||||||
glm::dot({rotX.x, rotY.x, hit->normal.x}, sampled_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.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;
|
result.indirect += indirect_result->combined * cos_theta;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.indirect /= num_indirect_samples;
|
result.indirect /= num_indirect_samples;
|
||||||
}
|
}
|
||||||
|
|
||||||
result.hit = *hit;
|
result.hit = *hit;
|
||||||
result.combined = (result.indirect + result.direct + result.reflect);
|
result.combined = (result.indirect + result.direct + result.reflect);
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
} else {
|
} else {
|
||||||
return {};
|
return {};
|
||||||
|
|
Loading…
Add table
Reference in a new issue