├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── data └── gi_1.jpg └── src ├── .clang-format ├── CMakeLists.txt ├── main.cpp ├── shader ├── depth_fs.glsl ├── dilate_fs.glsl ├── fullscreen_triangle_vs.glsl ├── lightmap_fs.glsl ├── lightmap_vs.glsl ├── mesh_fs.glsl ├── mesh_vs.glsl ├── shadow_map_vs.glsl ├── skybox_fs.glsl ├── skybox_vs.glsl ├── visualize_lightmap_fs.glsl └── visualize_submeshes_fs.glsl ├── skybox.cpp └── skybox.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Compiled Dynamic libraries 14 | *.so 15 | *.dylib 16 | *.dll 17 | 18 | # Fortran module files 19 | *.mod 20 | *.smod 21 | 22 | # Compiled Static libraries 23 | *.lai 24 | *.la 25 | *.a 26 | *.lib 27 | 28 | # Executables 29 | *.exe 30 | *.out 31 | *.app 32 | 33 | external/ 34 | external/* 35 | external/dwSampleFramwork 36 | bin/ 37 | bin/* 38 | build/ 39 | build/* 40 | lib/ 41 | lib/* 42 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/embree"] 2 | path = external/embree 3 | url = https://github.com/embree/embree.git 4 | [submodule "external/xatlas"] 5 | path = external/xatlas 6 | url = https://github.com/jpcy/xatlas.git 7 | [submodule "external/dwSampleFramework"] 8 | path = external/dwSampleFramework 9 | url = https://github.com/diharaw/dwSampleFramework.git 10 | [submodule "external/oidn"] 11 | path = external/oidn 12 | url = https://github.com/OpenImageDenoise/oidn.git 13 | [submodule "external/dwThreadPool"] 14 | path = external/dwThreadPool 15 | url = https://github.com/diharaw/dwThreadPool.git 16 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8 FATAL_ERROR) 2 | 3 | project("PrecomputedGI") 4 | 5 | IF(APPLE) 6 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") 7 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") 8 | ENDIF() 9 | 10 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib") 11 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib") 12 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) 13 | 14 | add_subdirectory(external/embree) 15 | add_subdirectory(external/dwSampleFramework) 16 | 17 | set(XATLAS_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/external/xatlas") 18 | set(EMBREE_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/external/embree/include/embree3") 19 | set(HOSEKSKY_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/external/HosekSky") 20 | set(THREADPOOL_INCLUDE_DIRS "${PROJECT_SOURCE_DIR}/external/dwThreadPool/include") 21 | 22 | include_directories("${DW_SAMPLE_FRAMEWORK_INCLUDES}" 23 | "${XATLAS_INCLUDE_DIRS}" 24 | "${EMBREE_INCLUDE_DIRS}" 25 | "${HOSEKSKY_INCLUDE_DIRS}" 26 | "${THREADPOOL_INCLUDE_DIRS}") 27 | 28 | add_subdirectory(src) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Dihara Wijetunga 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/packagist/l/doctrine/orm.svg)](https://opensource.org/licenses/MIT) 2 | 3 | # Lightmaps 4 | An OpenGL sample for generating path traced indirect diffuse lightmaps for real-time rendering applications. 5 | 6 | ## Screenshots 7 | 8 | ![Lightmaps](data/gi_1.jpg) 9 | 10 | ## Dependencies 11 | * [dwSampleFramework](https://github.com/diharaw/dwSampleFramework) 12 | * [embree](https://https://github.com/embree/embree) 13 | * [Open Image Denoise](https://github.com/OpenImageDenoise/oidn) 14 | * [dwThreadPool](https://github.com/diharaw/dwThreadPool) 15 | 16 | ## License 17 | ``` 18 | Copyright (c) 2019 Dihara Wijetunga 19 | 20 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 21 | associated documentation files (the "Software"), to deal in the Software without restriction, 22 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 23 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 24 | subject to the following conditions: 25 | 26 | The above copyright notice and this permission notice shall be included in all copies or substantial 27 | portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 30 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 31 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 32 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 33 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 34 | ``` 35 | -------------------------------------------------------------------------------- /data/gi_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/lightmap-baking/c6f27d8a78883e6f3d2bcab2d27a0511f7488d1c/data/gi_1.jpg -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: 'true' 5 | AlignConsecutiveDeclarations: 'true' 6 | AlignEscapedNewlines: Left 7 | AlignOperands: 'false' 8 | AlignTrailingComments: 'true' 9 | AllowAllParametersOfDeclarationOnNextLine: 'true' 10 | AllowShortBlocksOnASingleLine: 'true' 11 | AllowShortCaseLabelsOnASingleLine: 'true' 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: 'true' 14 | AllowShortLoopsOnASingleLine: 'true' 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: 'false' 17 | AlwaysBreakTemplateDeclarations: 'true' 18 | BinPackArguments: 'false' 19 | BinPackParameters: 'false' 20 | BreakBeforeBraces: Allman 21 | BreakBeforeInheritanceComma: 'false' 22 | BreakBeforeTernaryOperators: 'false' 23 | BreakConstructorInitializers: AfterColon 24 | BreakStringLiterals: 'true' 25 | CompactNamespaces: 'true' 26 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 27 | Cpp11BracedListStyle: 'false' 28 | FixNamespaceComments: 'true' 29 | IncludeBlocks: Regroup 30 | IndentCaseLabels: 'true' 31 | IndentPPDirectives: AfterHash 32 | IndentWrappedFunctionNames: 'true' 33 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 34 | Language: Cpp 35 | NamespaceIndentation: None 36 | PointerAlignment: Left 37 | ReflowComments: 'true' 38 | SortIncludes: 'false' 39 | SortUsingDeclarations: 'true' 40 | SpaceAfterCStyleCast: 'false' 41 | SpaceBeforeAssignmentOperators: 'true' 42 | SpaceBeforeParens: ControlStatements 43 | SpaceInEmptyParentheses: 'false' 44 | SpacesInAngles: 'false' 45 | SpacesInCStyleCastParentheses: 'false' 46 | SpacesInContainerLiterals: 'true' 47 | SpacesInParentheses: 'false' 48 | SpacesInSquareBrackets: 'false' 49 | 50 | ... 51 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8 FATAL_ERROR) 2 | 3 | find_program(CLANG_FORMAT_EXE NAMES "clang-format" DOC "Path to clang-format executable") 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 7 | 8 | set(PRECOMPUTEDGI_SOURCES ${PROJECT_SOURCE_DIR}/src/main.cpp 9 | ${PROJECT_SOURCE_DIR}/src/skybox.h 10 | ${PROJECT_SOURCE_DIR}/src/skybox.cpp) 11 | 12 | set(XATLAS_SOURCES ${PROJECT_SOURCE_DIR}/external/xatlas/xatlas.cpp 13 | ${PROJECT_SOURCE_DIR}/external/xatlas/xatlas.h) 14 | 15 | set(HOSEKSKY_SOURCES ${PROJECT_SOURCE_DIR}/external/HosekSky/ArHosekSkyModel.cpp) 16 | 17 | file(GLOB_RECURSE SHADER_SOURCES ${PROJECT_SOURCE_DIR}/src/*.glsl) 18 | 19 | if(APPLE) 20 | add_executable(PrecomputedGI MACOSX_BUNDLE ${PRECOMPUTEDGI_SOURCES} ${XATLAS_SOURCES} ${HOSEKSKY_SOURCES} ${SHADER_SOURCES} ${ASSET_SOURCES}) 21 | set(MACOSX_BUNDLE_BUNDLE_NAME "PrecomputedGI") 22 | set_source_files_properties(${SHADER_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/shader) 23 | set_source_files_properties(${ASSET_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) 24 | else() 25 | add_executable(PrecomputedGI ${PRECOMPUTEDGI_SOURCES} ${XATLAS_SOURCES} ${HOSEKSKY_SOURCES}) 26 | endif() 27 | 28 | target_link_libraries(PrecomputedGI dwSampleFramework) 29 | target_link_libraries(PrecomputedGI embree) 30 | 31 | if (NOT APPLE) 32 | add_custom_command(TARGET PrecomputedGI POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/shader $/shader) 33 | endif() 34 | 35 | if(CLANG_FORMAT_EXE) 36 | add_custom_target(PrecomputedGI-clang-format COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${PRECOMPUTEDGI_SOURCES} ${SHADER_SOURCES}) 37 | endif() 38 | 39 | set_property(TARGET PrecomputedGI PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/$(Configuration)") -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #define _USE_MATH_DEFINES 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include "skybox.h" 21 | 22 | #undef min 23 | #define CAMERA_FAR_PLANE 200.0f 24 | #define DEBUG_CAMERA_FAR_PLANE 10000.0f 25 | #define LIGHTMAP_TEXTURE_SIZE 1024 26 | #define LIGHTMAP_CHART_PADDING 6 27 | #define LIGHTMAP_SPP 1 28 | #define LIGHTMAP_BOUNCES 2 29 | #define SHADOW_MAP_SIZE 1024 30 | #define LIGHT_FAR_PLANE 650.0f 31 | #define SHADOW_MAP_EXTENTS 75.0f 32 | 33 | struct GlobalUniforms 34 | { 35 | DW_ALIGNED(16) 36 | glm::mat4 view_proj; 37 | DW_ALIGNED(16) 38 | glm::mat4 light_view_proj; 39 | DW_ALIGNED(16) 40 | glm::vec4 cam_pos; 41 | }; 42 | 43 | struct LightmapSubMesh 44 | { 45 | uint32_t index_count; 46 | uint32_t base_vertex; 47 | uint32_t base_index; 48 | glm::vec3 max_extents; 49 | glm::vec3 min_extents; 50 | glm::vec3 color; 51 | }; 52 | 53 | struct LightmapVertex 54 | { 55 | glm::vec3 position; 56 | glm::vec2 uv; 57 | glm::vec2 lightmap_uv; 58 | glm::vec3 normal; 59 | glm::vec3 tangent; 60 | glm::vec3 bitangent; 61 | }; 62 | 63 | struct LightmapMesh 64 | { 65 | std::vector submeshes; 66 | std::vector submesh_colors; 67 | std::vector vertex_colors; 68 | std::unique_ptr vbo; 69 | std::unique_ptr ibo; 70 | std::unique_ptr vao; 71 | }; 72 | 73 | struct BakePoint 74 | { 75 | glm::vec3 position; 76 | glm::vec3 direction; 77 | glm::ivec2 coord; 78 | }; 79 | 80 | struct BakeTaskArgs 81 | { 82 | uint32_t start_idx = 0; 83 | uint32_t end_idx = 0; 84 | }; 85 | 86 | class PrecomputedGI : public dw::Application 87 | { 88 | protected: 89 | // ----------------------------------------------------------------------------------------------------------------------------------- 90 | 91 | bool init(int argc, const char* argv[]) override 92 | { 93 | m_distribution = std::uniform_real_distribution(0.0f, 0.9999999f); 94 | 95 | m_light_target = glm::vec3(0.0f); 96 | glm::vec3 default_light_dir = glm::normalize(glm::vec3(0.0f, 0.9770f, 0.5000f)); 97 | m_light_direction = -default_light_dir; 98 | m_light_color = glm::vec3(10000.0f); 99 | 100 | // Create GPU resources. 101 | if (!create_shaders()) 102 | return false; 103 | 104 | // Load scene. 105 | if (!load_scene()) 106 | return false; 107 | 108 | create_textures(); 109 | create_lightmap_buffers(); 110 | initialize_lightmap(); 111 | 112 | if (!m_skybox.initialize(default_light_dir, glm::vec3(0.5f), 2.0f)) 113 | return false; 114 | 115 | if (!load_cached_lightmap()) 116 | bake_lightmap(); 117 | 118 | if (!create_uniform_buffer()) 119 | return false; 120 | 121 | // Create camera. 122 | create_camera(); 123 | 124 | m_transform = glm::mat4(1.0f); 125 | m_transform = glm::scale(m_transform, glm::vec3(10.0f)); 126 | m_transform = glm::rotate(m_transform, glm::radians(45.0f), glm::vec3(0.0, 1.0f, 0.0f)); 127 | 128 | return true; 129 | } 130 | 131 | // ----------------------------------------------------------------------------------------------------------------------------------- 132 | 133 | void update(double delta) override 134 | { 135 | finish_bake(); 136 | 137 | if (m_debug_gui) 138 | gui(); 139 | 140 | // Update camera. 141 | update_camera(); 142 | 143 | update_uniforms(); 144 | 145 | render_shadow_map(); 146 | render_lit_scene(); 147 | 148 | m_skybox.render(nullptr, m_width, m_height, m_main_camera->m_projection, m_main_camera->m_view); 149 | 150 | if (m_visualize_atlas) 151 | { 152 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 153 | 154 | glViewport(0, 0, m_height, m_height); 155 | 156 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 157 | glClear(GL_COLOR_BUFFER_BIT); 158 | 159 | if (m_highlight_submeshes) 160 | visualize_atlas_submeshes(); 161 | else 162 | { 163 | visualize_lightmap(); 164 | 165 | if (m_highlight_wireframe) 166 | visualize_atlas_submeshes(); 167 | } 168 | } 169 | } 170 | 171 | // ----------------------------------------------------------------------------------------------------------------------------------- 172 | 173 | void shutdown() override 174 | { 175 | rtcReleaseGeometry(m_embree_triangle_mesh); 176 | rtcReleaseScene(m_embree_scene); 177 | rtcReleaseDevice(m_embree_device); 178 | } 179 | 180 | // ----------------------------------------------------------------------------------------------------------------------------------- 181 | 182 | void window_resized(int width, int height) override 183 | { 184 | // Override window resized method to update camera projection. 185 | m_main_camera->update_projection(60.0f, 0.1f, CAMERA_FAR_PLANE, float(m_width) / float(m_height)); 186 | 187 | create_textures(); 188 | } 189 | 190 | // ----------------------------------------------------------------------------------------------------------------------------------- 191 | 192 | void key_pressed(int code) override 193 | { 194 | // Handle forward movement. 195 | if (code == GLFW_KEY_W) 196 | m_heading_speed = m_camera_speed; 197 | else if (code == GLFW_KEY_S) 198 | m_heading_speed = -m_camera_speed; 199 | 200 | // Handle sideways movement. 201 | if (code == GLFW_KEY_A) 202 | m_sideways_speed = -m_camera_speed; 203 | else if (code == GLFW_KEY_D) 204 | m_sideways_speed = m_camera_speed; 205 | 206 | if (code == GLFW_KEY_SPACE) 207 | m_mouse_look = true; 208 | 209 | if (code == GLFW_KEY_G) 210 | m_debug_gui = !m_debug_gui; 211 | } 212 | 213 | // ----------------------------------------------------------------------------------------------------------------------------------- 214 | 215 | void key_released(int code) override 216 | { 217 | // Handle forward movement. 218 | if (code == GLFW_KEY_W || code == GLFW_KEY_S) 219 | m_heading_speed = 0.0f; 220 | 221 | // Handle sideways movement. 222 | if (code == GLFW_KEY_A || code == GLFW_KEY_D) 223 | m_sideways_speed = 0.0f; 224 | 225 | if (code == GLFW_KEY_SPACE) 226 | m_mouse_look = false; 227 | } 228 | 229 | // ----------------------------------------------------------------------------------------------------------------------------------- 230 | 231 | void mouse_pressed(int code) override 232 | { 233 | // Enable mouse look. 234 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 235 | m_mouse_look = true; 236 | } 237 | 238 | // ----------------------------------------------------------------------------------------------------------------------------------- 239 | 240 | void mouse_released(int code) override 241 | { 242 | // Disable mouse look. 243 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 244 | m_mouse_look = false; 245 | } 246 | 247 | // ----------------------------------------------------------------------------------------------------------------------------------- 248 | 249 | protected: 250 | // ----------------------------------------------------------------------------------------------------------------------------------- 251 | 252 | dw::AppSettings intial_app_settings() override 253 | { 254 | dw::AppSettings settings; 255 | 256 | settings.resizable = true; 257 | settings.maximized = false; 258 | settings.refresh_rate = 60; 259 | settings.major_ver = 4; 260 | settings.width = 1920; 261 | settings.height = 1080; 262 | settings.title = "PrecomputedGI (c) 2019 Dihara Wijetunga"; 263 | 264 | return settings; 265 | } 266 | 267 | // ----------------------------------------------------------------------------------------------------------------------------------- 268 | 269 | private: 270 | // ----------------------------------------------------------------------------------------------------------------------------------- 271 | 272 | void gui() 273 | { 274 | if (ImGui::Checkbox("Conservative Rasterization", &m_enable_conservative_raster)) 275 | initialize_lightmap(); 276 | 277 | if (ImGui::Checkbox("Bilinear Filtering", &m_bilinear_filtering)) 278 | { 279 | if (m_bilinear_filtering) 280 | { 281 | m_lightmap_texture->set_mag_filter(GL_LINEAR); 282 | m_lightmap_dilated_texture->set_mag_filter(GL_LINEAR); 283 | } 284 | else 285 | { 286 | m_lightmap_texture->set_mag_filter(GL_NEAREST); 287 | m_lightmap_dilated_texture->set_mag_filter(GL_NEAREST); 288 | } 289 | } 290 | 291 | ImGui::Checkbox("Visualize Atlas", &m_visualize_atlas); 292 | ImGui::Checkbox("Dilated", &m_dilated); 293 | ImGui::Checkbox("Indirect Lighting", &m_indirect_lighting); 294 | 295 | if (m_visualize_atlas) 296 | { 297 | ImGui::Checkbox("Hightlight Submeshes", &m_highlight_submeshes); 298 | ImGui::Checkbox("Hightlight Wireframe", &m_highlight_wireframe); 299 | } 300 | 301 | if (ImGui::InputFloat3("Light Direction", &m_light_direction.x)) 302 | m_skybox.initialize(-m_light_direction, glm::vec3(0.5f), 2.0f); 303 | 304 | ImGui::SliderFloat("Ambient Intensity", &m_ambient_intensity, 0.0f, 1.0f); 305 | ImGui::InputFloat("Bias", &m_shadow_bias); 306 | ImGui::InputFloat("Offset", &m_offset); 307 | ImGui::InputInt("Num Samples", &m_num_samples); 308 | ImGui::InputInt("Num Bounces", &m_num_bounces); 309 | 310 | if (ImGui::Button("Bake")) 311 | bake_lightmap(); 312 | 313 | if (m_bake_in_progress) 314 | { 315 | uint32_t progress = m_baking_progress; 316 | 317 | ImGui::ProgressBar(float(progress) / float(m_total_samples_to_bake), ImVec2(0.0f, 0.0f)); 318 | ImGui::SameLine(0.0f, ImGui::GetStyle().ItemInnerSpacing.x); 319 | ImGui::Text("Baking Progress"); 320 | } 321 | } 322 | 323 | // ----------------------------------------------------------------------------------------------------------------------------------- 324 | 325 | void initialize_lightmap() 326 | { 327 | std::unique_ptr pos_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 328 | std::unique_ptr normal_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 329 | std::unique_ptr pos_dilated_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 330 | std::unique_ptr normal_dilated_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 331 | 332 | pos_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 333 | pos_dilated_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 334 | normal_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 335 | normal_dilated_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 336 | 337 | std::unique_ptr gbuffer_fbo = std::make_unique(); 338 | std::unique_ptr pos_dilated_fbo = std::make_unique(); 339 | std::unique_ptr normal_dilated_fbo = std::make_unique(); 340 | 341 | dw::Texture* textures[] = { pos_texture.get(), normal_texture.get() }; 342 | gbuffer_fbo->attach_multiple_render_targets(2, textures); 343 | pos_dilated_fbo->attach_render_target(0, pos_dilated_texture.get(), 0, 0); 344 | normal_dilated_fbo->attach_render_target(0, normal_dilated_texture.get(), 0, 0); 345 | 346 | if (m_enable_conservative_raster) 347 | { 348 | if (GLAD_GL_NV_conservative_raster) 349 | glEnable(GL_CONSERVATIVE_RASTERIZATION_NV); 350 | else if (GLAD_GL_INTEL_conservative_rasterization) 351 | glEnable(GL_INTEL_conservative_rasterization); 352 | } 353 | 354 | glDisable(GL_DEPTH_TEST); 355 | glDisable(GL_BLEND); 356 | 357 | glDisable(GL_CULL_FACE); 358 | 359 | gbuffer_fbo->bind(); 360 | 361 | glViewport(0, 0, m_lightmap_size, m_lightmap_size); 362 | 363 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 364 | glClear(GL_COLOR_BUFFER_BIT); 365 | 366 | // Bind shader program. 367 | m_lightmap_program->use(); 368 | 369 | // Bind vertex array. 370 | m_unwrapped_mesh.vao->bind(); 371 | 372 | for (uint32_t i = 0; i < m_unwrapped_mesh.submeshes.size(); i++) 373 | { 374 | LightmapSubMesh& submesh = m_unwrapped_mesh.submeshes[i]; 375 | 376 | // Issue draw call. 377 | glDrawElementsBaseVertex(GL_TRIANGLES, submesh.index_count, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * submesh.base_index), submesh.base_vertex); 378 | } 379 | 380 | if (m_enable_conservative_raster) 381 | { 382 | if (GLAD_GL_NV_conservative_raster) 383 | glDisable(GL_CONSERVATIVE_RASTERIZATION_NV); 384 | else if (GLAD_GL_INTEL_conservative_rasterization) 385 | glDisable(GL_INTEL_conservative_rasterization); 386 | } 387 | 388 | glFinish(); 389 | 390 | // Dilate 391 | dilate(pos_texture, pos_dilated_fbo.get()); 392 | dilate(normal_texture, normal_dilated_fbo.get()); 393 | 394 | glFinish(); 395 | 396 | std::vector ray_positions; 397 | std::vector ray_directions; 398 | 399 | ray_positions.resize(m_lightmap_size * m_lightmap_size); 400 | ray_directions.resize(m_lightmap_size * m_lightmap_size); 401 | 402 | // Copy bake sample points 403 | GL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0)); 404 | GL_CHECK_ERROR(glBindTexture(pos_dilated_texture->target(), pos_dilated_texture->id())); 405 | GL_CHECK_ERROR(glGetTexImage(pos_dilated_texture->target(), 0, pos_dilated_texture->format(), pos_dilated_texture->type(), ray_positions.data())); 406 | 407 | GL_CHECK_ERROR(glBindTexture(normal_dilated_texture->target(), normal_dilated_texture->id())); 408 | GL_CHECK_ERROR(glGetTexImage(normal_dilated_texture->target(), 0, normal_dilated_texture->format(), normal_dilated_texture->type(), ray_directions.data())); 409 | GL_CHECK_ERROR(glBindTexture(normal_dilated_texture->target(), 0)); 410 | 411 | glFinish(); 412 | 413 | for (int y = 0; y < m_lightmap_size; y++) 414 | { 415 | for (int x = 0; x < m_lightmap_size; x++) 416 | { 417 | glm::vec3 normal = ray_directions[m_lightmap_size * y + x]; 418 | glm::vec3 position = ray_positions[m_lightmap_size * y + x]; 419 | 420 | // Check if this is a valid lightmap texel 421 | if (valid_texel(normal)) 422 | m_bake_points.push_back({ position, normal, { x, y } }); 423 | } 424 | } 425 | } 426 | 427 | // ----------------------------------------------------------------------------------------------------------------------------------- 428 | 429 | void dilate(std::unique_ptr& tex, dw::Framebuffer* fbo) 430 | { 431 | fbo->bind(); 432 | 433 | glViewport(0, 0, m_lightmap_size, m_lightmap_size); 434 | 435 | glClearColor(0.0f, 0.0f, 0.0f, 0.0f); 436 | glClear(GL_COLOR_BUFFER_BIT); 437 | 438 | // Bind shader program. 439 | m_dilate_program->use(); 440 | 441 | if (m_dilate_program->set_uniform("s_Texture", 0)) 442 | tex->bind(0); 443 | 444 | // Render fullscreen triangle 445 | glDrawArrays(GL_TRIANGLES, 0, 3); 446 | } 447 | 448 | // ----------------------------------------------------------------------------------------------------------------------------------- 449 | 450 | void create_lightmap_buffers() 451 | { 452 | m_framebuffer.resize(m_lightmap_size * m_lightmap_size); 453 | } 454 | 455 | // ----------------------------------------------------------------------------------------------------------------------------------- 456 | 457 | void render_lit_scene() 458 | { 459 | render_scene(nullptr, m_mesh_program, 0, 0, m_width, m_height, GL_BACK); 460 | } 461 | 462 | // ----------------------------------------------------------------------------------------------------------------------------------- 463 | 464 | void render_shadow_map() 465 | { 466 | glEnable(GL_DEPTH_TEST); 467 | glDisable(GL_BLEND); 468 | glDisable(GL_CULL_FACE); 469 | 470 | m_shadow_map_fbo->bind(); 471 | 472 | glViewport(0, 0, SHADOW_MAP_SIZE, SHADOW_MAP_SIZE); 473 | 474 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 475 | glClearDepth(1.0); 476 | glClear(GL_DEPTH_BUFFER_BIT); 477 | 478 | // Bind shader program. 479 | m_shadow_map_program->use(); 480 | 481 | // Bind uniform buffers. 482 | m_global_ubo->bind_base(0); 483 | 484 | // Draw scene. 485 | render_mesh(m_unwrapped_mesh, m_transform, m_shadow_map_program); 486 | } 487 | 488 | // ----------------------------------------------------------------------------------------------------------------------------------- 489 | 490 | bool create_shaders() 491 | { 492 | { 493 | // Create general shaders 494 | m_lightmap_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/lightmap_fs.glsl")); 495 | m_mesh_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/mesh_vs.glsl")); 496 | m_shadow_map_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/shadow_map_vs.glsl")); 497 | m_mesh_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/mesh_fs.glsl")); 498 | m_triangle_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/fullscreen_triangle_vs.glsl")); 499 | m_lightmap_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/lightmap_vs.glsl")); 500 | m_visualize_lightmap_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/visualize_lightmap_fs.glsl")); 501 | m_visualize_submeshes_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/visualize_submeshes_fs.glsl")); 502 | m_dilate_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/dilate_fs.glsl")); 503 | m_depth_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/depth_fs.glsl")); 504 | 505 | { 506 | if (!m_lightmap_vs || !m_lightmap_fs) 507 | { 508 | DW_LOG_FATAL("Failed to create Shaders"); 509 | return false; 510 | } 511 | 512 | // Create general shader program 513 | dw::Shader* shaders[] = { m_lightmap_vs.get(), m_lightmap_fs.get() }; 514 | m_lightmap_program = std::make_unique(2, shaders); 515 | 516 | if (!m_lightmap_program) 517 | { 518 | DW_LOG_FATAL("Failed to create Shader Program"); 519 | return false; 520 | } 521 | } 522 | 523 | { 524 | if (!m_lightmap_vs || !m_visualize_submeshes_fs) 525 | { 526 | DW_LOG_FATAL("Failed to create Shaders"); 527 | return false; 528 | } 529 | 530 | // Create general shader program 531 | dw::Shader* shaders[] = { m_lightmap_vs.get(), m_visualize_submeshes_fs.get() }; 532 | m_visualize_submeshes_program = std::make_unique(2, shaders); 533 | 534 | if (!m_visualize_submeshes_program) 535 | { 536 | DW_LOG_FATAL("Failed to create Shader Program"); 537 | return false; 538 | } 539 | } 540 | 541 | { 542 | if (!m_shadow_map_vs || !m_depth_fs) 543 | { 544 | DW_LOG_FATAL("Failed to create Shaders"); 545 | return false; 546 | } 547 | 548 | // Create general shader program 549 | dw::Shader* shaders[] = { m_shadow_map_vs.get(), m_depth_fs.get() }; 550 | m_shadow_map_program = std::make_unique(2, shaders); 551 | 552 | if (!m_shadow_map_program) 553 | { 554 | DW_LOG_FATAL("Failed to create Shader Program"); 555 | return false; 556 | } 557 | 558 | m_shadow_map_program->uniform_block_binding("GlobalUniforms", 0); 559 | m_shadow_map_program->uniform_block_binding("CSMUniforms", 1); 560 | } 561 | 562 | { 563 | if (!m_triangle_vs || !m_dilate_fs) 564 | { 565 | DW_LOG_FATAL("Failed to create Shaders"); 566 | return false; 567 | } 568 | 569 | // Create general shader program 570 | dw::Shader* shaders[] = { m_triangle_vs.get(), m_dilate_fs.get() }; 571 | m_dilate_program = std::make_unique(2, shaders); 572 | 573 | if (!m_dilate_program) 574 | { 575 | DW_LOG_FATAL("Failed to create Shader Program"); 576 | return false; 577 | } 578 | } 579 | 580 | { 581 | if (!m_triangle_vs || !m_visualize_lightmap_fs) 582 | { 583 | DW_LOG_FATAL("Failed to create Shaders"); 584 | return false; 585 | } 586 | 587 | // Create general shader program 588 | dw::Shader* shaders[] = { m_triangle_vs.get(), m_visualize_lightmap_fs.get() }; 589 | m_visualize_lightmap_program = std::make_unique(2, shaders); 590 | 591 | if (!m_visualize_lightmap_program) 592 | { 593 | DW_LOG_FATAL("Failed to create Shader Program"); 594 | return false; 595 | } 596 | } 597 | 598 | { 599 | if (!m_mesh_vs || !m_mesh_fs) 600 | { 601 | DW_LOG_FATAL("Failed to create Shaders"); 602 | return false; 603 | } 604 | 605 | // Create general shader program 606 | dw::Shader* shaders[] = { m_mesh_vs.get(), m_mesh_fs.get() }; 607 | m_mesh_program = std::make_unique(2, shaders); 608 | 609 | if (!m_mesh_program) 610 | { 611 | DW_LOG_FATAL("Failed to create Shader Program"); 612 | return false; 613 | } 614 | 615 | m_mesh_program->uniform_block_binding("GlobalUniforms", 0); 616 | m_mesh_program->uniform_block_binding("CSMUniforms", 1); 617 | } 618 | } 619 | 620 | return true; 621 | } 622 | 623 | // ----------------------------------------------------------------------------------------------------------------------------------- 624 | 625 | void create_textures() 626 | { 627 | m_shadow_map = std::make_unique(SHADOW_MAP_SIZE, SHADOW_MAP_SIZE, 1, 1, 1, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT); 628 | m_lightmap_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 629 | 630 | m_lightmap_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 631 | 632 | m_shadow_map_fbo = std::make_unique(); 633 | m_shadow_map_fbo->attach_depth_stencil_target(m_shadow_map.get(), 0, 0); 634 | } 635 | 636 | // ----------------------------------------------------------------------------------------------------------------------------------- 637 | 638 | bool create_uniform_buffer() 639 | { 640 | // Create uniform buffer for global data 641 | m_global_ubo = std::make_unique(GL_DYNAMIC_DRAW, sizeof(GlobalUniforms)); 642 | 643 | return true; 644 | } 645 | 646 | // ----------------------------------------------------------------------------------------------------------------------------------- 647 | 648 | bool load_scene() 649 | { 650 | dw::Mesh* mesh = dw::Mesh::load("mesh/GI_Test_Scene.obj"); 651 | 652 | if (!mesh) 653 | { 654 | DW_LOG_FATAL("Failed to load mesh!"); 655 | return false; 656 | } 657 | 658 | if (!lightmap_uv_unwrap(mesh)) 659 | return false; 660 | 661 | if (!initialize_embree(mesh)) 662 | return false; 663 | 664 | dw::Mesh::unload(mesh); 665 | 666 | return true; 667 | } 668 | 669 | // ----------------------------------------------------------------------------------------------------------------------------------- 670 | 671 | bool create_lightmap_uv_unwrapped_mesh(xatlas::Atlas* atlas, dw::Mesh* mesh) 672 | { 673 | dw::Vertex* vertex_ptr = mesh->vertices(); 674 | 675 | std::vector vertices; 676 | std::vector indices; 677 | 678 | for (int i = 0; i < mesh->sub_mesh_count(); i++) 679 | { 680 | LightmapSubMesh sub; 681 | 682 | sub.color = mesh->sub_meshes()[i].mat->albedo_value(); 683 | sub.index_count = mesh->sub_meshes()[i].index_count; 684 | sub.base_vertex = mesh->sub_meshes()[i].base_vertex; 685 | sub.base_index = mesh->sub_meshes()[i].base_index; 686 | sub.max_extents = mesh->sub_meshes()[i].max_extents; 687 | sub.min_extents = mesh->sub_meshes()[i].min_extents; 688 | 689 | m_unwrapped_mesh.submeshes.push_back(sub); 690 | m_unwrapped_mesh.submesh_colors.push_back(glm::vec3(drand48(), drand48(), drand48())); 691 | } 692 | 693 | uint32_t index_count = 0; 694 | uint32_t vertex_count = 0; 695 | 696 | for (int mesh_idx = 0; mesh_idx < atlas->meshCount; mesh_idx++) 697 | { 698 | LightmapSubMesh& sub = m_unwrapped_mesh.submeshes[mesh_idx]; 699 | 700 | sub.base_index = index_count; 701 | sub.base_vertex = vertex_count; 702 | 703 | for (int i = 0; i < atlas->meshes[mesh_idx].vertexCount; i++) 704 | { 705 | int idx = atlas->meshes[mesh_idx].vertexArray[i].xref; 706 | 707 | LightmapVertex v; 708 | 709 | v.position = vertex_ptr[idx].position; 710 | v.uv = vertex_ptr[idx].tex_coord; 711 | v.normal = vertex_ptr[idx].normal; 712 | v.tangent = vertex_ptr[idx].tangent; 713 | v.bitangent = vertex_ptr[idx].bitangent; 714 | v.lightmap_uv = glm::vec2(atlas->meshes[mesh_idx].vertexArray[i].uv[0] / (atlas->width - 1), atlas->meshes[mesh_idx].vertexArray[i].uv[1] / (atlas->height - 1)); 715 | 716 | vertices.push_back(v); 717 | } 718 | 719 | for (int i = 0; i < atlas->meshes[mesh_idx].indexCount; i++) 720 | indices.push_back(atlas->meshes[mesh_idx].indexArray[i]); 721 | 722 | index_count += atlas->meshes[mesh_idx].indexCount; 723 | vertex_count += atlas->meshes[mesh_idx].vertexCount; 724 | } 725 | 726 | // Create vertex buffer. 727 | m_unwrapped_mesh.vbo = std::make_unique(GL_STATIC_DRAW, sizeof(LightmapVertex) * vertices.size(), vertices.data()); 728 | 729 | // Create index buffer. 730 | m_unwrapped_mesh.ibo = std::make_unique(GL_STATIC_DRAW, sizeof(uint32_t) * indices.size(), indices.data()); 731 | 732 | // Declare vertex attributes. 733 | dw::VertexAttrib attribs[] = { { 3, GL_FLOAT, false, 0 }, 734 | { 2, GL_FLOAT, false, offsetof(LightmapVertex, uv) }, 735 | { 2, GL_FLOAT, false, offsetof(LightmapVertex, lightmap_uv) }, 736 | { 3, GL_FLOAT, false, offsetof(LightmapVertex, normal) }, 737 | { 3, GL_FLOAT, false, offsetof(LightmapVertex, tangent) }, 738 | { 3, GL_FLOAT, false, offsetof(LightmapVertex, bitangent) } }; 739 | 740 | // Create vertex array. 741 | m_unwrapped_mesh.vao = std::make_unique(m_unwrapped_mesh.vbo.get(), m_unwrapped_mesh.ibo.get(), sizeof(LightmapVertex), 6, attribs); 742 | 743 | return true; 744 | } 745 | 746 | // ----------------------------------------------------------------------------------------------------------------------------------- 747 | 748 | bool lightmap_uv_unwrap(dw::Mesh* mesh) 749 | { 750 | std::vector positions(mesh->vertex_count()); 751 | std::vector normals(mesh->vertex_count()); 752 | std::vector uvs(mesh->vertex_count()); 753 | 754 | xatlas::Atlas* atlas = xatlas::Create(); 755 | 756 | int idx = 0; 757 | dw::Vertex* vertex_ptr = mesh->vertices(); 758 | uint32_t* index_ptr = mesh->indices(); 759 | 760 | for (int i = 0; i < mesh->vertex_count(); i++) 761 | { 762 | positions[i] = vertex_ptr[i].position; 763 | normals[i] = vertex_ptr[i].normal; 764 | uvs[i] = vertex_ptr[i].tex_coord; 765 | } 766 | 767 | for (int mesh_idx = 0; mesh_idx < mesh->sub_mesh_count(); mesh_idx++) 768 | { 769 | dw::SubMesh& submesh = mesh->sub_meshes()[mesh_idx]; 770 | 771 | xatlas::MeshDecl mesh_decl; 772 | 773 | mesh_decl.vertexCount = mesh->vertex_count(); 774 | mesh_decl.vertexPositionStride = sizeof(glm::vec3); 775 | mesh_decl.vertexPositionData = &positions[0]; 776 | mesh_decl.vertexNormalStride = sizeof(glm::vec3); 777 | mesh_decl.vertexNormalData = &normals[0]; 778 | mesh_decl.vertexUvStride = sizeof(glm::vec2); 779 | mesh_decl.vertexUvData = &uvs[0]; 780 | mesh_decl.indexCount = submesh.index_count; 781 | mesh_decl.indexData = &mesh->indices()[submesh.base_index]; 782 | mesh_decl.indexOffset = submesh.base_vertex; 783 | mesh_decl.indexFormat = xatlas::IndexFormat::UInt32; 784 | 785 | xatlas::AddMeshError::Enum error = xatlas::AddMesh(atlas, mesh_decl); 786 | 787 | if (error != xatlas::AddMeshError::Success) 788 | { 789 | xatlas::Destroy(atlas); 790 | DW_LOG_ERROR("Failed to add UV mesh to Lightmap Atlas"); 791 | return false; 792 | } 793 | } 794 | 795 | xatlas::ComputeCharts(atlas); 796 | xatlas::ParameterizeCharts(atlas); 797 | 798 | xatlas::PackOptions pack_options; 799 | 800 | pack_options.padding = LIGHTMAP_CHART_PADDING; 801 | pack_options.resolution = m_lightmap_size; 802 | 803 | xatlas::PackCharts(atlas, pack_options); 804 | 805 | bool status = create_lightmap_uv_unwrapped_mesh(atlas, mesh); 806 | xatlas::Destroy(atlas); 807 | 808 | return status; 809 | } 810 | 811 | // ----------------------------------------------------------------------------------------------------------------------------------- 812 | 813 | bool initialize_embree(dw::Mesh* mesh) 814 | { 815 | m_embree_device = rtcNewDevice(nullptr); 816 | 817 | RTCError embree_error = rtcGetDeviceError(m_embree_device); 818 | 819 | if (embree_error == RTC_ERROR_UNSUPPORTED_CPU) 820 | throw std::runtime_error("Your CPU does not meet the minimum requirements for embree"); 821 | else if (embree_error != RTC_ERROR_NONE) 822 | throw std::runtime_error("Failed to initialize embree!"); 823 | 824 | m_embree_scene = rtcNewScene(m_embree_device); 825 | 826 | rtcSetSceneFlags(m_embree_scene, RTC_SCENE_FLAG_ROBUST); 827 | 828 | m_embree_triangle_mesh = rtcNewGeometry(m_embree_device, RTC_GEOMETRY_TYPE_TRIANGLE); 829 | 830 | std::vector vertices(mesh->vertex_count()); 831 | 832 | m_unwrapped_mesh.vertex_colors.resize(mesh->index_count() / 3); 833 | 834 | std::vector indices(mesh->index_count()); 835 | uint32_t idx = 0; 836 | dw::Vertex* vertex_ptr = mesh->vertices(); 837 | uint32_t* index_ptr = mesh->indices(); 838 | 839 | for (int i = 0; i < mesh->vertex_count(); i++) 840 | vertices[i] = vertex_ptr[i].position; 841 | 842 | uint32_t tri_idx = 0; 843 | 844 | for (int i = 0; i < mesh->sub_mesh_count(); i++) 845 | { 846 | dw::SubMesh& submesh = mesh->sub_meshes()[i]; 847 | 848 | for (int j = submesh.base_index; j < (submesh.base_index + submesh.index_count); j++) 849 | indices[idx++] = submesh.base_vertex + index_ptr[j]; 850 | 851 | for (int j = 0; j < (submesh.index_count / 3); j++) 852 | m_unwrapped_mesh.vertex_colors[tri_idx++] = submesh.mat->albedo_value(); 853 | } 854 | 855 | void* data = rtcSetNewGeometryBuffer(m_embree_triangle_mesh, RTC_BUFFER_TYPE_VERTEX, 0, RTC_FORMAT_FLOAT3, sizeof(glm::vec3), mesh->vertex_count()); 856 | memcpy(data, vertices.data(), vertices.size() * sizeof(glm::vec3)); 857 | 858 | data = rtcSetNewGeometryBuffer(m_embree_triangle_mesh, RTC_BUFFER_TYPE_INDEX, 0, RTC_FORMAT_UINT3, 3 * sizeof(uint32_t), mesh->index_count() / 3); 859 | memcpy(data, indices.data(), indices.size() * sizeof(uint32_t)); 860 | 861 | rtcCommitGeometry(m_embree_triangle_mesh); 862 | rtcAttachGeometry(m_embree_scene, m_embree_triangle_mesh); 863 | rtcCommitScene(m_embree_scene); 864 | 865 | return true; 866 | } 867 | 868 | // ----------------------------------------------------------------------------------------------------------------------------------- 869 | 870 | void create_camera() 871 | { 872 | m_main_camera = std::make_unique(60.0f, 0.1f, CAMERA_FAR_PLANE, float(m_width) / float(m_height), glm::vec3(50.0f, 20.0f, 0.0f), glm::vec3(-1.0f, 0.0, 0.0f)); 873 | m_main_camera->set_rotatation_delta(glm::vec3(0.0f, -90.0f, 0.0f)); 874 | m_main_camera->update(); 875 | } 876 | 877 | // ----------------------------------------------------------------------------------------------------------------------------------- 878 | 879 | void render_mesh(LightmapMesh& mesh, glm::mat4 model, std::unique_ptr& program) 880 | { 881 | program->set_uniform("u_Model", model); 882 | 883 | // Bind vertex array. 884 | mesh.vao->bind(); 885 | 886 | for (uint32_t i = 0; i < mesh.submeshes.size(); i++) 887 | { 888 | LightmapSubMesh& submesh = mesh.submeshes[i]; 889 | 890 | if (program->set_uniform("s_Lightmap", 0)) 891 | { 892 | if (m_dilated && !m_bake_in_progress) 893 | m_lightmap_dilated_texture->bind(0); 894 | else 895 | m_lightmap_texture->bind(0); 896 | } 897 | 898 | program->set_uniform("u_Roughness", m_roughness); 899 | program->set_uniform("u_Metallic", m_metallic); 900 | program->set_uniform("u_Color", submesh.color); 901 | program->set_uniform("u_Direction", m_light_direction); 902 | program->set_uniform("u_LightColor", m_light_color); 903 | program->set_uniform("u_IndirectLighting", (int)m_indirect_lighting); 904 | program->set_uniform("u_AmbientIntensity", m_ambient_intensity); 905 | 906 | // Issue draw call. 907 | glDrawElementsBaseVertex(GL_TRIANGLES, submesh.index_count, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * submesh.base_index), submesh.base_vertex); 908 | } 909 | } 910 | 911 | // ----------------------------------------------------------------------------------------------------------------------------------- 912 | 913 | void render_scene(dw::Framebuffer* fbo, std::unique_ptr& program, int x, int y, int w, int h, GLenum cull_face, bool clear = true) 914 | { 915 | glEnable(GL_DEPTH_TEST); 916 | glDisable(GL_BLEND); 917 | 918 | if (cull_face == GL_NONE) 919 | glDisable(GL_CULL_FACE); 920 | else 921 | { 922 | glEnable(GL_CULL_FACE); 923 | glCullFace(cull_face); 924 | } 925 | 926 | if (fbo) 927 | fbo->bind(); 928 | else 929 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 930 | 931 | glViewport(x, y, w, h); 932 | 933 | if (clear) 934 | { 935 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 936 | glClearDepth(1.0); 937 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 938 | } 939 | 940 | // Bind shader program. 941 | program->use(); 942 | 943 | program->set_uniform("u_LightBias", m_shadow_bias); 944 | 945 | if (program->set_uniform("s_ShadowMap", 1)) 946 | m_shadow_map->bind(1); 947 | 948 | // Bind uniform buffers. 949 | m_global_ubo->bind_base(0); 950 | 951 | // Draw scene. 952 | render_mesh(m_unwrapped_mesh, m_transform, program); 953 | } 954 | 955 | // ----------------------------------------------------------------------------------------------------------------------------------- 956 | 957 | void visualize_lightmap() 958 | { 959 | glDisable(GL_DEPTH_TEST); 960 | glDisable(GL_CULL_FACE); 961 | glDisable(GL_BLEND); 962 | 963 | // Bind shader program. 964 | m_visualize_lightmap_program->use(); 965 | 966 | if (m_visualize_lightmap_program->set_uniform("s_Lightmap", 0)) 967 | m_lightmap_dilated_texture->bind(0); 968 | 969 | // Render fullscreen triangle 970 | glDrawArrays(GL_TRIANGLES, 0, 3); 971 | } 972 | 973 | // ----------------------------------------------------------------------------------------------------------------------------------- 974 | 975 | void visualize_atlas_submeshes() 976 | { 977 | if (m_enable_conservative_raster) 978 | { 979 | if (GLAD_GL_NV_conservative_raster) 980 | glEnable(GL_CONSERVATIVE_RASTERIZATION_NV); 981 | else if (GLAD_GL_INTEL_conservative_rasterization) 982 | glEnable(GL_INTEL_conservative_rasterization); 983 | } 984 | 985 | glDisable(GL_DEPTH_TEST); 986 | glDisable(GL_BLEND); 987 | 988 | glDisable(GL_CULL_FACE); 989 | 990 | if (m_highlight_wireframe) 991 | glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); 992 | 993 | // Bind shader program. 994 | m_visualize_submeshes_program->use(); 995 | 996 | // Bind vertex array. 997 | m_unwrapped_mesh.vao->bind(); 998 | 999 | for (uint32_t i = 0; i < m_unwrapped_mesh.submeshes.size(); i++) 1000 | { 1001 | LightmapSubMesh& submesh = m_unwrapped_mesh.submeshes[i]; 1002 | 1003 | m_visualize_submeshes_program->set_uniform("u_Color", m_unwrapped_mesh.submesh_colors[i]); 1004 | 1005 | // Issue draw call. 1006 | glDrawElementsBaseVertex(GL_TRIANGLES, submesh.index_count, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * submesh.base_index), submesh.base_vertex); 1007 | } 1008 | 1009 | if (m_enable_conservative_raster) 1010 | { 1011 | if (GLAD_GL_NV_conservative_raster) 1012 | glDisable(GL_CONSERVATIVE_RASTERIZATION_NV); 1013 | else if (GLAD_GL_INTEL_conservative_rasterization) 1014 | glDisable(GL_INTEL_conservative_rasterization); 1015 | } 1016 | 1017 | glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); 1018 | } 1019 | 1020 | // ----------------------------------------------------------------------------------------------------------------------------------- 1021 | 1022 | float drand48() 1023 | { 1024 | return m_distribution(m_generator); 1025 | } 1026 | 1027 | // ----------------------------------------------------------------------------------------------------------------------------------- 1028 | 1029 | glm::vec3 diffuse_lambert(glm::vec3 albedo) 1030 | { 1031 | return albedo; 1032 | } 1033 | 1034 | // ----------------------------------------------------------------------------------------------------------------------------------- 1035 | 1036 | bool valid_texel(glm::vec3 t) 1037 | { 1038 | return !(t.x == 0.0f && t.y == 0.0f && t.z == 0.0f); 1039 | } 1040 | 1041 | // ----------------------------------------------------------------------------------------------------------------------------------- 1042 | 1043 | void clear_lightmap() 1044 | { 1045 | for (int y = 0; y < m_lightmap_size; y++) 1046 | { 1047 | for (int x = 0; x < m_lightmap_size; x++) 1048 | m_framebuffer[m_lightmap_size * y + x] = glm::vec4(0.0f, 0.0f, 0.0f, 1.0f); 1049 | } 1050 | } 1051 | 1052 | // ----------------------------------------------------------------------------------------------------------------------------------- 1053 | 1054 | bool is_nan(glm::vec3 v) 1055 | { 1056 | glm::bvec3 b = glm::isnan(v); 1057 | return b.x || b.y || b.z; 1058 | } 1059 | 1060 | // ----------------------------------------------------------------------------------------------------------------------------------- 1061 | 1062 | glm::mat3 make_rotation_matrix(glm::vec3 z) 1063 | { 1064 | const glm::vec3 ref = glm::abs(glm::dot(z, glm::vec3(0, 1, 0))) > 0.99f ? glm::vec3(0, 0, 1) : glm::vec3(0, 1, 0); 1065 | 1066 | const glm::vec3 x = glm::normalize(glm::cross(ref, z)); 1067 | const glm::vec3 y = glm::cross(z, x); 1068 | 1069 | assert(!is_nan(x)); 1070 | assert(!is_nan(y)); 1071 | assert(!is_nan(z)); 1072 | 1073 | return { x, y, z }; 1074 | } 1075 | 1076 | // ----------------------------------------------------------------------------------------------------------------------------------- 1077 | 1078 | #undef max 1079 | 1080 | glm::vec3 sample_cosine_lobe_direction(glm::vec3 n) 1081 | { 1082 | glm::vec2 sample = glm::max(glm::vec2(0.00001f), glm::vec2(drand48(), drand48())); 1083 | 1084 | const float phi = 2.0f * M_PI * sample.y; 1085 | 1086 | const float cos_theta = sqrt(sample.x); 1087 | const float sin_theta = sqrt(1 - sample.x); 1088 | 1089 | glm::vec3 t = glm::vec3(sin_theta * cos(phi), sin_theta * sin(phi), cos_theta); 1090 | 1091 | assert(!is_nan(t)); 1092 | 1093 | return glm::normalize(make_rotation_matrix(n) * t); 1094 | } 1095 | 1096 | // ----------------------------------------------------------------------------------------------------------------------------------- 1097 | 1098 | void create_ray(glm::vec3 direction, glm::vec3 position, RTCRayHit& rayhit) 1099 | { 1100 | rayhit.ray.dir_x = direction.x; 1101 | rayhit.ray.dir_y = direction.y; 1102 | rayhit.ray.dir_z = direction.z; 1103 | 1104 | rayhit.ray.org_x = position.x; 1105 | rayhit.ray.org_y = position.y; 1106 | rayhit.ray.org_z = position.z; 1107 | 1108 | rayhit.ray.tnear = 0; 1109 | rayhit.ray.tfar = INFINITY; 1110 | rayhit.ray.mask = -1; 1111 | rayhit.ray.flags = 0; 1112 | rayhit.hit.geomID = RTC_INVALID_GEOMETRY_ID; 1113 | rayhit.hit.instID[0] = RTC_INVALID_GEOMETRY_ID; 1114 | } 1115 | 1116 | // ----------------------------------------------------------------------------------------------------------------------------------- 1117 | 1118 | bool is_triangle_back_facing(glm::vec3 n, glm::vec3 d) 1119 | { 1120 | return glm::dot(n, d) > 0.0f; 1121 | } 1122 | 1123 | // ----------------------------------------------------------------------------------------------------------------------------------- 1124 | 1125 | glm::vec3 evaluate_direct_lighting(RTCIntersectContext& context, glm::vec3 p, glm::vec3 n, glm::vec3 albedo) 1126 | { 1127 | const glm::vec3 l = -m_light_direction; 1128 | const glm::vec3 li = m_light_color; 1129 | 1130 | RTCRay rayhit; 1131 | 1132 | rayhit.dir_x = l.x; 1133 | rayhit.dir_y = l.y; 1134 | rayhit.dir_z = l.z; 1135 | 1136 | rayhit.org_x = p.x; 1137 | rayhit.org_y = p.y; 1138 | rayhit.org_z = p.z; 1139 | 1140 | rayhit.tnear = 0; 1141 | rayhit.tfar = INFINITY; 1142 | rayhit.mask = -1; 1143 | rayhit.flags = 0; 1144 | 1145 | rtcOccluded1(m_embree_scene, &context, &rayhit); 1146 | 1147 | // Is it visible? 1148 | if (rayhit.tfar == INFINITY) 1149 | return li * diffuse_lambert(albedo) * glm::max(glm::dot(n, l), 0.0f); 1150 | 1151 | return glm::vec3(0.0f); 1152 | } 1153 | 1154 | // ----------------------------------------------------------------------------------------------------------------------------------- 1155 | 1156 | glm::vec3 path_trace(glm::vec3 direction, glm::vec3 position, bool& gutter) 1157 | { 1158 | glm::vec3 color; 1159 | RTCRayHit rayhit; 1160 | 1161 | glm::vec3 p = position; 1162 | glm::vec3 n = direction; 1163 | glm::vec3 d = direction; 1164 | 1165 | p += n * m_offset; 1166 | 1167 | color = glm::vec3(0.0f); 1168 | glm::vec3 attenuation = glm::vec3(1.0f); 1169 | 1170 | for (int i = 0; i < m_num_bounces; i++) 1171 | { 1172 | RTCIntersectContext intersect_context; 1173 | rtcInitIntersectContext(&intersect_context); 1174 | 1175 | d = sample_cosine_lobe_direction(n); 1176 | 1177 | create_ray(d, p, rayhit); 1178 | 1179 | rtcIntersect1(m_embree_scene, &intersect_context, &rayhit); 1180 | 1181 | // Does intersect scene 1182 | if (rayhit.hit.geomID == RTC_INVALID_GEOMETRY_ID) 1183 | { 1184 | float sky_dir = d.y < 0.0f ? 0.0f : 1.0f; 1185 | return color + m_skybox.sample_sky(d) * sky_dir * attenuation; 1186 | } 1187 | 1188 | uint32_t v_idx = rayhit.hit.primID; 1189 | 1190 | const glm::vec3 albedo = m_unwrapped_mesh.vertex_colors[v_idx]; 1191 | 1192 | p = p + d * rayhit.ray.tfar; 1193 | n = glm::normalize(glm::vec3(rayhit.hit.Ng_x, rayhit.hit.Ng_y, rayhit.hit.Ng_z)); 1194 | 1195 | if (is_triangle_back_facing(n, d)) 1196 | { 1197 | if (i == 0) 1198 | gutter = true; 1199 | 1200 | break; 1201 | } 1202 | // Add bias to position 1203 | p += glm::sign(n) * abs(p * 0.0000002f); 1204 | 1205 | color += evaluate_direct_lighting(intersect_context, p, n, albedo) * attenuation; 1206 | 1207 | attenuation *= albedo; 1208 | } 1209 | 1210 | return color; 1211 | } 1212 | 1213 | // ----------------------------------------------------------------------------------------------------------------------------------- 1214 | 1215 | bool load_cached_lightmap() 1216 | { 1217 | auto ptr = dw::Texture2D::create_from_files("lightmap.hdr"); 1218 | 1219 | if (ptr) 1220 | { 1221 | m_lightmap_dilated_texture = std::unique_ptr(ptr); 1222 | return true; 1223 | } 1224 | else 1225 | { 1226 | m_lightmap_dilated_texture = std::make_unique(m_lightmap_size, m_lightmap_size, 1, 1, 1, GL_RGB32F, GL_RGB, GL_FLOAT); 1227 | m_lightmap_dilated_texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 1228 | 1229 | return false; 1230 | } 1231 | } 1232 | 1233 | // ----------------------------------------------------------------------------------------------------------------------------------- 1234 | 1235 | void write_lightmap() 1236 | { 1237 | m_lightmap_dilated_texture->save_to_disk("lightmap", 0, 0); 1238 | } 1239 | 1240 | // ----------------------------------------------------------------------------------------------------------------------------------- 1241 | 1242 | void finish_bake() 1243 | { 1244 | if (m_bake_in_progress) 1245 | { 1246 | if (m_thread_pool.is_done(m_bake_parent_task)) 1247 | { 1248 | m_bake_in_progress = false; 1249 | 1250 | m_lightmap_texture->set_data(0, 0, m_framebuffer.data()); 1251 | 1252 | std::unique_ptr m_lightmap_dilated_fbo = std::make_unique(); 1253 | m_lightmap_dilated_fbo->attach_render_target(0, m_lightmap_dilated_texture.get(), 0, 0); 1254 | 1255 | dilate(m_lightmap_texture, m_lightmap_dilated_fbo.get()); 1256 | 1257 | glFinish(); 1258 | 1259 | write_lightmap(); 1260 | } 1261 | else 1262 | m_lightmap_texture->set_data(0, 0, m_framebuffer.data()); 1263 | } 1264 | } 1265 | 1266 | // ----------------------------------------------------------------------------------------------------------------------------------- 1267 | 1268 | void bake_lightmap() 1269 | { 1270 | glFinish(); 1271 | 1272 | clear_lightmap(); 1273 | 1274 | dw::Task* tasks[16]; 1275 | 1276 | std::function bake_function = [=](void* data) { 1277 | BakeTaskArgs* args = (BakeTaskArgs*)data; 1278 | 1279 | for (int sample = 0; sample < m_num_samples; sample++) 1280 | { 1281 | for (int i = args->start_idx; i < args->end_idx; i++) 1282 | { 1283 | glm::vec4 current_color = m_framebuffer[m_lightmap_size * m_bake_points[i].coord.y + m_bake_points[i].coord.x]; 1284 | glm::vec3 color = current_color; 1285 | glm::vec3 normal = m_bake_points[i].direction; 1286 | glm::vec3 position = m_bake_points[i].position; 1287 | 1288 | bool is_gutter = false; 1289 | color += path_trace(normal, position, is_gutter) * m_sample_weight; 1290 | 1291 | float alpha = current_color.a; 1292 | 1293 | if (is_gutter) 1294 | alpha = 0.0f; 1295 | 1296 | m_framebuffer[m_lightmap_size * m_bake_points[i].coord.y + m_bake_points[i].coord.x] = glm::vec4(color, alpha); 1297 | m_baking_progress++; 1298 | } 1299 | } 1300 | }; 1301 | 1302 | uint32_t points_per_task = ceil(float(m_bake_points.size()) / float(m_thread_pool.num_worker_threads())); 1303 | uint32_t remaining = m_bake_points.size(); 1304 | 1305 | m_total_samples_to_bake = m_bake_points.size() * m_num_samples; 1306 | m_baking_progress = 0; 1307 | m_bake_in_progress = true; 1308 | m_sample_weight = 1.0f / float(m_num_samples); 1309 | 1310 | for (int i = 0; i < m_thread_pool.num_worker_threads(); i++) 1311 | { 1312 | tasks[i] = m_thread_pool.allocate(); 1313 | tasks[i]->function = bake_function; 1314 | 1315 | BakeTaskArgs* args = dw::task_data(tasks[i]); 1316 | 1317 | args->start_idx = points_per_task * i; 1318 | args->end_idx = args->start_idx + (i == (m_thread_pool.num_worker_threads() - 1) ? remaining : points_per_task); 1319 | 1320 | remaining -= points_per_task; 1321 | 1322 | if (i != 0) 1323 | { 1324 | m_thread_pool.add_as_child(tasks[0], tasks[i]); 1325 | m_thread_pool.enqueue(tasks[i]); 1326 | } 1327 | } 1328 | 1329 | m_thread_pool.enqueue(tasks[0]); 1330 | 1331 | m_bake_parent_task = tasks[0]; 1332 | } 1333 | 1334 | // ----------------------------------------------------------------------------------------------------------------------------------- 1335 | 1336 | void update_uniforms() 1337 | { 1338 | glm::vec3 light_camera_pos = m_light_target - m_light_direction * 200.0f; 1339 | glm::mat4 view = glm::lookAt(light_camera_pos, m_light_target, glm::vec3(0.0f, 1.0f, 0.0f)); 1340 | glm::mat4 proj = glm::ortho(-SHADOW_MAP_EXTENTS, SHADOW_MAP_EXTENTS, -SHADOW_MAP_EXTENTS, SHADOW_MAP_EXTENTS, 1.0f, LIGHT_FAR_PLANE); 1341 | 1342 | m_global_uniforms.light_view_proj = proj * view; 1343 | 1344 | void* ptr = m_global_ubo->map(GL_WRITE_ONLY); 1345 | memcpy(ptr, &m_global_uniforms, sizeof(GlobalUniforms)); 1346 | m_global_ubo->unmap(); 1347 | } 1348 | 1349 | // ----------------------------------------------------------------------------------------------------------------------------------- 1350 | 1351 | void update_transforms(dw::Camera* camera) 1352 | { 1353 | // Update camera matrices. 1354 | m_global_uniforms.view_proj = camera->m_projection * camera->m_view; 1355 | m_global_uniforms.cam_pos = glm::vec4(camera->m_position, 0.0f); 1356 | } 1357 | 1358 | // ----------------------------------------------------------------------------------------------------------------------------------- 1359 | 1360 | void update_camera() 1361 | { 1362 | dw::Camera* current = m_main_camera.get(); 1363 | 1364 | float forward_delta = m_heading_speed * m_delta; 1365 | float right_delta = m_sideways_speed * m_delta; 1366 | 1367 | current->set_translation_delta(current->m_forward, forward_delta); 1368 | current->set_translation_delta(current->m_right, right_delta); 1369 | 1370 | m_camera_x = m_mouse_delta_x * m_camera_sensitivity; 1371 | m_camera_y = m_mouse_delta_y * m_camera_sensitivity; 1372 | 1373 | if (m_mouse_look) 1374 | { 1375 | // Activate Mouse Look 1376 | current->set_rotatation_delta(glm::vec3((float)(m_camera_y), 1377 | (float)(m_camera_x), 1378 | (float)(0.0f))); 1379 | } 1380 | else 1381 | { 1382 | current->set_rotatation_delta(glm::vec3((float)(0), 1383 | (float)(0), 1384 | (float)(0))); 1385 | } 1386 | 1387 | current->update(); 1388 | update_transforms(current); 1389 | } 1390 | 1391 | // ----------------------------------------------------------------------------------------------------------------------------------- 1392 | 1393 | private: 1394 | // General GPU resources. 1395 | std::unique_ptr m_lightmap_fs; 1396 | std::unique_ptr m_dilate_fs; 1397 | std::unique_ptr m_mesh_fs; 1398 | std::unique_ptr m_visualize_lightmap_fs; 1399 | std::unique_ptr m_visualize_submeshes_fs; 1400 | std::unique_ptr m_depth_fs; 1401 | 1402 | std::unique_ptr m_lightmap_vs; 1403 | std::unique_ptr m_triangle_vs; 1404 | std::unique_ptr m_mesh_vs; 1405 | std::unique_ptr m_shadow_map_vs; 1406 | 1407 | std::unique_ptr m_lightmap_program; 1408 | std::unique_ptr m_dilate_program; 1409 | std::unique_ptr m_visualize_lightmap_program; 1410 | std::unique_ptr m_visualize_submeshes_program; 1411 | std::unique_ptr m_mesh_program; 1412 | std::unique_ptr m_shadow_map_program; 1413 | 1414 | std::unique_ptr m_shadow_map_fbo; 1415 | std::unique_ptr m_shadow_map; 1416 | std::unique_ptr m_lightmap_texture; 1417 | std::unique_ptr m_lightmap_dilated_texture; 1418 | 1419 | std::unique_ptr m_global_ubo; 1420 | 1421 | std::vector m_bake_points; 1422 | std::vector m_framebuffer; 1423 | 1424 | // Camera. 1425 | LightmapMesh m_unwrapped_mesh; 1426 | std::unique_ptr m_main_camera; 1427 | 1428 | GlobalUniforms m_global_uniforms; 1429 | 1430 | // Scene 1431 | glm::mat4 m_transform; 1432 | 1433 | // Camera controls. 1434 | bool m_mouse_look = false; 1435 | float m_heading_speed = 0.0f; 1436 | float m_sideways_speed = 0.0f; 1437 | float m_camera_sensitivity = 0.05f; 1438 | float m_camera_speed = 0.05f; 1439 | float m_offset = 0.1f; 1440 | bool m_debug_gui = true; 1441 | 1442 | // Lightmap settings 1443 | int m_num_samples = LIGHTMAP_SPP; 1444 | int m_num_bounces = LIGHTMAP_BOUNCES; 1445 | int m_lightmap_size = LIGHTMAP_TEXTURE_SIZE; 1446 | 1447 | // Embree structure 1448 | RTCDevice m_embree_device = nullptr; 1449 | RTCScene m_embree_scene = nullptr; 1450 | RTCGeometry m_embree_triangle_mesh = nullptr; 1451 | 1452 | bool m_enable_conservative_raster = true; 1453 | bool m_bilinear_filtering = true; 1454 | bool m_visualize_atlas = false; 1455 | bool m_highlight_submeshes = false; 1456 | bool m_highlight_wireframe = false; 1457 | bool m_dilated = true; 1458 | bool m_bake_in_progress = false; 1459 | 1460 | std::default_random_engine m_generator; 1461 | std::uniform_real_distribution m_distribution; 1462 | 1463 | glm::vec3 m_light_target; 1464 | glm::vec3 m_light_direction; 1465 | glm::vec3 m_light_color; 1466 | Skybox m_skybox; 1467 | 1468 | // Material 1469 | float m_roughness = 1.0f; 1470 | float m_metallic = 0.0f; 1471 | float m_ambient_intensity = 0.1f; 1472 | bool m_indirect_lighting = true; 1473 | 1474 | // Shadow Mapping. 1475 | float m_shadow_bias = 0.001f; 1476 | glm::mat4 m_light_view_proj; 1477 | 1478 | // Camera orientation. 1479 | float m_camera_x; 1480 | float m_camera_y; 1481 | 1482 | float m_sample_weight = 0.0f; 1483 | std::atomic m_baking_progress = 0; 1484 | uint32_t m_total_samples_to_bake = 0; 1485 | dw::Task* m_bake_parent_task = nullptr; 1486 | dw::ThreadPool m_thread_pool; 1487 | }; 1488 | 1489 | DW_DECLARE_MAIN(PrecomputedGI) -------------------------------------------------------------------------------- /src/shader/depth_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // MAIN ------------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | void main() 6 | { 7 | } 8 | 9 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/dilate_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | in vec2 FS_IN_TexCoord; 6 | 7 | // ------------------------------------------------------------------ 8 | // OUTPUT VARIABLES ------------------------------------------------ 9 | // ------------------------------------------------------------------ 10 | 11 | layout(location = 0) out vec4 FS_OUT_Color; 12 | 13 | // ------------------------------------------------------------------ 14 | // UNIFORMS -------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | uniform sampler2D s_Texture; 18 | 19 | // ------------------------------------------------------------------ 20 | // MAIN ------------------------------------------------------------ 21 | // ------------------------------------------------------------------ 22 | 23 | void main(void) 24 | { 25 | ivec2 size = textureSize(s_Texture, 0); 26 | vec2 pixel_offset = vec2(1.0 / float(size.x), 1.0 / float(size.y)); 27 | 28 | vec4 c = texture(s_Texture, FS_IN_TexCoord); 29 | 30 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord - pixel_offset); 31 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(0, -pixel_offset.y)); 32 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(pixel_offset.x, -pixel_offset.y)); 33 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(-pixel_offset.x, 0)); 34 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(pixel_offset.x, 0)); 35 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(-pixel_offset.x, pixel_offset.y)); 36 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + vec2(0, pixel_offset.y)); 37 | c = c.a > 0.0 ? c : texture(s_Texture, FS_IN_TexCoord + pixel_offset); 38 | 39 | FS_OUT_Color = c; 40 | } 41 | 42 | // ------------------------------------------------------------------ 43 | -------------------------------------------------------------------------------- /src/shader/fullscreen_triangle_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // OUTPUT VARIABLES ------------------------------------------------ 3 | // ------------------------------------------------------------------ 4 | 5 | out vec2 FS_IN_TexCoord; 6 | 7 | // ------------------------------------------------------------------ 8 | // MAIN ------------------------------------------------------------ 9 | // ------------------------------------------------------------------ 10 | 11 | void main(void) 12 | { 13 | float x = -1.0 + float((gl_VertexID & 1) << 2); 14 | float y = -1.0 + float((gl_VertexID & 2) << 1); 15 | FS_IN_TexCoord.x = (x + 1.0) * 0.5; 16 | FS_IN_TexCoord.y = (y + 1.0) * 0.5; 17 | gl_Position = vec4(x, y, 0.0, 1.0); 18 | } 19 | 20 | // ------------------------------------------------------------------ 21 | -------------------------------------------------------------------------------- /src/shader/lightmap_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | in vec3 FS_IN_Position; 6 | in vec3 FS_IN_Normal; 7 | 8 | // ------------------------------------------------------------------ 9 | // OUTPUT VARIABLES ------------------------------------------------ 10 | // ------------------------------------------------------------------ 11 | 12 | layout(location = 0) out vec4 FS_OUT_Position; 13 | layout(location = 1) out vec4 FS_OUT_Normal; 14 | 15 | // ------------------------------------------------------------------ 16 | // MAIN ------------------------------------------------------------ 17 | // ------------------------------------------------------------------ 18 | 19 | void main(void) 20 | { 21 | FS_OUT_Position = vec4(FS_IN_Position, 1.0); 22 | FS_OUT_Normal = vec4(normalize(FS_IN_Normal), 1.0); 23 | } 24 | 25 | // ------------------------------------------------------------------ 26 | -------------------------------------------------------------------------------- /src/shader/lightmap_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUTS VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec3 VS_IN_Position; 6 | layout(location = 1) in vec2 VS_IN_UV; 7 | layout(location = 2) in vec2 VS_IN_LightMapUV; 8 | layout(location = 3) in vec3 VS_IN_Normal; 9 | layout(location = 4) in vec3 VS_IN_Tangent; 10 | layout(location = 5) in vec3 VS_IN_Bitangent; 11 | 12 | // ------------------------------------------------------------------ 13 | // OUTPUT VARIABLES ------------------------------------------------ 14 | // ------------------------------------------------------------------ 15 | 16 | out vec3 FS_IN_Position; 17 | out vec3 FS_IN_Normal; 18 | 19 | // ------------------------------------------------------------------ 20 | // MAIN ------------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | void main() 24 | { 25 | FS_IN_Position = VS_IN_Position; 26 | FS_IN_Normal = VS_IN_Normal; 27 | 28 | vec2 clip_space_pos = 2.0 * VS_IN_LightMapUV - 1.0; 29 | 30 | gl_Position = vec4(clip_space_pos, 0.0, 1.0); 31 | } 32 | 33 | // ------------------------------------------------------------------ 34 | -------------------------------------------------------------------------------- /src/shader/mesh_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // OUTPUT VARIABLES ------------------------------------------------ 3 | // ------------------------------------------------------------------ 4 | 5 | out vec3 FS_OUT_Color; 6 | 7 | // ------------------------------------------------------------------ 8 | // INPUT VARIABLES ------------------------------------------------- 9 | // ------------------------------------------------------------------ 10 | 11 | in vec3 FS_IN_WorldPos; 12 | in vec3 FS_IN_Normal; 13 | in vec2 FS_IN_UV; 14 | in vec2 FS_IN_LightmapUV; 15 | in vec4 FS_IN_NDCFragPos; 16 | 17 | // ------------------------------------------------------------------ 18 | // UNIFORMS --------------------------------------------------------- 19 | // ------------------------------------------------------------------ 20 | 21 | uniform vec3 u_Color; 22 | uniform vec3 u_LightColor; 23 | uniform vec3 u_Direction; 24 | uniform sampler2D s_Lightmap; 25 | uniform sampler2D s_ShadowMap; 26 | uniform float u_LightBias; 27 | uniform float u_Roughness; 28 | uniform float u_Metallic; 29 | uniform float u_AmbientIntensity; 30 | uniform int u_IndirectLighting; 31 | 32 | layout(std140) uniform GlobalUniforms 33 | { 34 | mat4 view_proj; 35 | mat4 light_view_proj; 36 | vec4 cam_pos; 37 | }; 38 | 39 | const float PI = 3.14159265359; 40 | 41 | // ------------------------------------------------------------------ 42 | // FUNCTIONS ------------------------------------------------------- 43 | // ------------------------------------------------------------------ 44 | 45 | float distribution_ggx(vec3 N, vec3 H, float roughness) 46 | { 47 | float a = roughness * roughness; 48 | float a2 = a * a; 49 | float NdotH = max(dot(N, H), 0.0); 50 | float NdotH2 = NdotH * NdotH; 51 | 52 | float nom = a2; 53 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 54 | denom = PI * denom * denom; 55 | 56 | return nom / denom; 57 | } 58 | 59 | // ------------------------------------------------------------------ 60 | 61 | float geometry_schlick_ggx(float NdotV, float roughness) 62 | { 63 | float r = (roughness + 1.0); 64 | float k = (r * r) / 8.0; 65 | 66 | float nom = NdotV; 67 | float denom = NdotV * (1.0 - k) + k; 68 | 69 | return nom / denom; 70 | } 71 | 72 | // ------------------------------------------------------------------ 73 | 74 | float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) 75 | { 76 | float NdotV = max(dot(N, V), 0.0); 77 | float NdotL = max(dot(N, L), 0.0); 78 | float ggx2 = geometry_schlick_ggx(NdotV, roughness); 79 | float ggx1 = geometry_schlick_ggx(NdotL, roughness); 80 | 81 | return ggx1 * ggx2; 82 | } 83 | 84 | // ------------------------------------------------------------------ 85 | 86 | vec3 fresnel_schlick(float cosTheta, vec3 F0) 87 | { 88 | return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); 89 | } 90 | 91 | // ------------------------------------------------------------------ 92 | 93 | vec3 fresnel_schlick_roughness(float cosTheta, vec3 F0, float roughness) 94 | { 95 | return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); 96 | } 97 | 98 | // ------------------------------------------------------------------ 99 | 100 | vec3 linear_to_srgb(in vec3 color) 101 | { 102 | vec3 x = color * 12.92; 103 | vec3 y = 1.055 * pow(clamp(color, 0.0, 1.0), vec3(1.0 / 2.4)) - 0.055; 104 | 105 | vec3 clr = color; 106 | clr.r = color.r < 0.0031308 ? x.r : y.r; 107 | clr.g = color.g < 0.0031308 ? x.g : y.g; 108 | clr.b = color.b < 0.0031308 ? x.b : y.b; 109 | 110 | return clr; 111 | } 112 | 113 | // ------------------------------------------------------------------ 114 | 115 | vec3 exposed_color(vec3 color) 116 | { 117 | float exposure = -16.0; 118 | return exp2(exposure) * color; 119 | } 120 | 121 | // ------------------------------------------------------------------ 122 | 123 | float shadow_occlussion(vec3 p) 124 | { 125 | // Transform frag position into Light-space. 126 | vec4 light_space_pos = light_view_proj * vec4(p, 1.0); 127 | 128 | vec3 proj_coords = light_space_pos.xyz / light_space_pos.w; 129 | // transform to [0,1] range 130 | proj_coords = proj_coords * 0.5 + 0.5; 131 | // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) 132 | float closest_depth = texture(s_ShadowMap, proj_coords.xy).r; 133 | // get depth of current fragment from light's perspective 134 | float current_depth = proj_coords.z; 135 | // check whether current frag pos is in shadow 136 | float bias = u_LightBias; 137 | float shadow = current_depth - bias > closest_depth ? 1.0 : 0.0; 138 | 139 | return 1.0 - shadow; 140 | } 141 | 142 | // ------------------------------------------------------------------ 143 | // MAIN ------------------------------------------------------------- 144 | // ------------------------------------------------------------------ 145 | 146 | void main() 147 | { 148 | float frag_depth = (FS_IN_NDCFragPos.z / FS_IN_NDCFragPos.w) * 0.5 + 0.5; 149 | float shadow = shadow_occlussion(FS_IN_WorldPos); 150 | 151 | vec3 N = normalize(FS_IN_Normal); 152 | vec3 V = normalize(cam_pos.xyz - FS_IN_WorldPos); 153 | vec3 R = reflect(-V, N); 154 | 155 | vec3 F0 = vec3(0.04); 156 | F0 = mix(F0, u_Color, u_Metallic); 157 | 158 | // reflectance equation 159 | vec3 Lo = vec3(0.0); 160 | 161 | { 162 | vec3 L = -u_Direction; 163 | vec3 H = normalize(V + L); 164 | 165 | vec3 radiance = u_LightColor; 166 | 167 | // Cook-Torrance BRDF 168 | float NDF = distribution_ggx(N, H, u_Roughness); 169 | float G = geometry_smith(N, V, L, u_Roughness); 170 | vec3 F = fresnel_schlick(max(dot(H, V), 0.0), F0); 171 | 172 | vec3 nominator = NDF * G * F; 173 | float denominator = 4 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001; 174 | vec3 specular = nominator / denominator; 175 | 176 | vec3 kS = F; 177 | vec3 kD = vec3(1.0) - kS; 178 | kD *= 1.0 - u_Metallic; 179 | 180 | float NdotL = max(dot(N, L), 0.0); 181 | 182 | // add to outgoing radiance Lo 183 | Lo += (kD * u_Color / PI + specular) * radiance * NdotL; 184 | } 185 | 186 | // ambient lighting (we now use IBL as the ambient term) 187 | vec3 F = fresnel_schlick_roughness(max(dot(N, V), 0.0), F0, u_Roughness); 188 | 189 | vec3 kS = F; 190 | vec3 kD = 1.0 - kS; 191 | kD *= 1.0 - u_Metallic; 192 | 193 | vec3 irradiance = texture(s_Lightmap, FS_IN_LightmapUV).rgb; 194 | vec3 diffuse = irradiance * u_Color; 195 | 196 | vec3 ambient = (kD * diffuse); 197 | vec3 color = Lo * shadow; 198 | 199 | if (u_IndirectLighting == 1) 200 | color += ambient * u_AmbientIntensity; 201 | 202 | vec3 final_color = linear_to_srgb(exposed_color(color)); 203 | 204 | FS_OUT_Color = final_color; 205 | } 206 | 207 | // ------------------------------------------------------------------ 208 | -------------------------------------------------------------------------------- /src/shader/mesh_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES -------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec3 VS_IN_Position; 6 | layout(location = 1) in vec2 VS_IN_UV; 7 | layout(location = 2) in vec2 VS_IN_LightmapUV; 8 | layout(location = 3) in vec3 VS_IN_Normal; 9 | layout(location = 4) in vec3 VS_IN_Tangent; 10 | layout(location = 5) in vec3 VS_IN_Bitangent; 11 | 12 | // ------------------------------------------------------------------ 13 | // OUTPUT VARIABLES ------------------------------------------------- 14 | // ------------------------------------------------------------------ 15 | 16 | out vec3 FS_IN_WorldPos; 17 | out vec3 FS_IN_Normal; 18 | out vec2 FS_IN_UV; 19 | out vec2 FS_IN_LightmapUV; 20 | out vec4 FS_IN_NDCFragPos; 21 | 22 | // ------------------------------------------------------------------ 23 | // UNIFORMS --------------------------------------------------------- 24 | // ------------------------------------------------------------------ 25 | 26 | layout(std140) uniform GlobalUniforms 27 | { 28 | mat4 view_proj; 29 | mat4 light_view_proj; 30 | vec4 cam_pos; 31 | }; 32 | 33 | uniform mat4 u_Model; 34 | 35 | // ------------------------------------------------------------------ 36 | // MAIN ------------------------------------------------------------- 37 | // ------------------------------------------------------------------ 38 | 39 | void main() 40 | { 41 | vec4 world_pos = u_Model * vec4(VS_IN_Position, 1.0f); 42 | FS_IN_WorldPos = world_pos.xyz; 43 | FS_IN_Normal = normalize(normalize(mat3(u_Model) * VS_IN_Normal)); 44 | FS_IN_UV = VS_IN_UV; 45 | FS_IN_LightmapUV = VS_IN_LightmapUV; 46 | FS_IN_NDCFragPos = view_proj * world_pos; 47 | 48 | gl_Position = FS_IN_NDCFragPos; 49 | } 50 | 51 | // ------------------------------------------------------------------ 52 | -------------------------------------------------------------------------------- /src/shader/shadow_map_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUTS ----------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec3 VS_IN_Position; 6 | layout(location = 1) in vec2 VS_IN_UV; 7 | layout(location = 2) in vec2 VS_IN_LightmapUV; 8 | layout(location = 3) in vec3 VS_IN_Normal; 9 | layout(location = 4) in vec3 VS_IN_Tangent; 10 | layout(location = 5) in vec3 VS_IN_Bitangent; 11 | 12 | // ------------------------------------------------------------------ 13 | // UNIFORMS --------------------------------------------------------- 14 | // ------------------------------------------------------------------ 15 | 16 | layout(std140) uniform GlobalUniforms 17 | { 18 | mat4 view_proj; 19 | mat4 light_view_proj; 20 | vec4 cam_pos; 21 | }; 22 | 23 | uniform mat4 u_Model; 24 | uniform int u_CascadeIndex; 25 | 26 | // ------------------------------------------------------------------ 27 | // MAIN ------------------------------------------------------------- 28 | // ------------------------------------------------------------------ 29 | 30 | void main() 31 | { 32 | gl_Position = light_view_proj * u_Model * vec4(VS_IN_Position, 1.0); 33 | } 34 | 35 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/skybox_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // OUTPUTS --------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | out vec4 FS_OUT_Color; 6 | 7 | // ------------------------------------------------------------------ 8 | // INPUTS ---------------------------------------------------------- 9 | // ------------------------------------------------------------------ 10 | 11 | in vec3 FS_IN_TexCoord; 12 | 13 | // ------------------------------------------------------------------ 14 | // UNIFORMS -------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | uniform samplerCube s_Skybox; 18 | 19 | // ------------------------------------------------------------------ 20 | // FUNCTIONS ------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | vec3 linear_to_srgb(in vec3 color) 24 | { 25 | vec3 x = color * 12.92; 26 | vec3 y = 1.055 * pow(clamp(color, 0.0, 1.0), vec3(1.0 / 2.4)) - 0.055; 27 | 28 | vec3 clr = color; 29 | clr.r = color.r < 0.0031308 ? x.r : y.r; 30 | clr.g = color.g < 0.0031308 ? x.g : y.g; 31 | clr.b = color.b < 0.0031308 ? x.b : y.b; 32 | 33 | return clr; 34 | } 35 | 36 | // ------------------------------------------------------------------ 37 | 38 | vec3 exposed_color(vec3 color) 39 | { 40 | float exposure = -16.0; 41 | return exp2(exposure) * color; 42 | } 43 | 44 | // ------------------------------------------------------------------ 45 | // MAIN ------------------------------------------------------------ 46 | // ------------------------------------------------------------------ 47 | 48 | void main() 49 | { 50 | vec3 color = exposed_color(texture(s_Skybox, normalize(FS_IN_TexCoord)).rgb); 51 | FS_OUT_Color = vec4(linear_to_srgb(color), 1.0); 52 | } 53 | 54 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/skybox_vs.glsl: -------------------------------------------------------------------------------- 1 | // Using a fullscreen triangle for post-processing. 2 | // https://www.saschawillems.de/?page_id=2122 3 | // https://michaldrobot.com/2014/04/01/gcn-execution-patterns-in-full-screen-passes/ 4 | 5 | // ------------------------------------------------------------------ 6 | // OUTPUT VARIABLES ------------------------------------------------ 7 | // ------------------------------------------------------------------ 8 | 9 | out vec3 FS_IN_TexCoord; 10 | 11 | uniform mat4 u_CubemapInverseVP; 12 | 13 | // ------------------------------------------------------------------ 14 | // MAIN ------------------------------------------------------------ 15 | // ------------------------------------------------------------------ 16 | 17 | void main(void) 18 | { 19 | const vec3 vertices[4] = vec3[4](vec3(-1.0f, -1.0f, 1.0f), 20 | vec3(1.0f, -1.0f, 1.0f), 21 | vec3(-1.0f, 1.0f, 1.0f), 22 | vec3(1.0f, 1.0f, 1.0f)); 23 | 24 | vec4 clip_pos = vec4(vertices[gl_VertexID].xy, -1.0, 1.0); 25 | vec4 view_pos = u_CubemapInverseVP * clip_pos; 26 | 27 | vec3 dir = vec3(view_pos); 28 | dir = normalize(dir); 29 | 30 | FS_IN_TexCoord = dir; 31 | 32 | gl_Position = vec4(vertices[gl_VertexID], 1.0f); 33 | } 34 | 35 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/visualize_lightmap_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | in vec2 FS_IN_TexCoord; 6 | 7 | // ------------------------------------------------------------------ 8 | // OUTPUT VARIABLES ------------------------------------------------ 9 | // ------------------------------------------------------------------ 10 | 11 | out vec3 FS_OUT_Color; 12 | 13 | // ------------------------------------------------------------------ 14 | // UNIFORMS -------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | uniform sampler2D s_Lightmap; 18 | 19 | // ------------------------------------------------------------------ 20 | // MAIN ------------------------------------------------------------ 21 | // ------------------------------------------------------------------ 22 | 23 | void main(void) 24 | { 25 | FS_OUT_Color = texture(s_Lightmap, FS_IN_TexCoord).rgb; 26 | } 27 | 28 | // ------------------------------------------------------------------ 29 | -------------------------------------------------------------------------------- /src/shader/visualize_submeshes_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | in vec2 FS_IN_TexCoord; 6 | 7 | // ------------------------------------------------------------------ 8 | // OUTPUT VARIABLES ------------------------------------------------ 9 | // ------------------------------------------------------------------ 10 | 11 | out vec3 FS_OUT_Color; 12 | 13 | // ------------------------------------------------------------------ 14 | // UNIFORMS -------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | uniform vec3 u_Color; 18 | 19 | // ------------------------------------------------------------------ 20 | // MAIN ------------------------------------------------------------ 21 | // ------------------------------------------------------------------ 22 | 23 | void main(void) 24 | { 25 | FS_OUT_Color = u_Color; 26 | } 27 | 28 | // ------------------------------------------------------------------ 29 | -------------------------------------------------------------------------------- /src/skybox.cpp: -------------------------------------------------------------------------------- 1 | #define _USE_MATH_DEFINES 2 | #include 3 | #include "skybox.h" 4 | #include 5 | #include 6 | #include 7 | 8 | #define SKYBOX_TEXTURE_SIZE 1024 9 | #define FP16_SCALE 0.0009765625f; 10 | static const float Pi = 3.141592654f; 11 | static const float Pi2 = 6.283185307f; 12 | static const float Pi_2 = 1.570796327f; 13 | static const float Pi_4 = 0.7853981635f; 14 | 15 | // ----------------------------------------------------------------------------------------------------------------------------------- 16 | 17 | static float angle_between(const glm::vec3& dir0, const glm::vec3& dir1) 18 | { 19 | return glm::acos(glm::max(glm::dot(dir0, dir1), 0.00001f)); 20 | } 21 | 22 | // ----------------------------------------------------------------------------------------------------------------------------------- 23 | 24 | static glm::vec3 map_xys_to_direction(uint64_t x, uint64_t y, uint64_t s, uint64_t width, uint64_t height) 25 | { 26 | float u = ((x + 0.5f) / float(width)) * 2.0f - 1.0f; 27 | float v = ((y + 0.5f) / float(height)) * 2.0f - 1.0f; 28 | v *= -1.0f; 29 | 30 | glm::vec3 dir = glm::vec3(0.0f); 31 | 32 | // +x, -x, +y, -y, +z, -z 33 | switch (s) 34 | { 35 | case 0: 36 | dir = glm::normalize(glm::vec3(1.0f, v, -u)); 37 | break; 38 | case 1: 39 | dir = glm::normalize(glm::vec3(-1.0f, v, u)); 40 | break; 41 | case 2: 42 | dir = glm::normalize(glm::vec3(u, 1.0f, -v)); 43 | break; 44 | case 3: 45 | dir = glm::normalize(glm::vec3(u, -1.0f, v)); 46 | break; 47 | case 4: 48 | dir = glm::normalize(glm::vec3(u, v, 1.0f)); 49 | break; 50 | case 5: 51 | dir = glm::normalize(glm::vec3(-u, v, -1.0f)); 52 | break; 53 | } 54 | 55 | return dir; 56 | } 57 | 58 | // ----------------------------------------------------------------------------------------------------------------------------------- 59 | 60 | Skybox::~Skybox() 61 | { 62 | DW_SAFE_DELETE(m_state_r); 63 | DW_SAFE_DELETE(m_state_g); 64 | DW_SAFE_DELETE(m_state_b); 65 | } 66 | 67 | // ----------------------------------------------------------------------------------------------------------------------------------- 68 | 69 | bool Skybox::initialize(glm::vec3 sun_dir, glm::vec3 ground_albedo, float turbidity) 70 | { 71 | m_ground_albedo = ground_albedo; 72 | m_turbidity = turbidity; 73 | 74 | m_skybox_texture = std::make_unique(SKYBOX_TEXTURE_SIZE, SKYBOX_TEXTURE_SIZE, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 75 | m_skybox_texture->set_mag_filter(GL_NEAREST); 76 | m_skybox_texture->set_min_filter(GL_NEAREST); 77 | 78 | m_skybox_data.resize(6); 79 | 80 | for (int i = 0; i < 6; i++) 81 | m_skybox_data[i].resize(SKYBOX_TEXTURE_SIZE * SKYBOX_TEXTURE_SIZE); 82 | 83 | set_sun_dir(sun_dir); 84 | 85 | m_skybox_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/skybox_vs.glsl")); 86 | m_skybox_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/skybox_fs.glsl")); 87 | 88 | if (!m_skybox_vs || !m_skybox_fs) 89 | { 90 | DW_LOG_FATAL("Failed to create Shaders"); 91 | return false; 92 | } 93 | 94 | // Create general shader program 95 | dw::Shader* shaders[] = { m_skybox_vs.get(), m_skybox_fs.get() }; 96 | m_skybox_program = std::make_unique(2, shaders); 97 | 98 | if (!m_skybox_program) 99 | { 100 | DW_LOG_FATAL("Failed to create Shader Program"); 101 | return false; 102 | } 103 | 104 | return true; 105 | } 106 | 107 | // ----------------------------------------------------------------------------------------------------------------------------------- 108 | 109 | void Skybox::set_sun_dir(glm::vec3 sun_dir) 110 | { 111 | DW_SAFE_DELETE(m_state_r); 112 | DW_SAFE_DELETE(m_state_g); 113 | DW_SAFE_DELETE(m_state_b); 114 | 115 | sun_dir.y = glm::clamp(sun_dir.y, 0.0f, 1.0f); 116 | sun_dir = glm::normalize(sun_dir); 117 | float thetaS = angle_between(sun_dir, glm::vec3(0.0f, 1.0f, 0.0f)); 118 | float elevation = Pi_2 - thetaS; 119 | m_elevation = elevation; 120 | m_sun_dir = sun_dir; 121 | 122 | m_state_r = arhosek_rgb_skymodelstate_alloc_init(m_turbidity, m_ground_albedo.x, m_elevation); 123 | m_state_g = arhosek_rgb_skymodelstate_alloc_init(m_turbidity, m_ground_albedo.y, m_elevation); 124 | m_state_b = arhosek_rgb_skymodelstate_alloc_init(m_turbidity, m_ground_albedo.z, m_elevation); 125 | 126 | for (int s = 0; s < 6; s++) 127 | { 128 | for (int y = 0; y < SKYBOX_TEXTURE_SIZE; y++) 129 | { 130 | for (int x = 0; x < SKYBOX_TEXTURE_SIZE; x++) 131 | { 132 | glm::vec3 dir = map_xys_to_direction(x, y, s, SKYBOX_TEXTURE_SIZE, SKYBOX_TEXTURE_SIZE); 133 | glm::vec3 radiance = sample_sky(dir); 134 | uint64_t idx = (y * SKYBOX_TEXTURE_SIZE) + x; 135 | m_skybox_data[s][idx] = (glm::vec4(radiance, 1.0f)); 136 | } 137 | } 138 | 139 | m_skybox_texture->set_data(s, 0, 0, m_skybox_data[s].data()); 140 | } 141 | } 142 | 143 | // ----------------------------------------------------------------------------------------------------------------------------------- 144 | 145 | void Skybox::render(std::unique_ptr fbo, int w, int h, glm::mat4 proj, glm::mat4 view) 146 | { 147 | glEnable(GL_DEPTH_TEST); 148 | glDepthFunc(GL_LEQUAL); 149 | glDisable(GL_CULL_FACE); 150 | 151 | if (fbo) 152 | fbo->bind(); 153 | else 154 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 155 | 156 | glViewport(0, 0, w, h); 157 | 158 | // Bind shader program. 159 | m_skybox_program->use(); 160 | 161 | glm::mat4 inverse_vp = glm::inverse(proj * glm::mat4(glm::mat3(view))); 162 | 163 | m_skybox_program->set_uniform("u_CubemapInverseVP", inverse_vp); 164 | 165 | if (m_skybox_program->set_uniform("s_Skybox", 0)) 166 | m_skybox_texture->bind(0); 167 | 168 | // Render fullscreen triangle 169 | glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); 170 | 171 | glDepthFunc(GL_LESS); 172 | } 173 | 174 | // ----------------------------------------------------------------------------------------------------------------------------------- 175 | 176 | glm::vec3 Skybox::sample_sky(glm::vec3 dir) 177 | { 178 | float gamma = angle_between(dir, m_sun_dir); 179 | float theta = angle_between(dir, glm::vec3(0.0f, 1.0f, 0.0f)); 180 | 181 | glm::vec3 radiance; 182 | 183 | radiance.x = float(arhosek_tristim_skymodel_radiance(m_state_r, theta, gamma, 0)); 184 | radiance.y = float(arhosek_tristim_skymodel_radiance(m_state_g, theta, gamma, 1)); 185 | radiance.z = float(arhosek_tristim_skymodel_radiance(m_state_b, theta, gamma, 2)); 186 | 187 | // Multiply by standard luminous efficacy of 683 lm/W to bring us in line with the photometric 188 | // units used during rendering 189 | radiance *= 683.0f; 190 | 191 | //radiance *= FP16_SCALE; 192 | 193 | return radiance; 194 | } 195 | 196 | // ----------------------------------------------------------------------------------------------------------------------------------- -------------------------------------------------------------------------------- /src/skybox.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | struct ArHosekSkyModelState; 8 | 9 | struct Skybox 10 | { 11 | ~Skybox(); 12 | bool initialize(glm::vec3 sun_dir, glm::vec3 ground_albedo, float turbidity); 13 | void set_sun_dir(glm::vec3 sun_dir); 14 | void render(std::unique_ptr fbo, int w, int h, glm::mat4 proj, glm::mat4 view); 15 | glm::vec3 sample_sky(glm::vec3 dir); 16 | 17 | glm::vec3 m_sun_dir; 18 | float m_turbidity = 0.0f; 19 | glm::vec3 m_ground_albedo; 20 | float m_elevation = 0.0f; 21 | ArHosekSkyModelState* m_state_r = nullptr; 22 | ArHosekSkyModelState* m_state_g = nullptr; 23 | ArHosekSkyModelState* m_state_b = nullptr; 24 | std::unique_ptr m_skybox_texture; 25 | std::unique_ptr m_skybox_vs; 26 | std::unique_ptr m_skybox_fs; 27 | std::unique_ptr m_skybox_program; 28 | std::vector> m_skybox_data; 29 | }; --------------------------------------------------------------------------------