├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── Readme.md ├── conanfile.txt ├── data └── textures │ ├── TC_AboveClouds_Xn.png │ ├── TC_AboveClouds_Xp.png │ ├── TC_AboveClouds_Yn.png │ ├── TC_AboveClouds_Yp.png │ ├── TC_AboveClouds_Zn.png │ ├── TC_AboveClouds_Zp.png │ ├── TC_SkyRed_Xn.png │ ├── TC_SkyRed_Xp.png │ ├── TC_SkyRed_Yn.png │ ├── TC_SkyRed_Yp.png │ ├── TC_SkyRed_Zn.png │ ├── TC_SkyRed_Zp.png │ ├── TC_SkySpace_Xn.png │ ├── TC_SkySpace_Xp.png │ ├── TC_SkySpace_Yn.png │ ├── TC_SkySpace_Yp.png │ ├── TC_SkySpace_Zn.png │ ├── TC_SkySpace_Zp.png │ ├── T_Default_D.png │ ├── T_Default_E.png │ ├── T_Default_N.png │ └── T_Default_S.png ├── doc └── screenshot1.jpg └── source ├── Framebuffer.cpp ├── Framebuffer.h ├── GLDebug.cpp ├── GLDebug.h ├── Main.cpp ├── Model.cpp ├── Model.h ├── Shader.cpp ├── Shader.h ├── Ssao.cpp ├── Ssao.h ├── Texture.cpp ├── Texture.h ├── Util.cpp ├── Util.h └── shader ├── blur.frag.glsl ├── blur.vert.glsl ├── gbuffer.frag.glsl ├── gbuffer.vert.glsl ├── main.frag.glsl └── main.vert.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | CMakeUserPresets.json 3 | .vs/ -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.25.0) 2 | project(DeferredRender) 3 | 4 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME}) 5 | set_property(GLOBAL PROPERTY USE_FOLDERS ON) 6 | set(CXX_STANDARD_REQUIRED ON) 7 | 8 | if (MSVC) 9 | # Ignore warnings about missing pdb 10 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /ignore:4099") 11 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /ignore:4099") 12 | set(CMAKE_STATIC_LINKER_FLAGS "${CMAKE_STATIC_LINKER_FLAGS} /ignore:4099") 13 | endif() 14 | 15 | find_package(OpenGL REQUIRED) 16 | find_package(glad REQUIRED) 17 | find_package(stb REQUIRED) 18 | find_package(glm REQUIRED) 19 | find_package(fmtlog REQUIRED) 20 | find_package(SDL2 REQUIRED) 21 | 22 | # App 23 | file(GLOB_RECURSE SOURCE_FILES CONFIGURE_DEPENDS 24 | ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp 25 | ${CMAKE_CURRENT_SOURCE_DIR}/source/*.cxx 26 | ${CMAKE_CURRENT_SOURCE_DIR}/source/*.h 27 | ${CMAKE_CURRENT_SOURCE_DIR}/source/*.hpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/source/*.glsl 29 | ) 30 | 31 | source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SOURCE_FILES}) 32 | 33 | add_executable(${PROJECT_NAME} ${SOURCE_FILES}) 34 | 35 | set_target_properties(${PROJECT_NAME} PROPERTIES 36 | CXX_STANDARD 17 37 | ) 38 | 39 | target_link_libraries(${PROJECT_NAME} 40 | PUBLIC glad::glad 41 | PUBLIC OpenGL::GL 42 | PUBLIC stb::stb 43 | PUBLIC fmtlog::fmtlog 44 | PUBLIC glm::glm 45 | PUBLIC SDL2::SDL2 46 | ) 47 | 48 | target_include_directories(${PROJECT_NAME} 49 | PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/source 50 | PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/include 51 | ) 52 | 53 | target_compile_options(${PROJECT_NAME} PRIVATE 54 | $<$:/WX /W2> 55 | # $<$>:-Wall -Wextra -Wpedantic -Werror> 56 | ) 57 | 58 | file (COPY ${CMAKE_CURRENT_SOURCE_DIR}/data DESTINATION ${CMAKE_CURRENT_BINARY_DIR}) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 fennec-kun 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # Modern OpenGL - Deferred Rending 2 | 3 | ![Screenshot 1](./doc/screenshot1.jpg) 4 | 5 | Modern OpenGL deferred rendering example 6 | 7 | This little project shall contain the tools to produce the following techniques and effects: 8 | * deferred rendering 9 | * deferred lighting 10 | * shadows 11 | * bloom 12 | 13 | ## Requirements 14 | 15 | Development is done with 16 | * Conan 2.x 17 | * CMake 3.27.x 18 | * Visual Studio 2022 Community Edition 19 | 20 | ## Building 21 | 22 | Conan install for debug and release build types 23 | ``` 24 | conan install . --build=missing --settings=build_type=Debug && conan install . --build=missing --settings=build_type=Release 25 | ``` 26 | 27 | CMake project generation 28 | ``` 29 | cmake --preset conan-default 30 | ``` 31 | 32 | For Windows development, open the solution file (.sln) located in the newly created build directory. 33 | For Linux development, note that the build directory now should contain a Debug and Release directory which can be build via build command separately. 34 | 35 | ## Troubleshooting 36 | 37 | In case of issues try (in no particular order) 38 | * Removing the build directory and the CMakeUserPresets.json 39 | * Cleaning the repository via ```git clean -xfd``` 40 | * Deleting and cloning the whole repository again 41 | * Ensure your cmake version is at least 3.27.0 42 | * Ensure your conan profile is generated in case of a new conan installation 43 | * Ensure your conan is at version 2.x 44 | * Ensure your conan profile is set to C++ standard 17 or higher -------------------------------------------------------------------------------- /conanfile.txt: -------------------------------------------------------------------------------- 1 | [requires] 2 | 3 | glad/0.1.36 4 | stb/cci.20220909 5 | glm/cci.20230113 6 | fmtlog/2.2.1 7 | sdl/2.28.2 8 | 9 | [options] 10 | glad/*:gl_profile=compatibility 11 | glad/*:gl_version=4.6 12 | glad/*:spec=gl 13 | glad/*:no_loader=False 14 | 15 | [generators] 16 | CMakeDeps 17 | CMakeToolchain 18 | 19 | [layout] 20 | cmake_layout -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Xn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Xn.png -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Xp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Xp.png -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Yn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Yn.png -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Yp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Yp.png -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Zn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Zn.png -------------------------------------------------------------------------------- /data/textures/TC_AboveClouds_Zp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_AboveClouds_Zp.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Xn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Xn.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Xp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Xp.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Yn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Yn.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Yp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Yp.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Zn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Zn.png -------------------------------------------------------------------------------- /data/textures/TC_SkyRed_Zp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkyRed_Zp.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Xn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Xn.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Xp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Xp.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Yn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Yn.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Yp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Yp.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Zn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Zn.png -------------------------------------------------------------------------------- /data/textures/TC_SkySpace_Zp.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/TC_SkySpace_Zp.png -------------------------------------------------------------------------------- /data/textures/T_Default_D.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/T_Default_D.png -------------------------------------------------------------------------------- /data/textures/T_Default_E.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/T_Default_E.png -------------------------------------------------------------------------------- /data/textures/T_Default_N.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/T_Default_N.png -------------------------------------------------------------------------------- /data/textures/T_Default_S.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/data/textures/T_Default_S.png -------------------------------------------------------------------------------- /doc/screenshot1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fendevel/ModernOpenGL/8a021908ddf6148335436955d4fc74100622e3a3/doc/screenshot1.jpg -------------------------------------------------------------------------------- /source/Framebuffer.cpp: -------------------------------------------------------------------------------- 1 | #include "Framebuffer.h" 2 | 3 | #include 4 | #include 5 | 6 | GLuint create_framebuffer(std::vector const& cols, GLuint depth) 7 | { 8 | GLuint fbo = 0; 9 | glCreateFramebuffers(1, &fbo); 10 | 11 | for (auto i = 0; i < cols.size(); i++) 12 | { 13 | glNamedFramebufferTexture(fbo, GL_COLOR_ATTACHMENT0 + i, cols[i], 0); 14 | } 15 | 16 | std::array draw_buffs; 17 | for (GLenum i = 0; i < cols.size(); i++) 18 | { 19 | draw_buffs[i] = GL_COLOR_ATTACHMENT0 + i; 20 | } 21 | 22 | glNamedFramebufferDrawBuffers(fbo, cols.size(), draw_buffs.data()); 23 | 24 | if (depth != GL_NONE) 25 | { 26 | glNamedFramebufferTexture(fbo, GL_DEPTH_ATTACHMENT, depth, 0); 27 | } 28 | 29 | if (glCheckNamedFramebufferStatus(fbo, GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) 30 | { 31 | throw std::runtime_error("incomplete framebuffer"); 32 | } 33 | return fbo; 34 | } 35 | -------------------------------------------------------------------------------- /source/Framebuffer.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | GLuint create_framebuffer(std::vector const& cols, GLuint depth = GL_NONE); -------------------------------------------------------------------------------- /source/GLDebug.cpp: -------------------------------------------------------------------------------- 1 | #include "GLDebug.h" 2 | 3 | #include 4 | #include 5 | 6 | void APIENTRY gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam) 7 | { 8 | std::ostringstream str; 9 | str << "message: " << message << '\n'; 10 | str << "type: "; 11 | switch (type) 12 | { 13 | case GL_DEBUG_TYPE_ERROR: str << "ERROR"; break; 14 | case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: str << "DEPRECATED_BEHAVIOR"; break; 15 | case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: str << "UNDEFINED_BEHAVIOR"; break; 16 | case GL_DEBUG_TYPE_PORTABILITY: str << "PORTABILITY"; break; 17 | case GL_DEBUG_TYPE_PERFORMANCE: str << "PERFORMANCE"; break; 18 | case GL_DEBUG_TYPE_OTHER: str << "OTHER"; break; 19 | } 20 | 21 | str << '\n'; 22 | str << "id: " << id << '\n'; 23 | str << "severity: "; 24 | switch (severity) 25 | { 26 | case GL_DEBUG_SEVERITY_LOW: str << "LOW"; break; 27 | case GL_DEBUG_SEVERITY_MEDIUM: str << "MEDIUM"; break; 28 | case GL_DEBUG_SEVERITY_HIGH: str << "HIGH"; break; 29 | default: str << "NONE"; 30 | } 31 | 32 | std::clog << str.str() << std::endl; 33 | } -------------------------------------------------------------------------------- /source/GLDebug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void APIENTRY gl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* message, const void* userParam); -------------------------------------------------------------------------------- /source/Main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #define SDL_MAIN_HANDLED 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "Framebuffer.h" 14 | #include "Shader.h" 15 | #include "Model.h" 16 | #include "Texture.h" 17 | #include "Util.h" 18 | #include "GLDebug.h" 19 | 20 | // Nvidia optimus 21 | #ifdef _MSC_VER 22 | extern "C" { _declspec(dllexport) unsigned int NvOptimusEnablement = 0x00000001; } 23 | #endif 24 | 25 | using glDeleterFunc = void (APIENTRYP)(GLuint item); 26 | using glDeleterFuncv = void (APIENTRYP)(GLsizei n, const GLuint* items); 27 | inline void delete_items(glDeleterFuncv deleter, std::initializer_list items) { deleter(items.size(), items.begin()); } 28 | inline void delete_items(glDeleterFunc deleter, std::initializer_list items) 29 | { 30 | for (size_t i = 0; i < items.size(); i++) 31 | { 32 | deleter(*(items.begin() + i)); 33 | } 34 | } 35 | 36 | void measure_frames(SDL_Window* const window, double& deltaTimeAverage, int& frameCounter, int framesToAverage) 37 | { 38 | if (frameCounter == framesToAverage) 39 | { 40 | deltaTimeAverage /= framesToAverage; 41 | 42 | auto window_title = string_format("frametime = %.3fms, fps = %.1f", 1000.0 * deltaTimeAverage, 1.0 / deltaTimeAverage); 43 | SDL_SetWindowTitle(window, window_title.c_str()); 44 | 45 | deltaTimeAverage = 0.0; 46 | frameCounter = 0; 47 | } 48 | } 49 | 50 | enum class shape_t 51 | { 52 | cube = 0, 53 | quad = 1 54 | }; 55 | 56 | struct scene_object_t 57 | { 58 | glm::mat4 model = glm::mat4(0.f); 59 | glm::mat4 mvp_inv_prev = glm::mat4(0.f); 60 | shape_t shape = shape_t::cube; 61 | // Excempt this object from being motion blurred 62 | bool except = false; 63 | float emissive_strength = 1.f; 64 | scene_object_t(shape_t shape, bool except = false, float emissive_strength = 1.f) 65 | : model(), mvp_inv_prev(), shape(shape), except(except), emissive_strength(emissive_strength) 66 | {} 67 | }; 68 | 69 | void GuardedMain() 70 | { 71 | constexpr auto window_width = 1920; 72 | constexpr auto window_height = 1080; 73 | SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); 74 | const auto window = SDL_CreateWindow("ModernOpenGL\0", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, window_width, window_height, SDL_WINDOW_OPENGL); 75 | const auto gl_context = SDL_GL_CreateContext(window); 76 | SDL_GL_SetSwapInterval(1); 77 | auto ev = SDL_Event(); 78 | 79 | auto key_count = 0; 80 | const auto key_state = SDL_GetKeyboardState(&key_count); 81 | 82 | std::array key; 83 | std::array key_pressed; 84 | std::array key_released; 85 | 86 | auto const [screen_width, screen_height] = []() 87 | { 88 | SDL_DisplayMode display_mode; 89 | SDL_GetCurrentDisplayMode(0, &display_mode); 90 | return std::pair(display_mode.w, display_mode.h); 91 | }(); 92 | 93 | if (!gladLoadGL()) 94 | { 95 | SDL_GL_DeleteContext(gl_context); 96 | SDL_DestroyWindow(window); 97 | throw std::runtime_error("failed to load gl"); 98 | } 99 | 100 | std::clog << glGetString(GL_VERSION) << '\n'; 101 | 102 | if (glDebugMessageCallback) 103 | { 104 | std::clog << "registered opengl debug callback\n"; 105 | glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); 106 | glDebugMessageCallback(gl_debug_callback, nullptr); 107 | GLuint unusedIds = 0; 108 | glDebugMessageControl(GL_DONT_CARE, GL_DONT_CARE, GL_DONT_CARE, 0, &unusedIds, true); 109 | } 110 | else 111 | { 112 | std::clog << "glDebugMessageCallback not available\n"; 113 | } 114 | 115 | glEnable(GL_CULL_FACE); 116 | glEnable(GL_DEPTH_TEST); 117 | glEnable(GL_PROGRAM_POINT_SIZE); 118 | 119 | std::vector const vertices_cube = 120 | { 121 | vertex_t(glm::vec3(-0.5f, 0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f,-1.0f), glm::vec2(0.0f, 0.0f)), 122 | vertex_t(glm::vec3(0.5f, 0.5f,-0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f,-1.0f), glm::vec2(1.0f, 0.0f)), 123 | vertex_t(glm::vec3(0.5f,-0.5f,-0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f,-1.0f), glm::vec2(1.0f, 1.0f)), 124 | vertex_t(glm::vec3(-0.5f,-0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f,-1.0f), glm::vec2(0.0f, 1.0f)), 125 | 126 | vertex_t(glm::vec3(0.5f, 0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 0.0f)), 127 | vertex_t(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), 128 | vertex_t(glm::vec3(0.5f,-0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 1.0f)), 129 | vertex_t(glm::vec3(0.5f,-0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), 130 | 131 | vertex_t(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(1.0f, 0.0f)), 132 | vertex_t(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(0.0f, 0.0f)), 133 | vertex_t(glm::vec3(-0.5f,-0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(0.0f, 1.0f)), 134 | vertex_t(glm::vec3(0.5f,-0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec2(1.0f, 1.0f)), 135 | 136 | vertex_t(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 0.0f)), 137 | vertex_t(glm::vec3(-0.5f, 0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 0.0f)), 138 | vertex_t(glm::vec3(-0.5f,-0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(0.0f, 1.0f)), 139 | vertex_t(glm::vec3(-0.5f,-0.5f, 0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec2(1.0f, 1.0f)), 140 | 141 | vertex_t(glm::vec3(-0.5f, 0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 0.0f)), 142 | vertex_t(glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), 143 | vertex_t(glm::vec3(0.5f, 0.5f,-0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 1.0f)), 144 | vertex_t(glm::vec3(-0.5f, 0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), 145 | 146 | vertex_t(glm::vec3(0.5f,-0.5f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f,-1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), 147 | vertex_t(glm::vec3(-0.5f,-0.5f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f,-1.0f, 0.0f), glm::vec2(0.0f, 0.0f)), 148 | vertex_t(glm::vec3(-0.5f,-0.5f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(0.0f,-1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), 149 | vertex_t(glm::vec3(0.5f,-0.5f,-0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f,-1.0f, 0.0f), glm::vec2(1.0f, 1.0f)), 150 | }; 151 | 152 | std::vector const vertices_quad = 153 | { 154 | vertex_t(glm::vec3(-0.5f, 0.0f, 0.5f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 0.0f)), 155 | vertex_t(glm::vec3(0.5f, 0.0f, 0.5f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 0.0f)), 156 | vertex_t(glm::vec3(0.5f, 0.0f,-0.5f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(1.0f, 1.0f)), 157 | vertex_t(glm::vec3(-0.5f, 0.0f,-0.5f), glm::vec3(1.0f, 0.0f, 1.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec2(0.0f, 1.0f)), 158 | }; 159 | 160 | std::vector const indices_cube = 161 | { 162 | 0, 1, 2, 2, 3, 0, 163 | 4, 5, 6, 6, 7, 4, 164 | 8, 9, 10, 10, 11, 8, 165 | 166 | 12, 13, 14, 14, 15, 12, 167 | 16, 17, 18, 18, 19, 16, 168 | 20, 21, 22, 22, 23, 20, 169 | }; 170 | 171 | std::vector const indices_quad = 172 | { 173 | 0, 1, 2, 2, 3, 0, 174 | }; 175 | 176 | auto const texture_cube_diffuse = create_texture_2d_from_file("data/textures/T_Default_D.png", STBI_rgb, true); 177 | auto const texture_cube_specular = create_texture_2d_from_file("data/textures/T_Default_S.png", STBI_grey, true); 178 | auto const texture_cube_normal = create_texture_2d_from_file("data/textures/T_Default_N.png", STBI_rgb, true); 179 | auto const texture_cube_emissive = create_texture_2d_from_file("data/textures/T_Default_E.png", STBI_rgb, true); 180 | auto const texture_skybox = create_texture_cube_from_file({ 181 | "data/textures/TC_SkySpace_Xn.png", 182 | "data/textures/TC_SkySpace_Xp.png", 183 | "data/textures/TC_SkySpace_Yn.png", 184 | "data/textures/TC_SkySpace_Yp.png", 185 | "data/textures/TC_SkySpace_Zn.png", 186 | "data/textures/TC_SkySpace_Zp.png" 187 | }); 188 | 189 | /* framebuffer textures */ 190 | auto const texture_gbuffer_color = create_texture_2d(GL_RGB8, GL_RGB, screen_width, screen_height, nullptr, GL_NEAREST); 191 | 192 | auto const texture_gbuffer_position = create_texture_2d(GL_RGB16F, GL_RGB, screen_width, screen_height, nullptr, GL_NEAREST); 193 | auto const texture_gbuffer_normal = create_texture_2d(GL_RGB16F, GL_RGB, screen_width, screen_height, nullptr, GL_NEAREST); 194 | auto const texture_gbuffer_albedo = create_texture_2d(GL_RGBA16F, GL_RGBA, screen_width, screen_height, nullptr, GL_NEAREST); 195 | auto const texture_gbuffer_depth = create_texture_2d(GL_DEPTH_COMPONENT32, GL_DEPTH, screen_width, screen_height, nullptr, GL_NEAREST); 196 | auto const texture_gbuffer_velocity = create_texture_2d(GL_RG16F, GL_RG, screen_width, screen_height, nullptr, GL_NEAREST); 197 | auto const texture_gbuffer_emissive = create_texture_2d(GL_RGB8, GL_RGB, screen_width, screen_height, nullptr, GL_NEAREST); 198 | 199 | auto const texture_motion_blur = create_texture_2d(GL_RGB8, GL_RGB, screen_width, screen_height, nullptr, GL_NEAREST); 200 | auto const texture_motion_blur_mask = create_texture_2d(GL_R8, GL_RED, screen_width, screen_height, nullptr, GL_NEAREST); 201 | 202 | auto const fb_gbuffer = create_framebuffer({ texture_gbuffer_position, texture_gbuffer_normal, texture_gbuffer_albedo, texture_gbuffer_velocity, texture_gbuffer_emissive }, texture_gbuffer_depth); 203 | auto const fb_finalcolor = create_framebuffer({ texture_gbuffer_color }); 204 | auto const fb_blur = create_framebuffer({ texture_motion_blur }); 205 | 206 | /* vertex formatting information */ 207 | std::vector const vertex_format = 208 | { 209 | create_attrib_format(0, offsetof(vertex_t, position)), 210 | create_attrib_format(1, offsetof(vertex_t, color)), 211 | create_attrib_format(2, offsetof(vertex_t, normal)), 212 | create_attrib_format(3, offsetof(vertex_t, texcoord)) 213 | }; 214 | 215 | /* geometry buffers */ 216 | auto const vao_empty = [] { GLuint name = 0; glCreateVertexArrays(1, &name); return name; }(); 217 | auto const [vao_cube, vbo_cube, ibo_cube] = create_geometry(vertices_cube, indices_cube, vertex_format); 218 | auto const [vao_quad, vbo_quad, ibo_quad] = create_geometry(vertices_quad, indices_quad, vertex_format); 219 | 220 | /* shaders */ 221 | auto const [pr_main, vert_shader, frag_shader] = create_program_from_sources(mainVertSource, mainFragSource); 222 | auto const [pr_gbuffer, vert_shader_g, frag_shader_g] = create_program_from_sources(gbufferVertSource, gbufferFragSource); 223 | auto const [pr_blur, vert_shader_blur, frag_shader_blur] = create_program_from_sources(blurVertSource, blurFragSource); 224 | 225 | /* uniforms */ 226 | constexpr auto uniform_projection = 0; 227 | constexpr auto uniform_cam_pos = 0; 228 | constexpr auto uniform_light_col = 1; 229 | constexpr auto uniform_light_pos = 2; 230 | constexpr auto uniform_cam_dir = 0; 231 | constexpr auto uniform_view = 1; 232 | constexpr auto uniform_fov = 1; 233 | constexpr auto uniform_aspect = 2; 234 | constexpr auto uniform_modl = 2; 235 | constexpr auto uniform_lght = 3; 236 | constexpr auto uniform_blur_bias = 0; 237 | constexpr auto uniform_uvs_diff = 3; 238 | constexpr auto uniform_mvp = 3; 239 | constexpr auto uniform_mvp_inverse = 4; 240 | constexpr auto uniform_blur_except = 5; 241 | constexpr auto uniform_emissive_strength = 0; 242 | 243 | constexpr auto fov = glm::radians(60.0f); 244 | auto const camera_projection = glm::perspective(fov, float(window_width) / float(window_height), 0.1f, 1000.0f); 245 | set_uniform(vert_shader_g, uniform_projection, camera_projection); 246 | 247 | auto t1 = SDL_GetTicks() / 1000.0; 248 | 249 | const auto framesToAverage = 10; 250 | auto deltaTimeAverage = 0.0; // first moment 251 | auto frameCounter = 0; 252 | 253 | std::vector objects = { 254 | scene_object_t(shape_t::cube), 255 | scene_object_t(shape_t::cube), 256 | scene_object_t(shape_t::cube), 257 | scene_object_t(shape_t::cube), 258 | scene_object_t(shape_t::cube), 259 | scene_object_t(shape_t::quad, false, 0.f) 260 | }; 261 | 262 | double totalTime = 0.0; 263 | auto curr_time = now(); 264 | auto frames = int64_t(0); 265 | while (ev.type != SDL_QUIT) 266 | { 267 | const auto t2 = SDL_GetTicks() / 1000.0; 268 | const auto dt = t2 - t1; 269 | t1 = t2; 270 | 271 | deltaTimeAverage += dt; 272 | totalTime += dt; 273 | frameCounter++; 274 | 275 | measure_frames(window, deltaTimeAverage, frameCounter, framesToAverage); 276 | 277 | if (SDL_PollEvent(&ev)) 278 | { 279 | for (int i = 0; i < key_count; i++) 280 | { 281 | key_pressed[i] = !key[i] && key_state[i]; 282 | key_released[i] = key[i] && !key_state[i]; 283 | key[i] = bool(key_state[i]); 284 | } 285 | } 286 | static auto rot_x = 0.0f; 287 | static auto rot_y = 0.0f; 288 | static glm::vec3 light_pos = glm::vec3(0.0, 4.0, 0.0); 289 | static glm::vec3 camera_position = glm::vec3(0.0f, 0.0f, -7.0f); 290 | static glm::quat camera_orientation = glm::vec3(0.0f, 0.0f, 0.0f); 291 | auto const camera_forward = camera_orientation * glm::vec3(0.0f, 0.0f, 1.0f); 292 | auto const camera_up = camera_orientation * glm::vec3(0.0f, 1.0f, 0.0f); 293 | auto const camera_right = camera_orientation * glm::vec3(1.0f, 0.0f, 0.0f); 294 | 295 | /* user input */ 296 | if (key[SDL_SCANCODE_ESCAPE]) 297 | ev.type = SDL_QUIT; 298 | 299 | if (key[SDL_SCANCODE_LEFT]) rot_y += 0.025f; 300 | if (key[SDL_SCANCODE_RIGHT]) rot_y -= 0.025f; 301 | if (key[SDL_SCANCODE_UP]) rot_x -= 0.025f; 302 | if (key[SDL_SCANCODE_DOWN]) rot_x += 0.025f; 303 | 304 | camera_orientation = glm::quat(glm::vec3(rot_x, rot_y, 0.0f)); 305 | 306 | if (key[SDL_SCANCODE_W]) camera_position += camera_forward * 0.1f; 307 | if (key[SDL_SCANCODE_A]) camera_position += camera_right * 0.1f; 308 | if (key[SDL_SCANCODE_S]) camera_position -= camera_forward * 0.1f; 309 | if (key[SDL_SCANCODE_D]) camera_position -= camera_right * 0.1f; 310 | 311 | static float cube_speed = 0.1f; 312 | if (key[SDL_SCANCODE_Q]) cube_speed -= 0.001f; 313 | if (key[SDL_SCANCODE_E]) cube_speed += 0.001f; 314 | 315 | auto const camera_view = glm::lookAt(camera_position, camera_position + camera_forward, camera_up); 316 | 317 | /* light movement */ 318 | light_pos.y = 4.0 + std::sin(totalTime) * 3.0; 319 | 320 | /* cube orbit */ 321 | auto const orbit_center = glm::vec3(0.0f, 0.0f, 0.0f); 322 | static auto orbit_progression = 0.0f; 323 | 324 | /* rotate inner cube */ 325 | objects[0].model = glm::translate(orbit_center) * glm::rotate(orbit_progression * cube_speed, glm::vec3(0.0f, 1.0f, 0.0f)); 326 | 327 | for (auto i = 0; i < 4; i++) 328 | { 329 | auto const orbit_amount = (orbit_progression * cube_speed + float(i) * 90.0f * glm::pi() / 180.0f); 330 | auto const orbit_pos = orbit_axis(orbit_amount, glm::vec3(-1.0f, -1.0f, 0.0f), glm::vec3(0.0f, 2.0f, 0.0f)) + glm::vec3(-2.0f, 0.0f, 0.0f); 331 | objects[1 + i].model = glm::translate(orbit_center + orbit_pos) * glm::rotate(orbit_amount, glm::vec3(0.0f, -1.0f, 0.0f)); 332 | } 333 | orbit_progression += 0.01f; 334 | 335 | objects[5].model = glm::translate(glm::vec3(0.0f, -3.0f, 0.0f)) * glm::scale(glm::vec3(10.0f, 1.0f, 10.0f)); 336 | 337 | set_uniform(vert_shader_g, uniform_view, camera_view); 338 | 339 | /* g-buffer pass */ 340 | static auto const viewport_width = screen_width; 341 | static auto const viewport_height = screen_height; 342 | glViewport(0, 0, viewport_width, viewport_height); 343 | 344 | auto const depth_clear_val = 1.0f; 345 | glClearNamedFramebufferfv(fb_gbuffer, GL_COLOR, 0, glm::value_ptr(glm::vec3(0.0f))); 346 | glClearNamedFramebufferfv(fb_gbuffer, GL_COLOR, 1, glm::value_ptr(glm::vec3(0.0f))); 347 | glClearNamedFramebufferfv(fb_gbuffer, GL_COLOR, 2, glm::value_ptr(glm::vec4(0.0f))); 348 | glClearNamedFramebufferfv(fb_gbuffer, GL_COLOR, 3, glm::value_ptr(glm::vec2(0.0f))); 349 | glClearNamedFramebufferfv(fb_gbuffer, GL_COLOR, 4, glm::value_ptr(glm::vec2(0.0f))); 350 | glClearNamedFramebufferfv(fb_gbuffer, GL_DEPTH, 0, &depth_clear_val); 351 | 352 | glBindFramebuffer(GL_FRAMEBUFFER, fb_gbuffer); 353 | 354 | glBindTextureUnit(0, texture_cube_diffuse); 355 | glBindTextureUnit(1, texture_cube_specular); 356 | glBindTextureUnit(2, texture_cube_normal); 357 | glBindTextureUnit(3, texture_cube_emissive); 358 | 359 | glBindProgramPipeline(pr_gbuffer); 360 | 361 | for (auto& object : objects) 362 | { 363 | switch (object.shape) 364 | { 365 | case shape_t::cube: glBindVertexArray(vao_cube); break; 366 | case shape_t::quad: glBindVertexArray(vao_quad); break; 367 | } 368 | 369 | auto const curr_mvp_inv = camera_projection * camera_view * object.model; 370 | 371 | set_uniform(vert_shader_g, uniform_modl, object.model); 372 | set_uniform(vert_shader_g, uniform_mvp, curr_mvp_inv); 373 | set_uniform(vert_shader_g, uniform_mvp_inverse, object.mvp_inv_prev); 374 | set_uniform(vert_shader_g, uniform_blur_except, object.except); 375 | set_uniform(frag_shader_g, uniform_emissive_strength, object.emissive_strength); 376 | 377 | object.mvp_inv_prev = curr_mvp_inv; 378 | 379 | for (auto const& object : objects) 380 | { 381 | switch (object.shape) 382 | { 383 | case shape_t::cube: glDrawElements(GL_TRIANGLES, indices_cube.size(), GL_UNSIGNED_BYTE, nullptr); break; 384 | case shape_t::quad: glDrawElements(GL_TRIANGLES, indices_quad.size(), GL_UNSIGNED_BYTE, nullptr); break; 385 | } 386 | } 387 | } 388 | 389 | /* actual shading pass */ 390 | glClearNamedFramebufferfv(fb_finalcolor, GL_COLOR, 0, glm::value_ptr(glm::vec3(0.0f))); 391 | glClearNamedFramebufferfv(fb_finalcolor, GL_DEPTH, 0, &depth_clear_val); 392 | 393 | glBindFramebuffer(GL_FRAMEBUFFER, fb_finalcolor); 394 | 395 | glBindTextureUnit(0, texture_gbuffer_position); 396 | glBindTextureUnit(1, texture_gbuffer_normal); 397 | glBindTextureUnit(2, texture_gbuffer_albedo); 398 | glBindTextureUnit(3, texture_gbuffer_depth); 399 | glBindTextureUnit(4, texture_gbuffer_emissive); 400 | glBindTextureUnit(5, texture_skybox); 401 | 402 | glBindProgramPipeline(pr_main); 403 | glBindVertexArray(vao_empty); 404 | 405 | set_uniform(frag_shader, uniform_cam_pos, camera_position); 406 | set_uniform(frag_shader, uniform_light_col, glm::vec3(1.0)); 407 | set_uniform(frag_shader, uniform_light_pos, light_pos); 408 | set_uniform(vert_shader, uniform_cam_dir, glm::inverse(glm::mat3(camera_view))); 409 | set_uniform(vert_shader, uniform_fov, fov); 410 | set_uniform(vert_shader, uniform_aspect, float(viewport_width) / float(viewport_height)); 411 | set_uniform(vert_shader, uniform_uvs_diff, glm::vec2( 412 | float(viewport_width) / float(screen_width), 413 | float(viewport_height) / float(screen_height) 414 | )); 415 | 416 | glDrawArrays(GL_TRIANGLES, 0, 6); 417 | 418 | /* motion blur */ 419 | 420 | glClearNamedFramebufferfv(fb_blur, GL_COLOR, 0, glm::value_ptr(glm::vec3(0.0f))); 421 | 422 | glBindFramebuffer(GL_FRAMEBUFFER, fb_blur); 423 | 424 | glBindTextureUnit(0, texture_gbuffer_color); 425 | glBindTextureUnit(1, texture_gbuffer_velocity); 426 | 427 | glBindProgramPipeline(pr_blur); 428 | glBindVertexArray(vao_empty); 429 | 430 | set_uniform(frag_shader_blur, uniform_blur_bias, 2.0f/*float(fps_sum) / float(60)*/); 431 | set_uniform(vert_shader_blur, uniform_uvs_diff, glm::vec2( 432 | float(viewport_width) / float(screen_width), 433 | float(viewport_height) / float(screen_height) 434 | )); 435 | 436 | glDrawArrays(GL_TRIANGLES, 0, 6); 437 | 438 | /* scale raster */ 439 | glViewport(0, 0, window_width, window_height); 440 | 441 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 442 | glBlitNamedFramebuffer(fb_blur, 0, 0, 0, viewport_width, viewport_height, 0, 0, window_width, window_height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 443 | 444 | SDL_GL_SwapWindow(window); 445 | } 446 | 447 | delete_items(glDeleteBuffers, 448 | { 449 | vbo_cube, 450 | ibo_cube, 451 | 452 | vbo_quad, 453 | ibo_quad, 454 | }); 455 | delete_items(glDeleteTextures, 456 | { 457 | texture_cube_diffuse, 458 | texture_cube_specular, 459 | texture_cube_normal, 460 | 461 | texture_gbuffer_position, 462 | texture_gbuffer_albedo, 463 | texture_gbuffer_normal, 464 | texture_gbuffer_depth, 465 | texture_gbuffer_color, 466 | 467 | texture_skybox, 468 | 469 | texture_motion_blur, 470 | texture_motion_blur_mask 471 | }); 472 | delete_items(glDeleteProgram, { 473 | vert_shader, 474 | frag_shader, 475 | 476 | vert_shader_g, 477 | frag_shader_g, 478 | }); 479 | 480 | delete_items(glDeleteProgramPipelines, { pr_main, pr_gbuffer }); 481 | delete_items(glDeleteVertexArrays, { vao_cube, vao_empty }); 482 | delete_items(glDeleteFramebuffers, { fb_gbuffer, fb_finalcolor, fb_blur }); 483 | 484 | SDL_GL_DeleteContext(gl_context); 485 | SDL_DestroyWindow(window); 486 | } 487 | 488 | int main(int argc, char* argv[]) 489 | { 490 | SDL_SetMainReady(); 491 | SDL_Init(SDL_INIT_VIDEO); 492 | 493 | try 494 | { 495 | GuardedMain(); 496 | } 497 | catch (const std::exception& e) 498 | { 499 | std::cerr << e.what() << std::endl; 500 | } 501 | 502 | SDL_Quit(); 503 | return 0; 504 | } 505 | -------------------------------------------------------------------------------- /source/Model.cpp: -------------------------------------------------------------------------------- 1 | #include "Model.h" 2 | 3 | std::vector calc_tangents(std::vector const& vertices, std::vector const& indices) 4 | { 5 | std::vector res; 6 | res.reserve(indices.size()); 7 | for (auto q = 0; q < 6; ++q) 8 | { 9 | auto 10 | v = q * 4, 11 | i = q * 6; 12 | glm::vec3 13 | edge0 = vertices[indices[i + 1]].position - vertices[indices[i + 0]].position, edge1 = vertices[indices[i + 2]].position - vertices[indices[0]].position, 14 | edge2 = vertices[indices[i + 4]].position - vertices[indices[i + 3]].position, edge3 = vertices[indices[i + 5]].position - vertices[indices[3]].position; 15 | 16 | glm::vec2 17 | delta_uv0 = vertices[indices[i + 1]].texcoord - vertices[indices[i + 0]].texcoord, delta_uv1 = vertices[indices[i + 2]].texcoord - vertices[indices[i + 0]].texcoord, 18 | delta_uv2 = vertices[indices[i + 4]].texcoord - vertices[indices[i + 3]].texcoord, delta_uv3 = vertices[indices[i + 5]].texcoord - vertices[indices[i + 3]].texcoord; 19 | 20 | float const 21 | f0 = 1.0f / (delta_uv0.x * delta_uv1.y - delta_uv1.x * delta_uv0.y), 22 | f1 = 1.0f / (delta_uv2.x * delta_uv3.y - delta_uv3.x * delta_uv2.y); 23 | 24 | auto const 25 | t0 = glm::normalize(glm::vec3( 26 | f0 * (delta_uv1.y * edge0.x - delta_uv0.y * edge1.x), 27 | f0 * (delta_uv1.y * edge0.y - delta_uv0.y * edge1.y), 28 | f0 * (delta_uv1.y * edge0.z - delta_uv0.y * edge1.z) 29 | )), 30 | t1 = glm::normalize(glm::vec3( 31 | f1 * (delta_uv3.y * edge2.x - delta_uv2.y * edge3.x), 32 | f1 * (delta_uv3.y * edge2.y - delta_uv2.y * edge3.y), 33 | f1 * (delta_uv3.y * edge2.z - delta_uv2.y * edge3.z) 34 | )); 35 | 36 | res.push_back(t0); res.push_back(t0); res.push_back(t0); 37 | res.push_back(t1); res.push_back(t1); res.push_back(t1); 38 | } 39 | return res; 40 | } -------------------------------------------------------------------------------- /source/Model.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | struct vertex_t 11 | { 12 | glm::vec3 position = glm::vec3(0.f); 13 | glm::vec3 color = glm::vec3(0.f); 14 | glm::vec3 normal = glm::vec3(0.f); 15 | glm::vec2 texcoord = glm::vec2(0.f); 16 | 17 | vertex_t(glm::vec3 const& position, glm::vec3 const& color, glm::vec3 const& normal, glm::vec2 const& texcoord) 18 | : position(position), color(color), normal(normal), texcoord(texcoord) {} 19 | }; 20 | 21 | struct attrib_format_t 22 | { 23 | GLuint attrib_index = 0; 24 | GLint size = 0; 25 | GLenum type = 0; 26 | GLuint relative_offset = 0; 27 | }; 28 | 29 | template 30 | constexpr std::pair type_to_size_enum() 31 | { 32 | if constexpr (std::is_same_v) 33 | return std::make_pair(1, GL_FLOAT); 34 | if constexpr (std::is_same_v) 35 | return std::make_pair(1, GL_INT); 36 | if constexpr (std::is_same_v) 37 | return std::make_pair(1, GL_UNSIGNED_INT); 38 | if constexpr (std::is_same_v) 39 | return std::make_pair(2, GL_FLOAT); 40 | if constexpr (std::is_same_v) 41 | return std::make_pair(3, GL_FLOAT); 42 | if constexpr (std::is_same_v) 43 | return std::make_pair(4, GL_FLOAT); 44 | throw std::runtime_error("unsupported type"); 45 | } 46 | 47 | template 48 | inline attrib_format_t create_attrib_format(GLuint attrib_index, GLuint relative_offset) 49 | { 50 | auto const [comp_count, type] = type_to_size_enum(); 51 | return attrib_format_t{ attrib_index, comp_count, type, relative_offset }; 52 | } 53 | 54 | template 55 | inline GLuint create_buffer(std::vector const& buff, GLenum flags = GL_DYNAMIC_STORAGE_BIT) 56 | { 57 | GLuint name = 0; 58 | glCreateBuffers(1, &name); 59 | glNamedBufferStorage(name, sizeof(typename std::vector::value_type) * buff.size(), buff.data(), flags); 60 | return name; 61 | } 62 | 63 | template 64 | std::tuple create_geometry(std::vector const& vertices, std::vector const& indices, std::vector const& attrib_formats) 65 | { 66 | GLuint vao = 0; 67 | auto vbo = create_buffer(vertices); 68 | auto ibo = create_buffer(indices); 69 | 70 | glCreateVertexArrays(1, &vao); 71 | glVertexArrayVertexBuffer(vao, 0, vbo, 0, sizeof(T)); 72 | glVertexArrayElementBuffer(vao, ibo); 73 | 74 | for (auto const& format : attrib_formats) 75 | { 76 | glEnableVertexArrayAttrib(vao, format.attrib_index); 77 | glVertexArrayAttribFormat(vao, format.attrib_index, format.size, format.type, GL_FALSE, format.relative_offset); 78 | glVertexArrayAttribBinding(vao, format.attrib_index, 0); 79 | } 80 | 81 | return std::make_tuple(vao, vbo, ibo); 82 | } 83 | 84 | std::vector calc_tangents(std::vector const& vertices, std::vector const& indices); -------------------------------------------------------------------------------- /source/Shader.cpp: -------------------------------------------------------------------------------- 1 | #include "Shader.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "Util.h" 9 | 10 | void validate_program(GLuint shader) 11 | { 12 | GLint compiled = 0; 13 | glProgramParameteri(shader, GL_PROGRAM_SEPARABLE, GL_TRUE); 14 | glGetProgramiv(shader, GL_LINK_STATUS, &compiled); 15 | if (compiled == GL_FALSE) 16 | { 17 | std::array compiler_log; 18 | glGetProgramInfoLog(shader, compiler_log.size(), nullptr, compiler_log.data()); 19 | glDeleteShader(shader); 20 | 21 | std::ostringstream message; 22 | message << "shader contains error(s):\n\n" << compiler_log.data() << '\n'; 23 | auto str = message.str(); 24 | throw std::runtime_error(str); 25 | } 26 | } 27 | 28 | std::tuple create_program_from_files(std::string_view vert_filepath, std::string_view frag_filepath) 29 | { 30 | auto const vert_source = read_text_file(vert_filepath); 31 | auto const frag_source = read_text_file(frag_filepath); 32 | 33 | return create_program_from_sources(vert_source, frag_source); 34 | } 35 | 36 | std::tuple create_program_from_sources(std::string_view vert_source, std::string_view frag_source) 37 | { 38 | auto const v_ptr = vert_source.data(); 39 | auto const f_ptr = frag_source.data(); 40 | GLuint pipeline = 0; 41 | 42 | auto vert = glCreateShaderProgramv(GL_VERTEX_SHADER, 1, &v_ptr); 43 | validate_program(vert); 44 | 45 | auto frag = glCreateShaderProgramv(GL_FRAGMENT_SHADER, 1, &f_ptr); 46 | validate_program(frag); 47 | 48 | glCreateProgramPipelines(1, &pipeline); 49 | glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, vert); 50 | glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, frag); 51 | 52 | return std::make_tuple(pipeline, vert, frag); 53 | } 54 | 55 | GLuint create_shader(GLuint vert, GLuint frag) 56 | { 57 | GLuint pipeline = 0; 58 | glCreateProgramPipelines(1, &pipeline); 59 | glUseProgramStages(pipeline, GL_VERTEX_SHADER_BIT, vert); 60 | glUseProgramStages(pipeline, GL_FRAGMENT_SHADER_BIT, frag); 61 | return pipeline; 62 | } 63 | 64 | void delete_shader(GLuint pr, GLuint vs, GLuint fs) 65 | { 66 | glDeleteProgramPipelines(1, &pr); 67 | glDeleteProgram(vs); 68 | glDeleteProgram(fs); 69 | } 70 | 71 | // Shader code 72 | const char* blurFragSource = 73 | #include "shader/blur.frag.glsl" 74 | ; 75 | 76 | const char* blurVertSource = 77 | #include "shader/blur.vert.glsl" 78 | ; 79 | 80 | const char* gbufferFragSource = 81 | #include "shader/gbuffer.frag.glsl" 82 | ; 83 | 84 | const char* gbufferVertSource = 85 | #include "shader/gbuffer.vert.glsl" 86 | ; 87 | 88 | const char* mainFragSource = 89 | #include "shader/main.frag.glsl" 90 | ; 91 | 92 | const char* mainVertSource = 93 | #include "shader/main.vert.glsl" 94 | ; -------------------------------------------------------------------------------- /source/Shader.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | void validate_program(GLuint shader); 12 | 13 | std::tuple create_program_from_files(std::string_view vert_filepath, std::string_view frag_filepath); 14 | 15 | std::tuple create_program_from_sources(std::string_view vert_source, std::string_view frag_source); 16 | 17 | GLuint create_shader(GLuint vert, GLuint frag); 18 | 19 | void delete_shader(GLuint pr, GLuint vs, GLuint fs); 20 | 21 | template 22 | void set_uniform(GLuint shader, GLint location, T const& value) 23 | { 24 | if constexpr (std::is_same_v) glProgramUniform1i(shader, location, value); 25 | else if constexpr (std::is_same_v) glProgramUniform1ui(shader, location, value); 26 | else if constexpr (std::is_same_v) glProgramUniform1ui(shader, location, value); 27 | else if constexpr (std::is_same_v) glProgramUniform1f(shader, location, value); 28 | else if constexpr (std::is_same_v) glProgramUniform1d(shader, location, value); 29 | else if constexpr (std::is_same_v) glProgramUniform2fv(shader, location, 1, glm::value_ptr(value)); 30 | else if constexpr (std::is_same_v) glProgramUniform3fv(shader, location, 1, glm::value_ptr(value)); 31 | else if constexpr (std::is_same_v) glProgramUniform4fv(shader, location, 1, glm::value_ptr(value)); 32 | else if constexpr (std::is_same_v) glProgramUniform2iv(shader, location, 1, glm::value_ptr(value)); 33 | else if constexpr (std::is_same_v) glProgramUniform3iv(shader, location, 1, glm::value_ptr(value)); 34 | else if constexpr (std::is_same_v) glProgramUniform4iv(shader, location, 1, glm::value_ptr(value)); 35 | else if constexpr (std::is_same_v) glProgramUniform2uiv(shader, location, 1, glm::value_ptr(value)); 36 | else if constexpr (std::is_same_v) glProgramUniform3uiv(shader, location, 1, glm::value_ptr(value)); 37 | else if constexpr (std::is_same_v) glProgramUniform4uiv(shader, location, 1, glm::value_ptr(value)); 38 | else if constexpr (std::is_same_v) glProgramUniform4fv(shader, location, 1, glm::value_ptr(value)); 39 | else if constexpr (std::is_same_v) glProgramUniformMatrix3fv(shader, location, 1, GL_FALSE, glm::value_ptr(value)); 40 | else if constexpr (std::is_same_v) glProgramUniformMatrix4fv(shader, location, 1, GL_FALSE, glm::value_ptr(value)); 41 | else throw std::runtime_error("unsupported type"); 42 | } 43 | 44 | // Shader source code 45 | extern const char* blurFragSource; 46 | extern const char* blurVertSource; 47 | extern const char* gbufferFragSource; 48 | extern const char* gbufferVertSource; 49 | extern const char* mainFragSource; 50 | extern const char* mainVertSource; -------------------------------------------------------------------------------- /source/Ssao.cpp: -------------------------------------------------------------------------------- 1 | #include "Ssao.h" 2 | 3 | #include 4 | 5 | #include "Util.h" 6 | 7 | std::vector generate_ssao_kernel() 8 | { 9 | std::vector res; 10 | res.reserve(64); 11 | 12 | std::uniform_real_distribution random(0.0f, 1.0f); 13 | std::default_random_engine generator; 14 | 15 | for (int i = 0; i < 64; ++i) 16 | { 17 | auto sample = glm::normalize(glm::vec3( 18 | random(generator) * 2.0f - 1.0f, 19 | random(generator) * 2.0f - 1.0f, 20 | random(generator) 21 | )) * random(generator) 22 | * lerp(0.1f, 1.0f, glm::pow(float(i) / 64.0f, 2)); 23 | res.push_back(sample); 24 | } 25 | return res; 26 | } 27 | 28 | std::vector generate_ssao_noise() 29 | { 30 | std::vector res; 31 | res.reserve(16); 32 | 33 | std::uniform_real_distribution random(0.0f, 1.0f); 34 | std::default_random_engine generator; 35 | 36 | for (int i = 0; i < 16; ++i) 37 | res.push_back(glm::vec3( 38 | random(generator) * 2.0f - 1.0f, 39 | random(generator) * 2.0f - 1.0f, 40 | 0.0f 41 | )); 42 | 43 | return res; 44 | } -------------------------------------------------------------------------------- /source/Ssao.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | std::vector generate_ssao_kernel(); 7 | 8 | std::vector generate_ssao_noise(); -------------------------------------------------------------------------------- /source/Texture.cpp: -------------------------------------------------------------------------------- 1 | #include "Texture.h" 2 | 3 | #include 4 | #include 5 | 6 | #define STB_IMAGE_IMPLEMENTATION 7 | #include 8 | 9 | GLuint create_texture_2d(GLenum internal_format, GLenum format, GLsizei width, GLsizei height, void* data, GLenum filter, GLenum repeat, bool generateMipMaps) 10 | { 11 | GLuint tex = 0; 12 | glCreateTextures(GL_TEXTURE_2D, 1, &tex); 13 | int levels = 1; 14 | if (generateMipMaps) 15 | { 16 | levels = 1 + (int)std::floor(std::log2(std::min(width, height))); 17 | } 18 | glTextureStorage2D(tex, levels, internal_format, width, height); 19 | 20 | 21 | if (generateMipMaps) 22 | { 23 | if (filter == GL_LINEAR) 24 | glTextureParameteri(tex, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); 25 | else if (filter == GL_NEAREST) 26 | glTextureParameteri(tex, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST); 27 | else 28 | throw std::runtime_error("Unsupported filter"); 29 | 30 | } 31 | else 32 | { 33 | glTextureParameteri(tex, GL_TEXTURE_MIN_FILTER, filter); 34 | } 35 | glTextureParameteri(tex, GL_TEXTURE_MAG_FILTER, filter); 36 | 37 | glTextureParameteri(tex, GL_TEXTURE_WRAP_S, repeat); 38 | glTextureParameteri(tex, GL_TEXTURE_WRAP_T, repeat); 39 | glTextureParameteri(tex, GL_TEXTURE_WRAP_R, repeat); 40 | 41 | if (data) 42 | { 43 | glTextureSubImage2D(tex, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, data); 44 | } 45 | 46 | if (generateMipMaps) glGenerateTextureMipmap(tex); 47 | 48 | return tex; 49 | } 50 | 51 | GLuint create_texture_2d_from_file(std::string_view filepath, stb_comp_t comp, bool generateMipMaps) 52 | { 53 | int x, y, c; 54 | if (!std::filesystem::exists(filepath.data())) 55 | { 56 | std::ostringstream message; 57 | message << "file " << filepath.data() << " does not exist."; 58 | throw std::runtime_error(message.str()); 59 | } 60 | const auto data = stbi_load(filepath.data(), &x, &y, &c, comp); 61 | 62 | auto const [in, ex] = [comp]() { 63 | switch (comp) 64 | { 65 | case STBI_rgb_alpha: return std::make_pair(GL_RGBA8, GL_RGBA); 66 | case STBI_rgb: return std::make_pair(GL_RGB8, GL_RGB); 67 | case STBI_grey: return std::make_pair(GL_R8, GL_RED); 68 | case STBI_grey_alpha: return std::make_pair(GL_RG8, GL_RG); 69 | default: throw std::runtime_error("invalid format"); 70 | } 71 | }(); 72 | 73 | const auto name = create_texture_2d(in, ex, x, y, data, GL_LINEAR, GL_REPEAT, generateMipMaps); 74 | stbi_image_free(data); 75 | return name; 76 | } 77 | 78 | GLuint create_texture_cube_from_file(std::array const& filepath, stb_comp_t comp) 79 | { 80 | int x, y, c; 81 | std::array faces; 82 | 83 | auto const [in, ex] = [comp]() { 84 | switch (comp) 85 | { 86 | case STBI_rgb_alpha: return std::make_pair(GL_RGBA8, GL_RGBA); 87 | case STBI_rgb: return std::make_pair(GL_RGB8, GL_RGB); 88 | case STBI_grey: return std::make_pair(GL_R8, GL_RED); 89 | case STBI_grey_alpha: return std::make_pair(GL_RG8, GL_RG); 90 | default: throw std::runtime_error("invalid format"); 91 | } 92 | }(); 93 | 94 | for (auto i = 0; i < 6; i++) 95 | { 96 | faces[i] = stbi_load(filepath[i].data(), &x, &y, &c, comp); 97 | } 98 | 99 | const auto name = create_texture_cube(in, ex, x, y, faces); 100 | 101 | for (auto face : faces) 102 | { 103 | stbi_image_free(face); 104 | } 105 | return name; 106 | } -------------------------------------------------------------------------------- /source/Texture.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | using stb_comp_t = int; 11 | GLuint create_texture_2d_from_file(std::string_view filepath, stb_comp_t comp = STBI_rgb_alpha, bool generateMipMaps = false); 12 | 13 | GLuint create_texture_2d(GLenum internal_format, GLenum format, GLsizei width, GLsizei height, void* data = nullptr, GLenum filter = GL_LINEAR, GLenum repeat = GL_REPEAT, bool generateMipMaps = false); 14 | 15 | GLuint create_texture_cube_from_file(std::array const& filepath, stb_comp_t comp = STBI_rgb_alpha); 16 | 17 | template 18 | GLuint create_texture_cube(GLenum internal_format, GLenum format, GLsizei width, GLsizei height, std::array const& data) 19 | { 20 | GLuint tex = 0; 21 | glCreateTextures(GL_TEXTURE_CUBE_MAP, 1, &tex); 22 | glTextureStorage2D(tex, 1, internal_format, width, height); 23 | for (GLint i = 0; i < 6; ++i) 24 | { 25 | if (data[i]) 26 | { 27 | glTextureSubImage3D(tex, 0, 0, 0, i, width, height, 1, format, GL_UNSIGNED_BYTE, data[i]); 28 | } 29 | } 30 | // Fix cubemap seams 31 | glTextureParameteri(tex, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 32 | glTextureParameteri(tex, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 33 | 34 | return tex; 35 | } -------------------------------------------------------------------------------- /source/Util.cpp: -------------------------------------------------------------------------------- 1 | #include "Util.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | std::string read_text_file(std::string_view filepath) 10 | { 11 | if (!std::filesystem::exists(filepath.data())) 12 | { 13 | std::ostringstream message; 14 | message << "file " << filepath.data() << " does not exist."; 15 | throw std::filesystem::filesystem_error(message.str(), std::make_error_code(std::errc::no_such_file_or_directory)); 16 | } 17 | std::ifstream file(filepath.data()); 18 | return std::string(std::istreambuf_iterator(file), std::istreambuf_iterator()); 19 | } 20 | 21 | glm::vec3 orbit_axis(float angle, glm::vec3 const& axis, glm::vec3 const& spread) { return glm::angleAxis(angle, axis) * spread; } 22 | 23 | float lerp(float a, float b, float f) { return a + f * (b - a); } -------------------------------------------------------------------------------- /source/Util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | std::string read_text_file(std::string_view filepath); 12 | 13 | template 14 | std::string string_format(const std::string& format, Args ... args) 15 | { 16 | const size_t size = snprintf(nullptr, 0, format.c_str(), args ...) + 1; // Extra space for '\0' 17 | std::unique_ptr buf(new char[size]); 18 | snprintf(buf.get(), size, format.c_str(), args ...); 19 | return std::string(buf.get(), buf.get() + size - 1); // We don't want the '\0' inside 20 | } 21 | 22 | template 23 | int64_t now() 24 | { 25 | return std::chrono::duration_cast(std::chrono::high_resolution_clock::now().time_since_epoch()).count(); 26 | } 27 | 28 | glm::vec3 orbit_axis(float angle, glm::vec3 const& axis, glm::vec3 const& spread); 29 | 30 | float lerp(float a, float b, float f); 31 | -------------------------------------------------------------------------------- /source/shader/blur.frag.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | layout (location = 0) out vec4 col; 5 | layout (binding = 0) uniform sampler2D tex_col; 6 | layout (binding = 1) uniform sampler2D tex_vel; 7 | 8 | layout (location = 0) uniform float vel_scale; 9 | 10 | in in_block 11 | { 12 | vec2 texcoord; 13 | } inp; 14 | 15 | void main() 16 | { 17 | vec2 texel_size = 1.0 / vec2(textureSize(tex_col, 0)); 18 | vec2 tex_coords = gl_FragCoord.xy * texel_size; 19 | vec2 vel = texture(tex_vel, tex_coords).rg; 20 | vel *= vel_scale; 21 | 22 | float speed = length(vel / texel_size); 23 | int samples = clamp(int(speed), 1, 40); 24 | 25 | col = texture(tex_col, inp.texcoord); 26 | 27 | for (int i = 1; i < samples; ++i) 28 | { 29 | vec2 offset = vel * (float(i) / float(samples - 1) - 0.5); 30 | col += texture(tex_col, tex_coords + offset); 31 | } 32 | col /= float(samples); 33 | } 34 | 35 | )===" -------------------------------------------------------------------------------- /source/shader/blur.vert.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | out gl_PerVertex{ vec4 gl_Position; }; 5 | 6 | layout(location = 3) uniform vec2 u_uv_diff; 7 | 8 | out out_block 9 | { 10 | vec2 texcoord; 11 | } o; 12 | 13 | void main() 14 | { 15 | vec2 v[] = { 16 | vec2(-1.0f, 1.0f), 17 | vec2(1.0f, 1.0f), 18 | vec2(1.0f,-1.0f), 19 | vec2(-1.0f,-1.0f) 20 | }; 21 | vec2 t[] = { 22 | vec2(0.0f, 1.0f), 23 | vec2(1.0f, 1.0f), 24 | vec2(1.0f, 0.0f), 25 | vec2(0.0f, 0.0f) 26 | 27 | }; 28 | uint i[] = { 0, 3, 2, 2, 1, 0 }; 29 | 30 | const vec2 position = v[i[gl_VertexID]]; 31 | const vec2 texcoord = t[i[gl_VertexID]]; 32 | 33 | o.texcoord = texcoord * u_uv_diff; 34 | gl_Position = vec4(position, 0.0, 1.0); 35 | } 36 | 37 | )===" -------------------------------------------------------------------------------- /source/shader/gbuffer.frag.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | in in_block 5 | { 6 | vec3 pos; 7 | vec3 nrm; 8 | vec2 uvs; 9 | smooth vec4 curr_pos; 10 | smooth vec4 prev_pos; 11 | } i; 12 | 13 | layout(location = 0) out vec3 out_pos; 14 | layout(location = 1) out vec3 out_nrm; 15 | layout(location = 2) out vec4 out_alb; 16 | layout(location = 3) out vec2 out_vel; 17 | layout(location = 4) out vec3 out_emi; 18 | 19 | layout(binding = 0) uniform sampler2D dif; 20 | layout(binding = 1) uniform sampler2D spc; 21 | layout(binding = 2) uniform sampler2D nrm; 22 | layout(binding = 3) uniform sampler2D emi; 23 | 24 | layout(location = 0) uniform float emissive_strength; 25 | 26 | void main() 27 | { 28 | vec3 dif_tex = texture(dif, i.uvs).rgb; 29 | vec3 spc_tex = texture(spc, i.uvs).rgb; 30 | vec3 nrm_tex = texture(nrm, i.uvs).rgb; 31 | 32 | out_pos = i.pos; 33 | out_nrm = normalize(cross(i.nrm, nrm_tex)); 34 | out_alb.rgb = texture(dif, i.uvs).rgb; 35 | out_alb.a = texture(spc, i.uvs).r; 36 | out_vel = ((i.curr_pos.xy / i.curr_pos.w) * 0.5 + 0.5) - ((i.prev_pos.xy / i.prev_pos.w) * 0.5 + 0.5); 37 | out_emi = texture(emi, i.uvs).rgb * emissive_strength; 38 | } 39 | 40 | )===" -------------------------------------------------------------------------------- /source/shader/gbuffer.vert.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | out gl_PerVertex { vec4 gl_Position; }; 5 | 6 | out out_block 7 | { 8 | vec3 pos; 9 | vec3 nrm; 10 | vec2 uvs; 11 | smooth vec4 curr_pos; 12 | smooth vec4 prev_pos; 13 | } o; 14 | 15 | layout (location = 0) in vec3 pos; 16 | layout (location = 1) in vec3 col; 17 | layout (location = 2) in vec3 nrm; 18 | layout (location = 3) in vec2 uvs; 19 | 20 | layout (location = 0) uniform mat4 proj; 21 | layout (location = 1) uniform mat4 view; 22 | layout (location = 2) uniform mat4 modl; 23 | layout (location = 3) uniform mat4 mvp_curr; 24 | layout (location = 4) uniform mat4 mvp_prev; 25 | layout (location = 5) uniform bool except; 26 | 27 | void main() 28 | { 29 | if (!except) 30 | { 31 | o.curr_pos = mvp_curr * vec4(pos, 1.0); 32 | o.prev_pos = mvp_prev * vec4(pos, 1.0); 33 | } 34 | else 35 | { 36 | o.curr_pos = mvp_curr * vec4(pos, 1.0); 37 | o.prev_pos = o.curr_pos; 38 | } 39 | const vec4 mpos = (view * modl * vec4(pos, 1.0)); 40 | o.pos = (modl * vec4(pos, 1.0)).xyz; 41 | o.nrm = mat3(transpose(inverse(modl))) * nrm; 42 | o.uvs = uvs; 43 | gl_Position = proj * mpos; 44 | } 45 | 46 | )===" -------------------------------------------------------------------------------- /source/shader/main.frag.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | layout (location = 0) out vec3 col; 5 | 6 | // Textures 7 | layout(binding = 0) uniform sampler2D tex_position; 8 | layout(binding = 1) uniform sampler2D tex_normal; 9 | layout(binding = 2) uniform sampler2D tex_albedo; 10 | layout(binding = 3) uniform sampler2D tex_depth; 11 | layout(binding = 4) uniform sampler2D tex_emissive; 12 | layout(binding = 5) uniform samplerCube texcube_skybox; 13 | 14 | layout(location = 0) uniform vec3 u_camera_position; 15 | // Point light 16 | layout(location = 1) uniform vec3 light_col; 17 | layout(location = 2) uniform vec3 light_pos; 18 | 19 | in in_block 20 | { 21 | vec2 texcoord; 22 | vec3 ray; 23 | } i; 24 | 25 | vec3 calculate_specular(float strength, vec3 color, vec3 view_pos, vec3 vert_pos, vec3 light_dir, vec3 normal) 26 | { 27 | vec3 view_dir = normalize(view_pos - vert_pos); 28 | vec3 ref_dir = reflect(-light_dir, normal); 29 | 30 | float spec = pow(max(dot(view_dir, ref_dir), 0.0), 32.0); 31 | return strength * spec * color; 32 | } 33 | 34 | void main() 35 | { 36 | const float depth = texture(tex_depth, i.texcoord).r; 37 | if (depth == 1.0) 38 | { 39 | col = texture(texcube_skybox, i.ray).rgb; 40 | return; 41 | } 42 | 43 | const vec3 position = texture(tex_position, i.texcoord).rgb; 44 | const vec3 normal = texture(tex_normal, i.texcoord).rgb; 45 | // Albedo = diffuse 46 | const vec4 albedo_specular = texture(tex_albedo, i.texcoord); 47 | const vec3 albedo = albedo_specular.rgb; 48 | const float specular = albedo_specular.a; 49 | 50 | vec3 light_dir = normalize(light_pos - position); 51 | float light_dif = max(dot(normal, light_dir), 0.0); 52 | 53 | 54 | vec3 ambient_col = vec3(0.1); 55 | 56 | // Final colors 57 | vec3 ambient = ambient_col * albedo; 58 | vec3 diffuse = light_col * light_dif * albedo; 59 | vec3 light_spec = calculate_specular(specular, light_col, u_camera_position, position, light_dir, normal); 60 | vec3 emission = texture(tex_emissive, i.texcoord).rgb; 61 | 62 | col = ambient + diffuse + light_spec + emission; 63 | col = col / (col + vec3(1.0)); 64 | } 65 | 66 | )===" -------------------------------------------------------------------------------- /source/shader/main.vert.glsl: -------------------------------------------------------------------------------- 1 | R"===( 2 | #version 460 3 | 4 | out gl_PerVertex{ vec4 gl_Position; }; 5 | 6 | layout(location = 0) uniform mat3 u_camera_direction; 7 | layout(location = 1) uniform float u_fov; 8 | layout(location = 2) uniform float u_ratio; 9 | layout(location = 3) uniform vec2 u_uv_diff; 10 | 11 | out out_block 12 | { 13 | vec2 texcoord; 14 | vec3 ray; 15 | } o; 16 | 17 | vec3 skyray(vec2 texcoord, float fovy, float aspect) 18 | { 19 | float d = 0.5 / tan(fovy / 2.0); 20 | return normalize(vec3((texcoord.x - 0.5) * aspect, texcoord.y - 0.5, -d)); 21 | } 22 | 23 | void main() 24 | { 25 | vec2 v[] = { 26 | vec2(-1.0f, 1.0f), 27 | vec2(1.0f, 1.0f), 28 | vec2(1.0f,-1.0f), 29 | vec2(-1.0f,-1.0f) 30 | }; 31 | vec2 t[] = { 32 | vec2(0.0f, 1.0f), 33 | vec2(1.0f, 1.0f), 34 | vec2(1.0f, 0.0f), 35 | vec2(0.0f, 0.0f) 36 | 37 | }; 38 | uint i[] = { 0, 3, 2, 2, 1, 0 }; 39 | 40 | const vec2 position = v[i[gl_VertexID]]; 41 | const vec2 texcoord = t[i[gl_VertexID]]; 42 | 43 | o.ray = u_camera_direction * skyray(texcoord, u_fov, u_ratio); 44 | o.texcoord = texcoord * u_uv_diff; 45 | gl_Position = vec4(position, 0.0, 1.0); 46 | } 47 | 48 | )===" --------------------------------------------------------------------------------