2022-08-16 07:41:12 -04:00
|
|
|
#include <SDL.h>
|
|
|
|
#include <array>
|
|
|
|
#include <future>
|
|
|
|
#include <glm/glm.hpp>
|
2020-02-17 10:33:56 -05:00
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#define STB_IMAGE_WRITE_IMPLEMENTATION
|
|
|
|
#include <stb_image_write.h>
|
|
|
|
|
|
|
|
#include "camera.h"
|
2020-05-13 16:53:59 -04:00
|
|
|
#include "glad/glad.h"
|
2022-08-16 07:41:12 -04:00
|
|
|
#include "image.h"
|
2020-05-13 16:53:59 -04:00
|
|
|
#include "imgui.h"
|
|
|
|
#include "imgui_impl_opengl3.h"
|
2022-08-16 07:41:12 -04:00
|
|
|
#include "imgui_impl_sdl.h"
|
|
|
|
#include "intersections.h"
|
|
|
|
#include "lighting.h"
|
|
|
|
#include "scene.h"
|
2020-02-17 10:33:56 -05:00
|
|
|
|
|
|
|
#define TINYOBJLOADER_IMPLEMENTATION
|
|
|
|
#include <tiny_obj_loader.h>
|
|
|
|
|
|
|
|
// scene information
|
2020-07-30 10:06:47 -04:00
|
|
|
constexpr int32_t width = 256, height = 256;
|
2020-07-31 22:52:16 -04:00
|
|
|
bool use_bvh = true;
|
2020-05-29 21:31:56 -04:00
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
const Camera camera = [] {
|
|
|
|
Camera camera;
|
2022-08-16 07:36:36 -04:00
|
|
|
camera.look_at(glm::vec3(0, 0, 3), glm::vec3(0));
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
return camera;
|
|
|
|
}();
|
|
|
|
|
|
|
|
// internal variables
|
|
|
|
constexpr int32_t tile_size = 32;
|
|
|
|
constexpr int32_t num_tiles_x = width / tile_size;
|
|
|
|
constexpr int32_t num_tiles_y = height / tile_size;
|
|
|
|
|
|
|
|
// globals
|
|
|
|
Scene scene = {};
|
2020-05-13 16:53:59 -04:00
|
|
|
Image<glm::vec4, width, height> colors = {};
|
2020-05-13 17:19:21 -04:00
|
|
|
bool image_dirty = false;
|
2020-02-17 10:33:56 -05:00
|
|
|
|
2020-07-31 22:52:16 -04:00
|
|
|
enum class DisplayMode {
|
|
|
|
Combined,
|
|
|
|
Direct,
|
|
|
|
Indirect,
|
|
|
|
Reflect
|
|
|
|
};
|
|
|
|
|
2022-08-16 07:41:12 -04:00
|
|
|
const std::array diplay_mode_strings = {"Combined", "Direct", "Indirect", "Reflect"};
|
2020-07-31 22:52:16 -04:00
|
|
|
|
|
|
|
inline DisplayMode display_mode;
|
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
bool calculate_tile(const int32_t from_x, const int32_t to_width, const int32_t from_y, const int32_t to_height) {
|
2022-08-16 07:41:12 -04:00
|
|
|
for (int32_t y = from_y; y < (from_y + to_height); y++) {
|
|
|
|
for (int32_t x = from_x; x < (from_x + to_width); x++) {
|
2020-02-17 10:33:56 -05:00
|
|
|
Ray ray_camera = camera.get_ray(x, y, width, height);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (auto result = cast_scene(ray_camera, scene, use_bvh)) {
|
2020-07-31 22:52:16 -04:00
|
|
|
glm::vec3 chosen_display;
|
2022-08-16 07:41:12 -04:00
|
|
|
switch (display_mode) {
|
2020-07-31 22:52:16 -04:00
|
|
|
case DisplayMode::Combined:
|
|
|
|
chosen_display = result->combined;
|
|
|
|
break;
|
|
|
|
case DisplayMode::Direct:
|
|
|
|
chosen_display = result->direct;
|
|
|
|
break;
|
|
|
|
case DisplayMode::Indirect:
|
|
|
|
chosen_display = result->indirect;
|
|
|
|
break;
|
|
|
|
case DisplayMode::Reflect:
|
|
|
|
chosen_display = result->reflect;
|
|
|
|
break;
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-31 22:52:16 -04:00
|
|
|
colors.get(x, y) = glm::vec4(chosen_display, 1.0f);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 17:19:21 -04:00
|
|
|
image_dirty = true;
|
2020-02-17 10:33:56 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
GLuint quad_vao = 0;
|
|
|
|
GLuint pixel_program = 0;
|
|
|
|
GLuint pixels_texture = 0;
|
|
|
|
|
|
|
|
void setup_gfx() {
|
|
|
|
// create quad for pixel rendering
|
|
|
|
constexpr std::array vertices = {
|
2022-08-16 07:41:12 -04:00
|
|
|
-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
|
2020-05-13 16:53:59 -04:00
|
|
|
};
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
constexpr std::array elements = {0, 1, 2, 2, 3, 0};
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glGenVertexArrays(1, &quad_vao);
|
|
|
|
glBindVertexArray(quad_vao);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
GLuint vbo = 0;
|
|
|
|
glGenBuffers(1, &vbo);
|
|
|
|
glBindBuffer(GL_ARRAY_BUFFER, vbo);
|
|
|
|
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), vertices.data(), GL_STATIC_DRAW);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
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)));
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glEnableVertexAttribArray(0);
|
|
|
|
glEnableVertexAttribArray(1);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
GLuint ebo = 0;
|
|
|
|
glGenBuffers(1, &ebo);
|
|
|
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
|
|
|
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER, elements.size() * sizeof(uint32_t), elements.data(), GL_STATIC_DRAW);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
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"
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
|
|
|
" gl_Position = vec4(in_position.xyz, 1.0);\n"
|
|
|
|
" uv = in_uv;\n"
|
|
|
|
"}\n";
|
2020-05-13 16:53:59 -04:00
|
|
|
const char* vertex_src = vertex_glsl.data();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
constexpr std::string_view fragment_glsl = "#version 330 core\n"
|
|
|
|
"in vec2 uv;\n"
|
|
|
|
"out vec4 out_color;\n"
|
|
|
|
"uniform sampler2D pixel_texture;\n"
|
|
|
|
"void main()\n"
|
|
|
|
"{\n"
|
|
|
|
" out_color = texture(pixel_texture, uv);\n"
|
|
|
|
"}\n";
|
2020-05-13 16:53:59 -04:00
|
|
|
const char* fragment_src = fragment_glsl.data();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
GLuint vertex_shader = glCreateShader(GL_VERTEX_SHADER);
|
|
|
|
glShaderSource(vertex_shader, 1, &vertex_src, nullptr);
|
|
|
|
glCompileShader(vertex_shader);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
GLuint fragment_shader = glCreateShader(GL_FRAGMENT_SHADER);
|
|
|
|
glShaderSource(fragment_shader, 1, &fragment_src, nullptr);
|
|
|
|
glCompileShader(fragment_shader);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
pixel_program = glCreateProgram();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glAttachShader(pixel_program, vertex_shader);
|
|
|
|
glAttachShader(pixel_program, fragment_shader);
|
|
|
|
glLinkProgram(pixel_program);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glDeleteShader(vertex_shader);
|
|
|
|
glDeleteShader(fragment_shader);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glGenTextures(1, &pixels_texture);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, nullptr);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void update_texture() {
|
|
|
|
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
2020-05-13 17:19:21 -04:00
|
|
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, colors.array.data());
|
2020-05-13 16:53:59 -04:00
|
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
}
|
|
|
|
|
2020-05-13 17:19:21 -04:00
|
|
|
std::vector<std::future<bool>> futures;
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
void render() {
|
2020-05-13 17:19:21 -04:00
|
|
|
futures.clear();
|
|
|
|
colors.reset();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
for (int32_t y = 0; y < num_tiles_y; y++) {
|
|
|
|
for (int32_t x = 0; x < num_tiles_x; x++) {
|
2020-05-13 17:19:21 -04:00
|
|
|
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));
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 16:53:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void dump_to_file() {
|
2020-02-17 10:33:56 -05:00
|
|
|
uint8_t pixels[width * height * 3] = {};
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
int i = 0;
|
2022-08-16 07:41:12 -04:00
|
|
|
for (int32_t y = height - 1; y >= 0; y--) {
|
|
|
|
for (int32_t x = 0; x < width; x++) {
|
2020-02-17 10:33:56 -05:00
|
|
|
const glm::ivec4 c = colors.get(x, y);
|
|
|
|
pixels[i++] = c.r;
|
|
|
|
pixels[i++] = c.g;
|
|
|
|
pixels[i++] = c.b;
|
|
|
|
}
|
|
|
|
}
|
2020-05-13 16:53:59 -04:00
|
|
|
|
|
|
|
stbi_write_png("output.png", width, height, 3, pixels, width * 3);
|
|
|
|
}
|
|
|
|
|
2022-08-16 07:41:12 -04:00
|
|
|
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)) {
|
2020-07-30 10:06:47 -04:00
|
|
|
ImGui::Text("Is split: %i", node.is_split);
|
|
|
|
ImGui::Text("Contained triangles: %lu", node.contained_objects.size());
|
|
|
|
|
2022-08-16 07:41:12 -04:00
|
|
|
if (node.is_split) {
|
|
|
|
for (auto& child : node.children)
|
2020-07-30 10:06:47 -04:00
|
|
|
walk_node(*child);
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-30 10:06:47 -04:00
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void walk_object(Object& object) {
|
|
|
|
walk_node(object.octree->root);
|
|
|
|
}
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
int main(int, char*[]) {
|
|
|
|
SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_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_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
SDL_Window* window = SDL_CreateWindow(
|
|
|
|
"raytracer",
|
|
|
|
SDL_WINDOWPOS_CENTERED,
|
|
|
|
SDL_WINDOWPOS_CENTERED,
|
|
|
|
800,
|
|
|
|
600,
|
|
|
|
SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE);
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
SDL_GLContext gl_context = SDL_GL_CreateContext(window);
|
|
|
|
SDL_GL_MakeCurrent(window, gl_context);
|
|
|
|
SDL_GL_SetSwapInterval(1);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
gladLoadGL();
|
|
|
|
setup_gfx();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui::CreateContext();
|
|
|
|
ImGui::StyleColorsDark();
|
|
|
|
|
|
|
|
ImGui_ImplSDL2_InitForOpenGL(window, gl_context);
|
|
|
|
ImGui_ImplOpenGL3_Init("#version 330 core");
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
bool running = true;
|
2022-08-16 07:41:12 -04:00
|
|
|
while (running) {
|
2020-05-13 16:53:59 -04:00
|
|
|
SDL_Event event = {};
|
2022-08-16 07:41:12 -04:00
|
|
|
while (SDL_PollEvent(&event)) {
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui_ImplSDL2_ProcessEvent(&event);
|
|
|
|
|
2022-08-16 07:41:12 -04:00
|
|
|
if (event.type == SDL_QUIT)
|
2020-05-13 16:53:59 -04:00
|
|
|
running = false;
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui_ImplOpenGL3_NewFrame();
|
|
|
|
ImGui_ImplSDL2_NewFrame(window);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui::NewFrame();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::BeginMainMenuBar()) {
|
|
|
|
if (ImGui::BeginMenu("File")) {
|
|
|
|
if (ImGui::Button("Load Example Models")) {
|
2022-08-16 07:33:10 -04:00
|
|
|
auto& sphere = scene.load_from_file("misc/sphere.obj");
|
2022-08-16 07:38:52 -04:00
|
|
|
sphere.color = {1, 1, 1};
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2022-08-16 07:33:10 -04:00
|
|
|
auto& plane = scene.load_from_file("misc/plane.obj");
|
2020-05-29 19:12:37 -04:00
|
|
|
plane.position.y = -1;
|
2020-05-29 21:31:56 -04:00
|
|
|
plane.color = {1, 0, 0};
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-30 10:06:47 -04:00
|
|
|
scene.generate_acceleration();
|
2020-05-29 19:12:37 -04:00
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui::EndMenu();
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui::EndMainMenuBar();
|
|
|
|
}
|
2022-08-16 07:24:49 -04:00
|
|
|
|
|
|
|
ImGui::Begin("Render Options");
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-31 22:52:16 -04:00
|
|
|
ImGui::Checkbox("Use BVH", &use_bvh);
|
|
|
|
ImGui::InputInt("Indirect Samples", &num_indirect_samples);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::BeginCombo("Channel Selection", diplay_mode_strings[static_cast<int>(display_mode)])) {
|
|
|
|
if (ImGui::Selectable("Combined"))
|
2020-07-31 22:52:16 -04:00
|
|
|
display_mode = DisplayMode::Combined;
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::Selectable("Direct"))
|
2020-07-31 22:52:16 -04:00
|
|
|
display_mode = DisplayMode::Direct;
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::Selectable("Indirect"))
|
2020-07-31 22:52:16 -04:00
|
|
|
display_mode = DisplayMode::Indirect;
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::Selectable("Reflect"))
|
2020-07-31 22:52:16 -04:00
|
|
|
display_mode = DisplayMode::Reflect;
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-31 22:52:16 -04:00
|
|
|
ImGui::EndCombo();
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::Button("Render"))
|
2020-05-13 16:53:59 -04:00
|
|
|
render();
|
2022-08-16 07:26:11 -04:00
|
|
|
|
|
|
|
ImGui::SameLine();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
if (ImGui::Button("Save Output"))
|
2020-05-13 16:53:59 -04:00
|
|
|
dump_to_file();
|
2022-08-16 07:24:49 -04:00
|
|
|
|
2022-08-16 07:41:12 -04:00
|
|
|
if (image_dirty) {
|
2020-05-13 17:19:21 -04:00
|
|
|
update_texture();
|
|
|
|
image_dirty = false;
|
|
|
|
}
|
2022-08-16 07:41:12 -04:00
|
|
|
|
|
|
|
for (auto& object : scene.objects) {
|
|
|
|
if (ImGui::TreeNode("Object")) {
|
2020-07-30 10:06:47 -04:00
|
|
|
walk_object(*object);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-07-30 10:06:47 -04:00
|
|
|
ImGui::TreePop();
|
|
|
|
}
|
|
|
|
}
|
2022-08-16 07:24:49 -04:00
|
|
|
|
|
|
|
ImGui::End();
|
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
ImGui::Render();
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
auto& io = ImGui::GetIO();
|
|
|
|
glViewport(0, 0, (int)io.DisplaySize.x, (int)io.DisplaySize.y);
|
|
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
glUseProgram(pixel_program);
|
|
|
|
glBindVertexArray(quad_vao);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, pixels_texture);
|
|
|
|
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
|
|
|
|
|
|
|
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
2022-08-16 07:41:12 -04:00
|
|
|
|
2020-05-13 16:53:59 -04:00
|
|
|
SDL_GL_SwapWindow(window);
|
|
|
|
}
|
|
|
|
|
2020-02-17 10:33:56 -05:00
|
|
|
return 0;
|
|
|
|
}
|