1
Fork 0
raytracer/src/main.cpp

353 lines
11 KiB
C++
Raw Normal View History

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;
bool use_bvh = true;
2020-02-17 10:33:56 -05:00
const Camera camera = [] {
Camera camera;
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
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"};
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)) {
glm::vec3 chosen_display;
2022-08-16 07:41:12 -04:00
switch (display_mode) {
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
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")) {
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
auto& plane = scene.load_from_file("misc/plane.obj");
2020-05-29 19:12:37 -04:00
plane.position.y = -1;
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
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"))
display_mode = DisplayMode::Combined;
2022-08-16 07:41:12 -04:00
if (ImGui::Selectable("Direct"))
display_mode = DisplayMode::Direct;
2022-08-16 07:41:12 -04:00
if (ImGui::Selectable("Indirect"))
display_mode = DisplayMode::Indirect;
2022-08-16 07:41:12 -04:00
if (ImGui::Selectable("Reflect"))
display_mode = DisplayMode::Reflect;
2022-08-16 07:41:12 -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();
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;
}