├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── data ├── hdr │ └── Arches_E_PineTree_3k.hdr ├── mesh │ └── teapot_smooth.obj ├── screenshot.jpg └── texture │ ├── checker.png │ ├── inscatter.raw │ ├── irradiance.raw │ └── transmittance.raw └── src ├── .clang-format ├── CMakeLists.txt ├── main.cpp └── shader ├── atmosphere.glsl ├── brdf_cs.glsl ├── equirectangular_to_cubemap_fs.glsl ├── equirectangular_to_cubemap_vs.glsl ├── fullscreen_triangle_vs.glsl ├── mesh_fs.glsl ├── mesh_vs.glsl ├── prefilter_cs.glsl ├── sh_add_cs.glsl ├── sh_projection_cs.glsl ├── sky_envmap_fs.glsl ├── sky_envmap_vs.glsl ├── sky_fs.glsl └── sky_vs.glsl /.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 | bin/ 36 | bin/* 37 | build/ 38 | build/* 39 | lib/ 40 | lib/* 41 | 42 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "external/dwSampleFramework"] 2 | path = external/dwSampleFramework 3 | url = https://github.com/diharaw/dwSampleFramework.git 4 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8 FATAL_ERROR) 2 | 3 | project("RuntimeIBL") 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/dwSampleFramework) 15 | 16 | include_directories("${DW_SAMPLE_FRAMEWORK_INCLUDES}") 17 | 18 | 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 | # RuntimeIBL 2 | 3 | An OpenGL sample application demonstrating runtime irradiance map generation using Spherical Harmonics and cubemap prefiltering using Importance Sampling for dynamic reflection probes. 4 | 5 | ## Screenshots 6 | 7 | ![RuntimeIBL](data/screenshot.jpg) 8 | 9 | ## Dependencies 10 | * [dwSampleFramework](https://github.com/diharaw/dwSampleFramework) 11 | 12 | ## License 13 | ``` 14 | Copyright (c) 2019 Dihara Wijetunga 15 | 16 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 17 | associated documentation files (the "Software"), to deal in the Software without restriction, 18 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 19 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 20 | subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in all copies or substantial 23 | portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 26 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 27 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 28 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 29 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 30 | ``` 31 | -------------------------------------------------------------------------------- /data/hdr/Arches_E_PineTree_3k.hdr: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/hdr/Arches_E_PineTree_3k.hdr -------------------------------------------------------------------------------- /data/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/screenshot.jpg -------------------------------------------------------------------------------- /data/texture/checker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/texture/checker.png -------------------------------------------------------------------------------- /data/texture/inscatter.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/texture/inscatter.raw -------------------------------------------------------------------------------- /data/texture/irradiance.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/texture/irradiance.raw -------------------------------------------------------------------------------- /data/texture/transmittance.raw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/runtime-ibl/5518d12ed8fd15c705bc147fc7bb19f970fa0517/data/texture/transmittance.raw -------------------------------------------------------------------------------- /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 | file(GLOB_RECURSE IBL_HEADERS ${PROJECT_SOURCE_DIR}/src/*.h) 6 | file(GLOB_RECURSE IBL_SOURCES ${PROJECT_SOURCE_DIR}/src/*.cpp) 7 | file(GLOB_RECURSE IBL_SHADERS ${PROJECT_SOURCE_DIR}/src/*.glsl) 8 | 9 | if(APPLE) 10 | add_executable(RuntimeIBL MACOSX_BUNDLE ${IBL_HEADERS} ${IBL_SOURCES}) 11 | set(MACOSX_BUNDLE_BUNDLE_NAME "com.dihara.ibl") 12 | else() 13 | add_executable(RuntimeIBL ${IBL_HEADERS} ${IBL_SOURCES}) 14 | endif() 15 | 16 | target_link_libraries(RuntimeIBL dwSampleFramework) 17 | 18 | if (APPLE) 19 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/shader $/RuntimeIBL.app/Contents/Resources/assets/shader) 20 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/mesh $/RuntimeIBL.app/Contents/Resources/mesh) 21 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/hdr $/RuntimeIBL.app/Contents/Resources/hdr) 22 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/texture $/RuntimeIBL.app/Contents/Resources/texture) 23 | else() 24 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/shader $/shader) 25 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/mesh $/mesh) 26 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/hdr $/hdr) 27 | add_custom_command(TARGET RuntimeIBL POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/data/texture $/texture) 28 | endif() 29 | 30 | if(CLANG_FORMAT_EXE) 31 | add_custom_target(clang-format-project-files COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${IBL_HEADERS} ${IBL_SOURCES} ${IBL_SHADERS}) 32 | endif() 33 | 34 | set_property(TARGET RuntimeIBL PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/$(Configuration)") -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #define _USE_MATH_DEFINES 12 | #include 13 | 14 | #define CAMERA_FAR_PLANE 10000.0f 15 | #define ENVIRONMENT_MAP_SIZE 512 16 | #define PREFILTER_MAP_SIZE 256 17 | #define PREFILTER_MIP_LEVELS 5 18 | #define IRRADIANCE_CUBEMAP_SIZE 128 19 | #define IRRADIANCE_WORK_GROUP_SIZE 8 20 | #define PREFILTER_WORK_GROUP_SIZE 8 21 | #define BRDF_WORK_GROUP_SIZE 8 22 | #define MAX_PREFILTER_SAMPLES 64 23 | #define BRDF_LUT_SIZE 512 24 | #define SH_INTERMEDIATE_SIZE (IRRADIANCE_CUBEMAP_SIZE / IRRADIANCE_WORK_GROUP_SIZE) 25 | 26 | struct SkyModel 27 | { 28 | const float SCALE = 1000.0f; 29 | const int TRANSMITTANCE_W = 256; 30 | const int TRANSMITTANCE_H = 64; 31 | 32 | const int IRRADIANCE_W = 64; 33 | const int IRRADIANCE_H = 16; 34 | 35 | const int INSCATTER_R = 32; 36 | const int INSCATTER_MU = 128; 37 | const int INSCATTER_MU_S = 32; 38 | const int INSCATTER_NU = 8; 39 | 40 | glm::vec3 m_beta_r = glm::vec3(0.0058f, 0.0135f, 0.0331f); 41 | glm::vec3 m_direction = glm::vec3(0.0f, 0.0f, 1.0f); 42 | float m_mie_g = 0.75f; 43 | float m_sun_intensity = 100.0f; 44 | dw::Texture2D* m_transmittance_t; 45 | dw::Texture2D* m_irradiance_t; 46 | dw::Texture3D* m_inscatter_t; 47 | float m_sun_angle = 0.0f; 48 | 49 | bool initialize() 50 | { 51 | m_transmittance_t = new_texture_2d(TRANSMITTANCE_W, TRANSMITTANCE_H); 52 | m_irradiance_t = new_texture_2d(IRRADIANCE_W, IRRADIANCE_H); 53 | m_inscatter_t = new_texture_3d(INSCATTER_MU_S * INSCATTER_NU, INSCATTER_MU, INSCATTER_R); 54 | 55 | FILE* transmittance = fopen("texture/transmittance.raw", "r"); 56 | 57 | if (transmittance) 58 | { 59 | size_t n = sizeof(float) * TRANSMITTANCE_W * TRANSMITTANCE_H * 4; 60 | 61 | void* data = malloc(n); 62 | fread(data, n, 1, transmittance); 63 | 64 | m_transmittance_t->set_data(0, 0, data); 65 | 66 | fclose(transmittance); 67 | free(data); 68 | } 69 | else 70 | return false; 71 | 72 | FILE* irradiance = fopen("texture/irradiance.raw", "r"); 73 | 74 | if (irradiance) 75 | { 76 | size_t n = sizeof(float) * IRRADIANCE_W * IRRADIANCE_H * 4; 77 | 78 | void* data = malloc(n); 79 | fread(data, n, 1, irradiance); 80 | 81 | m_irradiance_t->set_data(0, 0, data); 82 | 83 | fclose(irradiance); 84 | free(data); 85 | } 86 | else 87 | return false; 88 | 89 | FILE* inscatter = fopen("texture/inscatter.raw", "r"); 90 | 91 | if (inscatter) 92 | { 93 | size_t n = sizeof(float) * INSCATTER_MU_S * INSCATTER_NU * INSCATTER_MU * INSCATTER_R * 4; 94 | 95 | void* data = malloc(n); 96 | fread(data, n, 1, inscatter); 97 | 98 | m_inscatter_t->set_data(0, data); 99 | 100 | fclose(inscatter); 101 | free(data); 102 | } 103 | else 104 | return false; 105 | 106 | return true; 107 | } 108 | 109 | void set_render_uniforms(dw::Program* program) 110 | { 111 | m_direction = glm::normalize(glm::vec3(0.0f, sin(m_sun_angle), cos(m_sun_angle))); 112 | 113 | program->set_uniform("betaR", m_beta_r / SCALE); 114 | program->set_uniform("mieG", m_mie_g); 115 | program->set_uniform("SUN_INTENSITY", m_sun_intensity); 116 | program->set_uniform("EARTH_POS", glm::vec3(0.0f, 6360010.0f, 0.0f)); 117 | program->set_uniform("SUN_DIR", m_direction * -1.0f); 118 | 119 | if (program->set_uniform("s_Transmittance", 3)) 120 | m_transmittance_t->bind(3); 121 | 122 | if (program->set_uniform("s_Irradiance", 4)) 123 | m_irradiance_t->bind(4); 124 | 125 | if (program->set_uniform("s_Inscatter", 5)) 126 | m_inscatter_t->bind(5); 127 | } 128 | 129 | dw::Texture2D* new_texture_2d(int width, int height) 130 | { 131 | dw::Texture2D* texture = new dw::Texture2D(width, height, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 132 | texture->set_min_filter(GL_LINEAR); 133 | texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 134 | 135 | return texture; 136 | } 137 | 138 | dw::Texture3D* new_texture_3d(int width, int height, int depth) 139 | { 140 | dw::Texture3D* texture = new dw::Texture3D(width, height, depth, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 141 | texture->set_min_filter(GL_LINEAR); 142 | texture->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 143 | 144 | return texture; 145 | } 146 | }; 147 | 148 | class RuntimeIBL : public dw::Application 149 | { 150 | protected: 151 | // ----------------------------------------------------------------------------------------------------------------------------------- 152 | 153 | bool init(int argc, const char* argv[]) override 154 | { 155 | // Create GPU resources. 156 | if (!create_shaders()) 157 | return false; 158 | 159 | // Load mesh. 160 | if (!load_mesh()) 161 | return false; 162 | 163 | if (!load_environment_map()) 164 | return false; 165 | 166 | if (!create_framebuffer()) 167 | return false; 168 | 169 | if (!m_model.initialize()) 170 | return false; 171 | 172 | // Create camera. 173 | create_camera(); 174 | create_cube(); 175 | convert_env_map(); 176 | precompute_prefilter_constants(); 177 | generate_brdf_lut(); 178 | 179 | glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS); 180 | 181 | return true; 182 | } 183 | 184 | // ----------------------------------------------------------------------------------------------------------------------------------- 185 | 186 | void update(double delta) override 187 | { 188 | DW_SCOPED_SAMPLE("Render"); 189 | 190 | // Update camera. 191 | update_camera(); 192 | 193 | if (m_show_gui) 194 | ui(); 195 | 196 | render_envmap(); 197 | 198 | compute_spherical_harmonics(); 199 | 200 | prefilter_cubemap(); 201 | 202 | render_meshes(); 203 | 204 | render_skybox(); 205 | 206 | if (m_debug_mode) 207 | m_debug_draw.frustum(m_main_camera->m_view_projection, glm::vec3(0.0f, 1.0f, 0.0f)); 208 | 209 | // Render debug draw. 210 | m_debug_draw.render(nullptr, m_width, m_height, m_debug_mode ? m_debug_camera->m_view_projection : m_main_camera->m_view_projection); 211 | } 212 | 213 | // ----------------------------------------------------------------------------------------------------------------------------------- 214 | 215 | void shutdown() override 216 | { 217 | dw::Mesh::unload(m_mesh); 218 | } 219 | 220 | // ----------------------------------------------------------------------------------------------------------------------------------- 221 | 222 | void window_resized(int width, int height) override 223 | { 224 | // Override window resized method to update camera projection. 225 | m_main_camera->update_projection(60.0f, 0.1f, CAMERA_FAR_PLANE, float(m_width) / float(m_height)); 226 | m_debug_camera->update_projection(60.0f, 0.1f, CAMERA_FAR_PLANE * 2.0f, float(m_width) / float(m_height)); 227 | 228 | create_framebuffer(); 229 | convert_env_map(); 230 | } 231 | 232 | // ----------------------------------------------------------------------------------------------------------------------------------- 233 | 234 | void key_pressed(int code) override 235 | { 236 | // Handle forward movement. 237 | if (code == GLFW_KEY_W) 238 | m_heading_speed = m_camera_speed; 239 | else if (code == GLFW_KEY_S) 240 | m_heading_speed = -m_camera_speed; 241 | 242 | // Handle sideways movement. 243 | if (code == GLFW_KEY_A) 244 | m_sideways_speed = -m_camera_speed; 245 | else if (code == GLFW_KEY_D) 246 | m_sideways_speed = m_camera_speed; 247 | 248 | if (code == GLFW_KEY_K) 249 | m_debug_mode = !m_debug_mode; 250 | } 251 | 252 | // ----------------------------------------------------------------------------------------------------------------------------------- 253 | 254 | void key_released(int code) override 255 | { 256 | // Handle forward movement. 257 | if (code == GLFW_KEY_W || code == GLFW_KEY_S) 258 | m_heading_speed = 0.0f; 259 | 260 | // Handle sideways movement. 261 | if (code == GLFW_KEY_A || code == GLFW_KEY_D) 262 | m_sideways_speed = 0.0f; 263 | 264 | if (code == GLFW_KEY_G) 265 | m_show_gui = !m_show_gui; 266 | } 267 | 268 | // ----------------------------------------------------------------------------------------------------------------------------------- 269 | 270 | void mouse_pressed(int code) override 271 | { 272 | // Enable mouse look. 273 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 274 | m_mouse_look = true; 275 | } 276 | 277 | // ----------------------------------------------------------------------------------------------------------------------------------- 278 | 279 | void mouse_released(int code) override 280 | { 281 | // Disable mouse look. 282 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 283 | m_mouse_look = false; 284 | } 285 | 286 | // ----------------------------------------------------------------------------------------------------------------------------------- 287 | 288 | protected: 289 | // ----------------------------------------------------------------------------------------------------------------------------------- 290 | 291 | dw::AppSettings intial_app_settings() override 292 | { 293 | dw::AppSettings settings; 294 | 295 | settings.resizable = true; 296 | settings.maximized = false; 297 | settings.refresh_rate = 60; 298 | settings.major_ver = 4; 299 | settings.width = 1920; 300 | settings.height = 1080; 301 | settings.title = "Runtime IBL"; 302 | 303 | return settings; 304 | } 305 | 306 | // ----------------------------------------------------------------------------------------------------------------------------------- 307 | 308 | private: 309 | // ----------------------------------------------------------------------------------------------------------------------------------- 310 | 311 | void ui() 312 | { 313 | static const char* items[] = { "Environment Map", "Irradiance", "Prefiltered" }; 314 | 315 | if (ImGui::BeginCombo("Skybox", items[m_type], 0)) 316 | { 317 | for (int i = 0; i < 3; i++) 318 | { 319 | bool is_selected = (m_type == i); 320 | 321 | if (ImGui::Selectable(items[i], is_selected)) 322 | m_type = i; 323 | if (is_selected) 324 | ImGui::SetItemDefaultFocus(); 325 | } 326 | 327 | ImGui::EndCombo(); 328 | } 329 | 330 | ImGui::SliderAngle("Sun Angle", &m_model.m_sun_angle, 0.0f, -180.0f); 331 | 332 | if (m_type == 2) 333 | ImGui::SliderFloat("Roughness", &m_roughness, 0, PREFILTER_MIP_LEVELS - 1); 334 | 335 | ImGui::Separator(); 336 | 337 | ImGui::Text("Profiler"); 338 | 339 | dw::profiler::ui(); 340 | 341 | ImGui::Separator(); 342 | 343 | ImGui::Text("Prefilter Options"); 344 | 345 | int sample_count = m_sample_count; 346 | ImGui::SliderInt("Sample Count", &m_sample_count, 1, 64); 347 | 348 | if (sample_count != m_sample_count) 349 | precompute_prefilter_constants(); 350 | } 351 | 352 | // ----------------------------------------------------------------------------------------------------------------------------------- 353 | 354 | bool load_environment_map() 355 | { 356 | m_env_map = std::unique_ptr(dw::Texture2D::create_from_files("hdr/Arches_E_PineTree_3k.hdr", true, false)); 357 | m_env_map->set_min_filter(GL_LINEAR); 358 | m_env_map->set_mag_filter(GL_LINEAR); 359 | return true; 360 | } 361 | 362 | // ----------------------------------------------------------------------------------------------------------------------------------- 363 | 364 | bool create_shaders() 365 | { 366 | { 367 | // Create general shaders 368 | m_cubemap_convert_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/equirectangular_to_cubemap_vs.glsl")); 369 | m_cubemap_convert_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/equirectangular_to_cubemap_fs.glsl")); 370 | 371 | if (!m_cubemap_convert_vs->compiled() || !m_cubemap_convert_fs->compiled()) 372 | { 373 | DW_LOG_FATAL("Failed to create Shaders"); 374 | return false; 375 | } 376 | 377 | // Create general shader program 378 | dw::Shader* shaders[] = { m_cubemap_convert_vs.get(), m_cubemap_convert_fs.get() }; 379 | m_cubemap_convert_program = std::make_unique(2, shaders); 380 | 381 | if (!m_cubemap_convert_program) 382 | { 383 | DW_LOG_FATAL("Failed to create Shader Program"); 384 | return false; 385 | } 386 | } 387 | 388 | { 389 | // Create general shaders 390 | m_brdf_cs = std::unique_ptr(dw::Shader::create_from_file(GL_COMPUTE_SHADER, "shader/brdf_cs.glsl")); 391 | 392 | if (!m_brdf_cs->compiled()) 393 | { 394 | DW_LOG_FATAL("Failed to create Shaders"); 395 | return false; 396 | } 397 | 398 | // Create general shader program 399 | dw::Shader* shaders[] = { m_brdf_cs.get() }; 400 | m_brdf_program = std::make_unique(1, shaders); 401 | 402 | if (!m_brdf_program) 403 | { 404 | DW_LOG_FATAL("Failed to create Shader Program"); 405 | return false; 406 | } 407 | } 408 | 409 | { 410 | // Create general shaders 411 | m_prefilter_cs = std::unique_ptr(dw::Shader::create_from_file(GL_COMPUTE_SHADER, "shader/prefilter_cs.glsl")); 412 | 413 | if (!m_prefilter_cs->compiled()) 414 | { 415 | DW_LOG_FATAL("Failed to create Shaders"); 416 | return false; 417 | } 418 | 419 | // Create general shader program 420 | dw::Shader* shaders[] = { m_prefilter_cs.get() }; 421 | m_prefilter_program = std::make_unique(1, shaders); 422 | 423 | if (!m_prefilter_program) 424 | { 425 | DW_LOG_FATAL("Failed to create Shader Program"); 426 | return false; 427 | } 428 | 429 | m_prefilter_program->uniform_block_binding("u_SampleDirections", 0); 430 | } 431 | 432 | { 433 | // Create general shaders 434 | m_sh_projection_cs = std::unique_ptr(dw::Shader::create_from_file(GL_COMPUTE_SHADER, "shader/sh_projection_cs.glsl")); 435 | 436 | if (!m_sh_projection_cs->compiled()) 437 | { 438 | DW_LOG_FATAL("Failed to create Shaders"); 439 | return false; 440 | } 441 | 442 | // Create general shader program 443 | dw::Shader* shaders[] = { m_sh_projection_cs.get() }; 444 | m_sh_projection_program = std::make_unique(1, shaders); 445 | 446 | if (!m_sh_projection_program) 447 | { 448 | DW_LOG_FATAL("Failed to create Shader Program"); 449 | return false; 450 | } 451 | } 452 | 453 | { 454 | // Create general shaders 455 | m_sh_add_cs = std::unique_ptr(dw::Shader::create_from_file(GL_COMPUTE_SHADER, "shader/sh_add_cs.glsl")); 456 | 457 | if (!m_sh_add_cs->compiled()) 458 | { 459 | DW_LOG_FATAL("Failed to create Shaders"); 460 | return false; 461 | } 462 | 463 | // Create general shader program 464 | dw::Shader* shaders[] = { m_sh_add_cs.get() }; 465 | m_sh_add_program = std::make_unique(1, shaders); 466 | 467 | if (!m_sh_add_program) 468 | { 469 | DW_LOG_FATAL("Failed to create Shader Program"); 470 | return false; 471 | } 472 | } 473 | 474 | { 475 | // Create general shaders 476 | m_mesh_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/mesh_vs.glsl")); 477 | m_mesh_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/mesh_fs.glsl")); 478 | 479 | if (!m_mesh_vs->compiled() || !m_mesh_fs->compiled()) 480 | { 481 | DW_LOG_FATAL("Failed to create Shaders"); 482 | return false; 483 | } 484 | 485 | // Create general shader program 486 | dw::Shader* shaders[] = { m_mesh_vs.get(), m_mesh_fs.get() }; 487 | m_mesh_program = std::make_unique(2, shaders); 488 | 489 | if (!m_mesh_program) 490 | { 491 | DW_LOG_FATAL("Failed to create Shader Program"); 492 | return false; 493 | } 494 | } 495 | 496 | { 497 | m_cubemap_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/sky_vs.glsl")); 498 | m_cubemap_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/sky_fs.glsl")); 499 | 500 | if (!m_cubemap_vs->compiled() || !m_cubemap_fs->compiled()) 501 | { 502 | DW_LOG_FATAL("Failed to create Shaders"); 503 | return false; 504 | } 505 | 506 | // Create general shader program 507 | dw::Shader* shaders[] = { m_cubemap_vs.get(), m_cubemap_fs.get() }; 508 | m_cubemap_program = std::make_unique(2, shaders); 509 | 510 | if (!m_cubemap_program) 511 | { 512 | DW_LOG_FATAL("Failed to create Shader Program"); 513 | return false; 514 | } 515 | } 516 | 517 | { 518 | m_sky_envmap_vs = std::unique_ptr(dw::Shader::create_from_file(GL_VERTEX_SHADER, "shader/sky_envmap_vs.glsl")); 519 | m_sky_envmap_fs = std::unique_ptr(dw::Shader::create_from_file(GL_FRAGMENT_SHADER, "shader/sky_envmap_fs.glsl")); 520 | 521 | if (!m_sky_envmap_vs->compiled() || !m_sky_envmap_fs->compiled()) 522 | { 523 | DW_LOG_FATAL("Failed to create Shaders"); 524 | return false; 525 | } 526 | 527 | // Create general shader program 528 | dw::Shader* shaders[] = { m_sky_envmap_vs.get(), m_sky_envmap_fs.get() }; 529 | m_sky_envmap_program = std::make_unique(2, shaders); 530 | 531 | if (!m_sky_envmap_program) 532 | { 533 | DW_LOG_FATAL("Failed to create Shader Program"); 534 | return false; 535 | } 536 | } 537 | 538 | return true; 539 | } 540 | 541 | // ----------------------------------------------------------------------------------------------------------------------------------- 542 | 543 | bool create_framebuffer() 544 | { 545 | // uint32_t w, uint32_t h, uint32_t array_size, int32_t mip_levels, GLenum internal_format, GLenum format, GLenum type 546 | m_env_cubemap = std::make_unique(ENVIRONMENT_MAP_SIZE, ENVIRONMENT_MAP_SIZE, 1, 1, GL_RGB16F, GL_RGB, GL_HALF_FLOAT); 547 | m_cubemap_depth = std::make_unique(ENVIRONMENT_MAP_SIZE, ENVIRONMENT_MAP_SIZE, 1, 1, 1, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_FLOAT); 548 | m_prefilter_cubemap = std::make_unique(PREFILTER_MAP_SIZE, PREFILTER_MAP_SIZE, 1, PREFILTER_MIP_LEVELS, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); 549 | m_sh_intermediate = std::make_unique(SH_INTERMEDIATE_SIZE * 9, SH_INTERMEDIATE_SIZE, 6, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 550 | m_brdf_lut = std::make_unique(BRDF_LUT_SIZE, BRDF_LUT_SIZE, 1, 1, 1, GL_RG16F, GL_RG, GL_HALF_FLOAT); 551 | m_sh = std::make_unique(9, 1, 1, 1, 1, GL_RGBA32F, GL_RGBA, GL_FLOAT); 552 | 553 | m_sh_intermediate->set_min_filter(GL_NEAREST); 554 | m_sh_intermediate->set_mag_filter(GL_NEAREST); 555 | 556 | m_brdf_lut->set_min_filter(GL_NEAREST); 557 | m_brdf_lut->set_mag_filter(GL_NEAREST); 558 | 559 | m_sh->set_min_filter(GL_NEAREST); 560 | m_sh->set_mag_filter(GL_NEAREST); 561 | 562 | for (int i = 0; i < 6; i++) 563 | { 564 | m_cubemap_fbos.push_back(std::make_unique()); 565 | m_cubemap_fbos[i]->attach_render_target(0, m_env_cubemap.get(), i, 0, 0, true, true); 566 | m_cubemap_fbos[i]->attach_depth_stencil_target(m_cubemap_depth.get(), 0, 0); 567 | } 568 | 569 | return true; 570 | } 571 | 572 | // ----------------------------------------------------------------------------------------------------------------------------------- 573 | 574 | bool load_mesh() 575 | { 576 | m_mesh = dw::Mesh::load("mesh/teapot_smooth.obj"); 577 | 578 | if (!m_mesh) 579 | { 580 | DW_LOG_FATAL("Failed to load mesh!"); 581 | return false; 582 | } 583 | 584 | m_mesh_roughness = std::unique_ptr(dw::Texture2D::create_from_files("texture/checker.png", false, true)); 585 | 586 | return true; 587 | } 588 | 589 | // ----------------------------------------------------------------------------------------------------------------------------------- 590 | 591 | void create_camera() 592 | { 593 | m_main_camera = std::make_unique(60.0f, 0.1f, CAMERA_FAR_PLANE, float(m_width) / float(m_height), glm::vec3(0.0f, 5.0f, 150.0f), glm::vec3(0.0f, 0.0, -1.0f)); 594 | m_debug_camera = std::make_unique(60.0f, 0.1f, CAMERA_FAR_PLANE * 2.0f, float(m_width) / float(m_height), glm::vec3(0.0f, 5.0f, 150.0f), glm::vec3(0.0f, 0.0, -1.0f)); 595 | } 596 | 597 | // ----------------------------------------------------------------------------------------------------------------------------------- 598 | 599 | void render_mesh(dw::Mesh* mesh) 600 | { 601 | // Bind vertex array. 602 | mesh->mesh_vertex_array()->bind(); 603 | 604 | dw::SubMesh* submeshes = mesh->sub_meshes(); 605 | 606 | for (uint32_t i = 0; i < mesh->sub_mesh_count(); i++) 607 | { 608 | dw::SubMesh& submesh = submeshes[i]; 609 | // Issue draw call. 610 | glDrawElementsBaseVertex(GL_TRIANGLES, submesh.index_count, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * submesh.base_index), submesh.base_vertex); 611 | } 612 | } 613 | 614 | // ----------------------------------------------------------------------------------------------------------------------------------- 615 | 616 | void render_meshes() 617 | { 618 | DW_SCOPED_SAMPLE("Render Meshes"); 619 | 620 | glEnable(GL_DEPTH_TEST); 621 | glDisable(GL_CULL_FACE); 622 | 623 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 624 | glViewport(0, 0, m_width, m_height); 625 | 626 | glClearColor(0.5f, 0.5f, 0.5f, 1.0f); 627 | glClearDepth(1.0); 628 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 629 | 630 | // Bind shader program. 631 | m_mesh_program->use(); 632 | 633 | glm::mat4 m = glm::mat4(1.0f); 634 | m_mesh_program->set_uniform("u_Model", glm::scale(m, glm::vec3(0.5f))); 635 | m_mesh_program->set_uniform("u_View", m_main_camera->m_view); 636 | m_mesh_program->set_uniform("u_Projection", m_main_camera->m_projection); 637 | m_mesh_program->set_uniform("u_CameraPos", m_main_camera->m_position); 638 | 639 | if (m_mesh_program->set_uniform("s_BRDF", 0)) 640 | m_brdf_lut->bind(0); 641 | 642 | if (m_mesh_program->set_uniform("s_IrradianceSH", 1)) 643 | m_sh->bind(1); 644 | 645 | if (m_mesh_program->set_uniform("s_Prefiltered", 2)) 646 | m_prefilter_cubemap->bind(2); 647 | 648 | if (m_mesh_program->set_uniform("s_Roughness", 3)) 649 | m_mesh_roughness->bind(3); 650 | 651 | // Draw bunny. 652 | render_mesh(m_mesh); 653 | } 654 | 655 | // ----------------------------------------------------------------------------------------------------------------------------------- 656 | 657 | void render_envmap() 658 | { 659 | m_sky_envmap_program->use(); 660 | m_model.set_render_uniforms(m_sky_envmap_program.get()); 661 | 662 | for (int i = 0; i < 6; i++) 663 | { 664 | m_sky_envmap_program->set_uniform("u_Projection", m_capture_projection); 665 | m_sky_envmap_program->set_uniform("u_View", m_capture_views[i]); 666 | m_sky_envmap_program->set_uniform("u_CameraPos", m_main_camera->m_position); 667 | 668 | m_cubemap_fbos[i]->bind(); 669 | glViewport(0, 0, ENVIRONMENT_MAP_SIZE, ENVIRONMENT_MAP_SIZE); 670 | 671 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 672 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 673 | 674 | m_cube_vao->bind(); 675 | 676 | glDrawArrays(GL_TRIANGLES, 0, 36); 677 | } 678 | 679 | m_env_cubemap->generate_mipmaps(); 680 | } 681 | 682 | // ----------------------------------------------------------------------------------------------------------------------------------- 683 | 684 | void render_skybox() 685 | { 686 | DW_SCOPED_SAMPLE("Render Skybox"); 687 | 688 | glEnable(GL_DEPTH_TEST); 689 | glDepthFunc(GL_LEQUAL); 690 | glDisable(GL_CULL_FACE); 691 | 692 | m_cubemap_program->use(); 693 | m_cube_vao->bind(); 694 | 695 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 696 | glViewport(0, 0, m_width, m_height); 697 | 698 | m_cubemap_program->set_uniform("u_Roughness", m_roughness); 699 | m_cubemap_program->set_uniform("u_Type", m_type); 700 | m_cubemap_program->set_uniform("u_View", m_main_camera->m_view); 701 | m_cubemap_program->set_uniform("u_Projection", m_main_camera->m_projection); 702 | m_cubemap_program->set_uniform("u_CameraPos", m_main_camera->m_position); 703 | 704 | if (m_cubemap_program->set_uniform("s_Cubemap", 0)) 705 | m_env_cubemap->bind(0); 706 | 707 | if (m_cubemap_program->set_uniform("s_Prefilter", 1)) 708 | m_prefilter_cubemap->bind(1); 709 | 710 | if (m_cubemap_program->set_uniform("s_SH", 2)) 711 | m_sh->bind(2); 712 | 713 | glDrawArrays(GL_TRIANGLES, 0, 36); 714 | 715 | glDepthFunc(GL_LESS); 716 | } 717 | 718 | // ----------------------------------------------------------------------------------------------------------------------------------- 719 | 720 | void convert_env_map() 721 | { 722 | m_cubemap_convert_program->use(); 723 | m_cube_vao->bind(); 724 | 725 | for (int i = 0; i < 6; i++) 726 | { 727 | m_cubemap_fbos[i]->bind(); 728 | 729 | glViewport(0, 0, ENVIRONMENT_MAP_SIZE, ENVIRONMENT_MAP_SIZE); 730 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 731 | 732 | m_cubemap_convert_program->set_uniform("u_Projection", m_capture_projection); 733 | m_cubemap_convert_program->set_uniform("u_View", m_capture_views[i]); 734 | m_cubemap_convert_program->set_uniform("u_CameraPos", m_main_camera->m_position); 735 | 736 | if (m_cubemap_convert_program->set_uniform("s_EnvMap", 0)) 737 | m_env_map->bind(0); 738 | 739 | glDrawArrays(GL_TRIANGLES, 0, 36); 740 | } 741 | 742 | m_env_cubemap->generate_mipmaps(); 743 | } 744 | 745 | // ----------------------------------------------------------------------------------------------------------------------------------- 746 | 747 | void compute_spherical_harmonics() 748 | { 749 | DW_SCOPED_SAMPLE("Compute Spherical Harmonics"); 750 | 751 | m_sh_projection_program->use(); 752 | 753 | m_sh_projection_program->set_uniform("u_Width", (float)m_env_cubemap->width() / 4.0f); 754 | m_sh_projection_program->set_uniform("u_Height", (float)m_env_cubemap->height() / 4.0f); 755 | 756 | if (m_sh_projection_program->set_uniform("s_Cubemap", 1)) 757 | m_env_cubemap->bind(1); 758 | 759 | m_sh_intermediate->bind_image(0, 0, 0, GL_WRITE_ONLY, GL_RGBA32F); 760 | 761 | glDispatchCompute(IRRADIANCE_CUBEMAP_SIZE / IRRADIANCE_WORK_GROUP_SIZE, IRRADIANCE_CUBEMAP_SIZE / IRRADIANCE_WORK_GROUP_SIZE, 6); 762 | 763 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 764 | 765 | m_sh_add_program->use(); 766 | 767 | m_sh->bind_image(0, 0, 0, GL_WRITE_ONLY, GL_RGBA32F); 768 | 769 | if (m_sh_add_program->set_uniform("s_SHIntermediate", 1)) 770 | m_sh_intermediate->bind(1); 771 | 772 | glDispatchCompute(9, 1, 1); 773 | 774 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 775 | } 776 | 777 | // ----------------------------------------------------------------------------------------------------------------------------------- 778 | 779 | void prefilter_cubemap() 780 | { 781 | DW_SCOPED_SAMPLE("Prefilter"); 782 | 783 | m_prefilter_program->use(); 784 | 785 | if (m_prefilter_program->set_uniform("s_EnvMap", 1)) 786 | m_env_cubemap->bind(1); 787 | 788 | int32_t start_level = (ENVIRONMENT_MAP_SIZE / PREFILTER_MAP_SIZE) - 1; 789 | m_prefilter_program->set_uniform("u_StartMipLevel", start_level); 790 | 791 | for (int mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) 792 | { 793 | m_sample_directions[mip]->bind_base(0); 794 | 795 | uint32_t mip_width = PREFILTER_MAP_SIZE * std::pow(0.5, mip); 796 | uint32_t mip_height = PREFILTER_MAP_SIZE * std::pow(0.5, mip); 797 | 798 | float roughness = (float)mip / (float)(PREFILTER_MIP_LEVELS - 1); 799 | m_prefilter_program->set_uniform("u_Roughness", roughness); 800 | m_prefilter_program->set_uniform("u_SampleCount", m_sample_count); 801 | m_prefilter_program->set_uniform("u_Width", float(mip_width)); 802 | m_prefilter_program->set_uniform("u_Height", float(mip_height)); 803 | 804 | m_prefilter_cubemap->bind_image(0, mip, 0, GL_WRITE_ONLY, GL_RGBA16F); 805 | 806 | glDispatchCompute(mip_width / PREFILTER_WORK_GROUP_SIZE, mip_height / PREFILTER_WORK_GROUP_SIZE, 6); 807 | } 808 | 809 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 810 | } 811 | 812 | // ----------------------------------------------------------------------------------------------------------------------------------- 813 | 814 | void generate_brdf_lut() 815 | { 816 | m_brdf_program->use(); 817 | 818 | m_brdf_lut->bind_image(0, 0, 0, GL_WRITE_ONLY, GL_RG16F); 819 | 820 | glDispatchCompute(BRDF_LUT_SIZE / BRDF_WORK_GROUP_SIZE, BRDF_LUT_SIZE / BRDF_WORK_GROUP_SIZE, 1); 821 | 822 | glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); 823 | } 824 | 825 | // ----------------------------------------------------------------------------------------------------------------------------------- 826 | 827 | void update_camera() 828 | { 829 | dw::Camera* current = m_main_camera.get(); 830 | 831 | if (m_debug_mode) 832 | current = m_debug_camera.get(); 833 | 834 | float forward_delta = m_heading_speed * m_delta; 835 | float right_delta = m_sideways_speed * m_delta; 836 | 837 | current->set_translation_delta(current->m_forward, forward_delta); 838 | current->set_translation_delta(current->m_right, right_delta); 839 | 840 | m_camera_x = m_mouse_delta_x * m_camera_sensitivity; 841 | m_camera_y = m_mouse_delta_y * m_camera_sensitivity; 842 | 843 | if (m_mouse_look) 844 | { 845 | // Activate Mouse Look 846 | current->set_rotatation_delta(glm::vec3((float)(m_camera_y), 847 | (float)(m_camera_x), 848 | (float)(0.0f))); 849 | } 850 | else 851 | { 852 | current->set_rotatation_delta(glm::vec3((float)(0), 853 | (float)(0), 854 | (float)(0))); 855 | } 856 | 857 | current->update(); 858 | } 859 | 860 | // ----------------------------------------------------------------------------------------------------------------------------------- 861 | 862 | void create_cube() 863 | { 864 | float vertices[] = { 865 | // back face 866 | -1.0f, 867 | -1.0f, 868 | -1.0f, 869 | 0.0f, 870 | 0.0f, 871 | -1.0f, 872 | 0.0f, 873 | 0.0f, // bottom-left 874 | 1.0f, 875 | 1.0f, 876 | -1.0f, 877 | 0.0f, 878 | 0.0f, 879 | -1.0f, 880 | 1.0f, 881 | 1.0f, // top-right 882 | 1.0f, 883 | -1.0f, 884 | -1.0f, 885 | 0.0f, 886 | 0.0f, 887 | -1.0f, 888 | 1.0f, 889 | 0.0f, // bottom-right 890 | 1.0f, 891 | 1.0f, 892 | -1.0f, 893 | 0.0f, 894 | 0.0f, 895 | -1.0f, 896 | 1.0f, 897 | 1.0f, // top-right 898 | -1.0f, 899 | -1.0f, 900 | -1.0f, 901 | 0.0f, 902 | 0.0f, 903 | -1.0f, 904 | 0.0f, 905 | 0.0f, // bottom-left 906 | -1.0f, 907 | 1.0f, 908 | -1.0f, 909 | 0.0f, 910 | 0.0f, 911 | -1.0f, 912 | 0.0f, 913 | 1.0f, // top-left 914 | // front face 915 | -1.0f, 916 | -1.0f, 917 | 1.0f, 918 | 0.0f, 919 | 0.0f, 920 | 1.0f, 921 | 0.0f, 922 | 0.0f, // bottom-left 923 | 1.0f, 924 | -1.0f, 925 | 1.0f, 926 | 0.0f, 927 | 0.0f, 928 | 1.0f, 929 | 1.0f, 930 | 0.0f, // bottom-right 931 | 1.0f, 932 | 1.0f, 933 | 1.0f, 934 | 0.0f, 935 | 0.0f, 936 | 1.0f, 937 | 1.0f, 938 | 1.0f, // top-right 939 | 1.0f, 940 | 1.0f, 941 | 1.0f, 942 | 0.0f, 943 | 0.0f, 944 | 1.0f, 945 | 1.0f, 946 | 1.0f, // top-right 947 | -1.0f, 948 | 1.0f, 949 | 1.0f, 950 | 0.0f, 951 | 0.0f, 952 | 1.0f, 953 | 0.0f, 954 | 1.0f, // top-left 955 | -1.0f, 956 | -1.0f, 957 | 1.0f, 958 | 0.0f, 959 | 0.0f, 960 | 1.0f, 961 | 0.0f, 962 | 0.0f, // bottom-left 963 | // left face 964 | -1.0f, 965 | 1.0f, 966 | 1.0f, 967 | -1.0f, 968 | 0.0f, 969 | 0.0f, 970 | 1.0f, 971 | 0.0f, // top-right 972 | -1.0f, 973 | 1.0f, 974 | -1.0f, 975 | -1.0f, 976 | 0.0f, 977 | 0.0f, 978 | 1.0f, 979 | 1.0f, // top-left 980 | -1.0f, 981 | -1.0f, 982 | -1.0f, 983 | -1.0f, 984 | 0.0f, 985 | 0.0f, 986 | 0.0f, 987 | 1.0f, // bottom-left 988 | -1.0f, 989 | -1.0f, 990 | -1.0f, 991 | -1.0f, 992 | 0.0f, 993 | 0.0f, 994 | 0.0f, 995 | 1.0f, // bottom-left 996 | -1.0f, 997 | -1.0f, 998 | 1.0f, 999 | -1.0f, 1000 | 0.0f, 1001 | 0.0f, 1002 | 0.0f, 1003 | 0.0f, // bottom-right 1004 | -1.0f, 1005 | 1.0f, 1006 | 1.0f, 1007 | -1.0f, 1008 | 0.0f, 1009 | 0.0f, 1010 | 1.0f, 1011 | 0.0f, // top-right 1012 | // right face 1013 | 1.0f, 1014 | 1.0f, 1015 | 1.0f, 1016 | 1.0f, 1017 | 0.0f, 1018 | 0.0f, 1019 | 1.0f, 1020 | 0.0f, // top-left 1021 | 1.0f, 1022 | -1.0f, 1023 | -1.0f, 1024 | 1.0f, 1025 | 0.0f, 1026 | 0.0f, 1027 | 0.0f, 1028 | 1.0f, // bottom-right 1029 | 1.0f, 1030 | 1.0f, 1031 | -1.0f, 1032 | 1.0f, 1033 | 0.0f, 1034 | 0.0f, 1035 | 1.0f, 1036 | 1.0f, // top-right 1037 | 1.0f, 1038 | -1.0f, 1039 | -1.0f, 1040 | 1.0f, 1041 | 0.0f, 1042 | 0.0f, 1043 | 0.0f, 1044 | 1.0f, // bottom-right 1045 | 1.0f, 1046 | 1.0f, 1047 | 1.0f, 1048 | 1.0f, 1049 | 0.0f, 1050 | 0.0f, 1051 | 1.0f, 1052 | 0.0f, // top-left 1053 | 1.0f, 1054 | -1.0f, 1055 | 1.0f, 1056 | 1.0f, 1057 | 0.0f, 1058 | 0.0f, 1059 | 0.0f, 1060 | 0.0f, // bottom-left 1061 | // bottom face 1062 | -1.0f, 1063 | -1.0f, 1064 | -1.0f, 1065 | 0.0f, 1066 | -1.0f, 1067 | 0.0f, 1068 | 0.0f, 1069 | 1.0f, // top-right 1070 | 1.0f, 1071 | -1.0f, 1072 | -1.0f, 1073 | 0.0f, 1074 | -1.0f, 1075 | 0.0f, 1076 | 1.0f, 1077 | 1.0f, // top-left 1078 | 1.0f, 1079 | -1.0f, 1080 | 1.0f, 1081 | 0.0f, 1082 | -1.0f, 1083 | 0.0f, 1084 | 1.0f, 1085 | 0.0f, // bottom-left 1086 | 1.0f, 1087 | -1.0f, 1088 | 1.0f, 1089 | 0.0f, 1090 | -1.0f, 1091 | 0.0f, 1092 | 1.0f, 1093 | 0.0f, // bottom-left 1094 | -1.0f, 1095 | -1.0f, 1096 | 1.0f, 1097 | 0.0f, 1098 | -1.0f, 1099 | 0.0f, 1100 | 0.0f, 1101 | 0.0f, // bottom-right 1102 | -1.0f, 1103 | -1.0f, 1104 | -1.0f, 1105 | 0.0f, 1106 | -1.0f, 1107 | 0.0f, 1108 | 0.0f, 1109 | 1.0f, // top-right 1110 | // top face 1111 | -1.0f, 1112 | 1.0f, 1113 | -1.0f, 1114 | 0.0f, 1115 | 1.0f, 1116 | 0.0f, 1117 | 0.0f, 1118 | 1.0f, // top-left 1119 | 1.0f, 1120 | 1.0f, 1121 | 1.0f, 1122 | 0.0f, 1123 | 1.0f, 1124 | 0.0f, 1125 | 1.0f, 1126 | 0.0f, // bottom-right 1127 | 1.0f, 1128 | 1.0f, 1129 | -1.0f, 1130 | 0.0f, 1131 | 1.0f, 1132 | 0.0f, 1133 | 1.0f, 1134 | 1.0f, // top-right 1135 | 1.0f, 1136 | 1.0f, 1137 | 1.0f, 1138 | 0.0f, 1139 | 1.0f, 1140 | 0.0f, 1141 | 1.0f, 1142 | 0.0f, // bottom-right 1143 | -1.0f, 1144 | 1.0f, 1145 | -1.0f, 1146 | 0.0f, 1147 | 1.0f, 1148 | 0.0f, 1149 | 0.0f, 1150 | 1.0f, // top-left 1151 | -1.0f, 1152 | 1.0f, 1153 | 1.0f, 1154 | 0.0f, 1155 | 1.0f, 1156 | 0.0f, 1157 | 0.0f, 1158 | 0.0f // bottom-left 1159 | }; 1160 | 1161 | m_cube_vbo = std::make_unique(GL_STATIC_DRAW, sizeof(vertices), vertices); 1162 | 1163 | if (!m_cube_vbo) 1164 | DW_LOG_ERROR("Failed to create Vertex Buffer"); 1165 | 1166 | // Declare vertex attributes. 1167 | dw::VertexAttrib attribs[] = { 1168 | { 3, GL_FLOAT, false, 0 }, 1169 | { 3, GL_FLOAT, false, (3 * sizeof(float)) }, 1170 | { 2, GL_FLOAT, false, (6 * sizeof(float)) } 1171 | }; 1172 | 1173 | // Create vertex array. 1174 | m_cube_vao = std::make_unique(m_cube_vbo.get(), nullptr, (8 * sizeof(float)), 3, attribs); 1175 | 1176 | m_capture_projection = glm::perspective(glm::radians(90.0f), 1.0f, 0.1f, 10.0f); 1177 | m_capture_views = { 1178 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), 1179 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(-1.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f)), 1180 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f)), 1181 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f)), 1182 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, -1.0f, 0.0f)), 1183 | glm::lookAt(glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, -1.0f), glm::vec3(0.0f, -1.0f, 0.0f)) 1184 | }; 1185 | } 1186 | 1187 | // ----------------------------------------------------------------------------------------------------------------------------------- 1188 | 1189 | float radical_inverse_vdc(uint32_t bits) 1190 | { 1191 | bits = (bits << 16u) | (bits >> 16u); 1192 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 1193 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 1194 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 1195 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 1196 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 1197 | } 1198 | 1199 | // ----------------------------------------------------------------------------------------------------------------------------------- 1200 | 1201 | glm::vec2 hammersley(uint32_t i, uint32_t N) 1202 | { 1203 | return glm::vec2(float(i) / float(N), radical_inverse_vdc(i)); 1204 | } 1205 | 1206 | // ----------------------------------------------------------------------------------------------------------------------------------- 1207 | 1208 | void precompute_prefilter_constants() 1209 | { 1210 | m_sample_directions.clear(); 1211 | m_sample_directions.resize(PREFILTER_MIP_LEVELS); 1212 | 1213 | for (int mip = 0; mip < PREFILTER_MIP_LEVELS; mip++) 1214 | { 1215 | uint32_t mip_width = PREFILTER_MAP_SIZE * std::pow(0.5, mip); 1216 | uint32_t mip_height = PREFILTER_MAP_SIZE * std::pow(0.5, mip); 1217 | 1218 | float roughness = (float)mip / (float)(PREFILTER_MIP_LEVELS - 1); 1219 | 1220 | std::vector samples; 1221 | 1222 | samples.resize(MAX_PREFILTER_SAMPLES); 1223 | 1224 | for (int i = 0; i < m_sample_count; i++) 1225 | { 1226 | glm::vec2 Xi = hammersley(i, m_sample_count); 1227 | float a = roughness * roughness; 1228 | 1229 | float phi = 2.0f * M_PI * Xi.x; 1230 | float cos_theta = sqrt((1.0f - Xi.y) / (1.0f + (a * a - 1.0f) * Xi.y)); 1231 | float sin_theta = sqrt(1.0f - cos_theta * cos_theta); 1232 | 1233 | // from spherical coordinates to cartesian coordinates - halfway vector 1234 | glm::vec3 H; 1235 | H.x = cos(phi) * sin_theta; 1236 | H.y = sin(phi) * sin_theta; 1237 | H.z = cos_theta; 1238 | 1239 | samples[i] = glm::vec4(H, 0.0f); 1240 | } 1241 | 1242 | m_sample_directions[mip] = std::make_unique(GL_DYNAMIC_DRAW, sizeof(glm::vec4) * MAX_PREFILTER_SAMPLES, samples.data()); 1243 | } 1244 | } 1245 | 1246 | // ----------------------------------------------------------------------------------------------------------------------------------- 1247 | 1248 | private: 1249 | // General GPU resources. 1250 | std::vector> m_cubemap_fbos; 1251 | std::vector m_capture_views; 1252 | glm::mat4 m_capture_projection; 1253 | 1254 | std::unique_ptr m_cube_vbo; 1255 | std::unique_ptr m_cube_vao; 1256 | 1257 | std::unique_ptr m_object_ubo; 1258 | std::unique_ptr m_global_ubo; 1259 | 1260 | std::unique_ptr m_cubemap_depth; 1261 | std::unique_ptr m_env_map; 1262 | std::unique_ptr m_env_cubemap; 1263 | std::unique_ptr m_prefilter_cubemap; 1264 | std::unique_ptr m_sh; 1265 | std::unique_ptr m_sh_intermediate; 1266 | std::unique_ptr m_brdf_lut; 1267 | 1268 | std::unique_ptr m_mesh_roughness; 1269 | 1270 | std::unique_ptr m_cubemap_convert_vs; 1271 | std::unique_ptr m_cubemap_convert_fs; 1272 | std::unique_ptr m_cubemap_convert_program; 1273 | 1274 | std::unique_ptr m_cubemap_vs; 1275 | std::unique_ptr m_cubemap_fs; 1276 | std::unique_ptr m_cubemap_program; 1277 | 1278 | std::unique_ptr m_sky_envmap_vs; 1279 | std::unique_ptr m_sky_envmap_fs; 1280 | std::unique_ptr m_sky_envmap_program; 1281 | 1282 | std::unique_ptr m_mesh_vs; 1283 | std::unique_ptr m_mesh_fs; 1284 | std::unique_ptr m_mesh_program; 1285 | 1286 | std::unique_ptr m_sh_projection_cs; 1287 | std::unique_ptr m_sh_projection_program; 1288 | 1289 | std::unique_ptr m_sh_add_cs; 1290 | std::unique_ptr m_sh_add_program; 1291 | 1292 | std::unique_ptr m_prefilter_cs; 1293 | std::unique_ptr m_prefilter_program; 1294 | 1295 | std::unique_ptr m_brdf_cs; 1296 | std::unique_ptr m_brdf_program; 1297 | 1298 | // Camera. 1299 | std::unique_ptr m_main_camera; 1300 | std::unique_ptr m_debug_camera; 1301 | 1302 | // Prefiltering Constants. 1303 | std::vector> m_sample_directions; 1304 | 1305 | SkyModel m_model; 1306 | 1307 | // Mesh 1308 | dw::Mesh* m_mesh; 1309 | 1310 | // Camera controls. 1311 | bool m_show_gui = true; 1312 | bool m_mouse_look = false; 1313 | bool m_debug_mode = false; 1314 | float m_heading_speed = 0.0f; 1315 | float m_sideways_speed = 0.0f; 1316 | float m_camera_sensitivity = 0.05f; 1317 | float m_camera_speed = 0.06f; 1318 | float m_camera_x = 0.0f; 1319 | float m_camera_y = 0.0f; 1320 | int m_type = 0; 1321 | int m_sample_count = 32; 1322 | float m_roughness = 0.0f; 1323 | }; 1324 | 1325 | DW_DECLARE_MAIN(RuntimeIBL) 1326 | -------------------------------------------------------------------------------- /src/shader/atmosphere.glsl: -------------------------------------------------------------------------------- 1 | // 2 | // Precomputed Atmospheric Scattering 3 | // Copyright (c) 2008 INRIA 4 | // All rights reserved. 5 | // 6 | // Redistribution and use in source and binary forms, with or without 7 | // modification, are permitted provided that the following conditions 8 | // are met: 9 | // 1. Redistributions of source code must retain the above copyright 10 | // notice, this list of conditions and the following disclaimer. 11 | // 2. Redistributions in binary form must reproduce the above copyright 12 | // notice, this list of conditions and the following disclaimer in the 13 | // documentation and/or other materials provided with the distribution. 14 | // 3. Neither the name of the copyright holders nor the names of its 15 | // contributors may be used to endorse or promote products derived from 16 | // this software without specific prior written permission. 17 | // 18 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 19 | // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 | // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 | // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 22 | // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 23 | // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 24 | // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 25 | // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 26 | // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 27 | // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 28 | // THE POSSIBILITY OF SUCH DAMAGE. 29 | // 30 | // Author: Eric Bruneton 31 | // 32 | 33 | uniform sampler2D s_Transmittance; 34 | uniform sampler2D s_Irradiance; 35 | uniform sampler3D s_Inscatter; 36 | 37 | uniform vec3 EARTH_POS; 38 | uniform vec3 SUN_DIR; 39 | uniform float SUN_INTENSITY; 40 | uniform vec3 betaR; 41 | uniform float mieG; 42 | 43 | #define M_PI 3.141592 44 | #define Rg 6360000.0 45 | #define Rt 6420000.0 46 | #define RL 6421000.0 47 | #define RES_R 32.0 48 | #define RES_MU 128.0 49 | #define RES_MU_S 32.0 50 | #define RES_NU 8.0 51 | 52 | vec3 hdr(vec3 L) 53 | { 54 | L = L * 0.4; 55 | L.r = L.r < 1.413 ? pow(L.r * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.r); 56 | L.g = L.g < 1.413 ? pow(L.g * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.g); 57 | L.b = L.b < 1.413 ? pow(L.b * 0.38317, 1.0 / 2.2) : 1.0 - exp(-L.b); 58 | return L; 59 | } 60 | 61 | vec4 Texture4D(sampler3D table, float r, float mu, float muS, float nu) 62 | { 63 | float H = sqrt(Rt * Rt - Rg * Rg); 64 | float rho = sqrt(r * r - Rg * Rg); 65 | 66 | float rmu = r * mu; 67 | float delta = rmu * rmu - r * r + Rg * Rg; 68 | vec4 cst = rmu < 0.0 && delta > 0.0 ? vec4(1.0, 0.0, 0.0, 0.5 - 0.5 / RES_MU) : vec4(-1.0, H * H, H, 0.5 + 0.5 / RES_MU); 69 | float uR = 0.5 / RES_R + rho / H * (1.0 - 1.0 / RES_R); 70 | float uMu = cst.w + (rmu * cst.x + sqrt(delta + cst.y)) / (rho + cst.z) * (0.5 - 1.0 / float(RES_MU)); 71 | // paper formula 72 | //float uMuS = 0.5 / RES_MU_S + max((1.0 - exp(-3.0 * muS - 0.6)) / (1.0 - exp(-3.6)), 0.0) * (1.0 - 1.0 / RES_MU_S); 73 | // better formula 74 | float uMuS = 0.5 / RES_MU_S + (atan(max(muS, -0.1975) * tan(1.26 * 1.1)) / 1.1 + (1.0 - 0.26)) * 0.5 * (1.0 - 1.0 / RES_MU_S); 75 | 76 | float lep = (nu + 1.0) / 2.0 * (RES_NU - 1.0); 77 | float uNu = floor(lep); 78 | lep = lep - uNu; 79 | 80 | return texture(table, vec3((uNu + uMuS) / RES_NU, uMu, uR)) * (1.0 - lep) + texture(table, vec3((uNu + uMuS + 1.0) / RES_NU, uMu, uR)) * lep; 81 | } 82 | 83 | vec3 GetMie(vec4 rayMie) 84 | { 85 | // approximated single Mie scattering (cf. approximate Cm in paragraph "Angular precision") 86 | // rayMie.rgb=C*, rayMie.w=Cm,r 87 | return rayMie.rgb * rayMie.w / max(rayMie.r, 1e-4) * (betaR.r / betaR); 88 | } 89 | 90 | float PhaseFunctionR(float mu) 91 | { 92 | // Rayleigh phase function 93 | return (3.0 / (16.0 * M_PI)) * (1.0 + mu * mu); 94 | } 95 | 96 | float PhaseFunctionM(float mu) 97 | { 98 | // Mie phase function 99 | return 1.5 * 1.0 / (4.0 * M_PI) * (1.0 - mieG * mieG) * pow(1.0 + (mieG * mieG) - 2.0 * mieG * mu, -3.0 / 2.0) * (1.0 + mu * mu) / (2.0 + mieG * mieG); 100 | } 101 | 102 | vec3 Transmittance(float r, float mu) 103 | { 104 | // transmittance(=transparency) of atmosphere for infinite ray (r,mu) 105 | // (mu=cos(view zenith angle)), intersections with ground ignored 106 | float uR, uMu; 107 | uR = sqrt((r - Rg) / (Rt - Rg)); 108 | uMu = atan((mu + 0.15) / (1.0 + 0.15) * tan(1.5)) / 1.5; 109 | 110 | return texture(s_Transmittance, vec2(uMu, uR)).rgb; 111 | } 112 | 113 | vec3 TransmittanceWithShadow(float r, float mu) 114 | { 115 | // transmittance(=transparency) of atmosphere for infinite ray (r,mu) 116 | // (mu=cos(view zenith angle)), or zero if ray intersects ground 117 | 118 | return mu < -sqrt(1.0 - (Rg / r) * (Rg / r)) ? vec3(0, 0, 0) : Transmittance(r, mu); 119 | } 120 | 121 | vec3 Irradiance(float r, float muS) 122 | { 123 | float uR = (r - Rg) / (Rt - Rg); 124 | float uMuS = (muS + 0.2) / (1.0 + 0.2); 125 | 126 | return texture(s_Irradiance, vec2(uMuS, uR)).rgb; 127 | } 128 | 129 | vec3 SunRadiance(vec3 worldPos) 130 | { 131 | vec3 worldV = normalize(worldPos + EARTH_POS); // vertical vector 132 | float r = length(worldPos + EARTH_POS); 133 | float muS = dot(worldV, SUN_DIR); 134 | 135 | return TransmittanceWithShadow(r, muS) * SUN_INTENSITY; 136 | } 137 | 138 | vec3 SkyIrradiance(float r, float muS) 139 | { 140 | return Irradiance(r, muS) * SUN_INTENSITY; 141 | } 142 | 143 | vec3 SkyIrradiance(vec3 worldPos) 144 | { 145 | vec3 worldV = normalize(worldPos + EARTH_POS); // vertical vector 146 | float r = length(worldPos + EARTH_POS); 147 | float muS = dot(worldV, SUN_DIR); 148 | 149 | return Irradiance(r, muS) * SUN_INTENSITY; 150 | } 151 | 152 | vec3 SkyRadiance(vec3 camera, vec3 viewdir, out vec3 extinction) 153 | { 154 | // scattered sunlight between two points 155 | // camera=observer 156 | // viewdir=unit vector towards observed point 157 | // sundir=unit vector towards the sun 158 | // return scattered light 159 | 160 | camera += EARTH_POS; 161 | 162 | vec3 result = vec3(0, 0, 0); 163 | float r = length(camera); 164 | float rMu = dot(camera, viewdir); 165 | float mu = rMu / r; 166 | float r0 = r; 167 | float mu0 = mu; 168 | 169 | float deltaSq = sqrt(rMu * rMu - r * r + Rt * Rt); 170 | float din = max(-rMu - deltaSq, 0.0); 171 | if (din > 0.0) 172 | { 173 | camera += din * viewdir; 174 | rMu += din; 175 | mu = rMu / Rt; 176 | r = Rt; 177 | } 178 | 179 | float nu = dot(viewdir, SUN_DIR); 180 | float muS = dot(camera, SUN_DIR) / r; 181 | 182 | vec4 inScatter = Texture4D(s_Inscatter, r, rMu / r, muS, nu); 183 | extinction = Transmittance(r, mu); 184 | 185 | if (r <= Rt) 186 | { 187 | vec3 inScatterM = GetMie(inScatter); 188 | float phase = PhaseFunctionR(nu); 189 | float phaseM = PhaseFunctionM(nu); 190 | result = inScatter.rgb * phase + inScatterM * phaseM; 191 | } 192 | else 193 | { 194 | result = vec3(0, 0, 0); 195 | extinction = vec3(1, 1, 1); 196 | } 197 | 198 | return result * SUN_INTENSITY; 199 | } 200 | 201 | vec3 InScattering(vec3 camera, vec3 _point, out vec3 extinction, float shaftWidth) 202 | { 203 | // single scattered sunlight between two points 204 | // camera=observer 205 | // point=point on the ground 206 | // sundir=unit vector towards the sun 207 | // return scattered light and extinction coefficient 208 | 209 | vec3 result = vec3(0, 0, 0); 210 | extinction = vec3(1, 1, 1); 211 | 212 | vec3 viewdir = _point - camera; 213 | float d = length(viewdir); 214 | viewdir = viewdir / d; 215 | float r = length(camera); 216 | 217 | if (r < 0.9 * Rg) 218 | { 219 | camera.y += Rg; 220 | _point.y += Rg; 221 | r = length(camera); 222 | } 223 | float rMu = dot(camera, viewdir); 224 | float mu = rMu / r; 225 | float r0 = r; 226 | float mu0 = mu; 227 | _point -= viewdir * clamp(shaftWidth, 0.0, d); 228 | 229 | float deltaSq = sqrt(rMu * rMu - r * r + Rt * Rt); 230 | float din = max(-rMu - deltaSq, 0.0); 231 | 232 | if (din > 0.0 && din < d) 233 | { 234 | camera += din * viewdir; 235 | rMu += din; 236 | mu = rMu / Rt; 237 | r = Rt; 238 | d -= din; 239 | } 240 | 241 | if (r <= Rt) 242 | { 243 | float nu = dot(viewdir, SUN_DIR); 244 | float muS = dot(camera, SUN_DIR) / r; 245 | 246 | vec4 inScatter; 247 | 248 | if (r < Rg + 600.0) 249 | { 250 | // avoids imprecision problems in aerial perspective near ground 251 | float f = (Rg + 600.0) / r; 252 | r = r * f; 253 | rMu = rMu * f; 254 | _point = _point * f; 255 | } 256 | 257 | float r1 = length(_point); 258 | float rMu1 = dot(_point, viewdir); 259 | float mu1 = rMu1 / r1; 260 | float muS1 = dot(_point, SUN_DIR) / r1; 261 | 262 | if (mu > 0.0) 263 | extinction = min(Transmittance(r, mu) / Transmittance(r1, mu1), 1.0); 264 | else 265 | extinction = min(Transmittance(r1, -mu1) / Transmittance(r, -mu), 1.0); 266 | 267 | const float EPS = 0.004; 268 | float lim = -sqrt(1.0 - (Rg / r) * (Rg / r)); 269 | 270 | if (abs(mu - lim) < EPS) 271 | { 272 | float a = ((mu - lim) + EPS) / (2.0 * EPS); 273 | 274 | mu = lim - EPS; 275 | r1 = sqrt(r * r + d * d + 2.0 * r * d * mu); 276 | mu1 = (r * mu + d) / r1; 277 | 278 | vec4 inScatter0 = Texture4D(s_Inscatter, r, mu, muS, nu); 279 | vec4 inScatter1 = Texture4D(s_Inscatter, r1, mu1, muS1, nu); 280 | vec4 inScatterA = max(inScatter0 - inScatter1 * extinction.rgbr, 0.0); 281 | 282 | mu = lim + EPS; 283 | r1 = sqrt(r * r + d * d + 2.0 * r * d * mu); 284 | mu1 = (r * mu + d) / r1; 285 | 286 | inScatter0 = Texture4D(s_Inscatter, r, mu, muS, nu); 287 | inScatter1 = Texture4D(s_Inscatter, r1, mu1, muS1, nu); 288 | vec4 inScatterB = max(inScatter0 - inScatter1 * extinction.rgbr, 0.0); 289 | 290 | inScatter = mix(inScatterA, inScatterB, a); 291 | } 292 | else 293 | { 294 | vec4 inScatter0 = Texture4D(s_Inscatter, r, mu, muS, nu); 295 | vec4 inScatter1 = Texture4D(s_Inscatter, r1, mu1, muS1, nu); 296 | inScatter = max(inScatter0 - inScatter1 * extinction.rgbr, 0.0); 297 | } 298 | 299 | // avoids imprecision problems in Mie scattering when sun is below horizon 300 | inScatter.w *= smoothstep(0.00, 0.02, muS); 301 | 302 | vec3 inScatterM = GetMie(inScatter); 303 | float phase = PhaseFunctionR(nu); 304 | float phaseM = PhaseFunctionM(nu); 305 | result = inScatter.rgb * phase + inScatterM * phaseM; 306 | } 307 | 308 | return result * SUN_INTENSITY; 309 | } -------------------------------------------------------------------------------- /src/shader/brdf_cs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // CONSTANTS -------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | #define LOCAL_SIZE 8 6 | #define PI 3.14159265359 7 | #define BRDF_LUT_SIZE 512 8 | 9 | // ------------------------------------------------------------------ 10 | // INPUTS ----------------------------------------------------------- 11 | // ------------------------------------------------------------------ 12 | 13 | layout(local_size_x = LOCAL_SIZE, local_size_y = LOCAL_SIZE, local_size_z = 1) in; 14 | 15 | // ------------------------------------------------------------------ 16 | // OUTPUTS ---------------------------------------------------------- 17 | // ------------------------------------------------------------------ 18 | 19 | layout(binding = 0, rg16f) uniform image2D i_BRDF; 20 | 21 | // ------------------------------------------------------------------ 22 | // FUNCTIONS -------------------------------------------------------- 23 | // ------------------------------------------------------------------ 24 | 25 | // http://holger.dammertz.org/stuff/notes_hammersleyOnHemisphere.html 26 | // efficient VanDerCorpus calculation. 27 | float radical_inverse_vdc(uint bits) 28 | { 29 | bits = (bits << 16u) | (bits >> 16u); 30 | bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u); 31 | bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u); 32 | bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u); 33 | bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u); 34 | return float(bits) * 2.3283064365386963e-10; // / 0x100000000 35 | } 36 | 37 | // ------------------------------------------------------------------ 38 | 39 | vec2 hammersley(uint i, uint N) 40 | { 41 | return vec2(float(i) / float(N), radical_inverse_vdc(i)); 42 | } 43 | 44 | // ------------------------------------------------------------------ 45 | 46 | vec3 importance_sample_ggx(vec2 Xi, vec3 N, float roughness) 47 | { 48 | float a = roughness * roughness; 49 | 50 | float phi = 2.0 * PI * Xi.x; 51 | float cosTheta = sqrt((1.0 - Xi.y) / (1.0 + (a * a - 1.0) * Xi.y)); 52 | float sinTheta = sqrt(1.0 - cosTheta * cosTheta); 53 | 54 | // from spherical coordinates to cartesian coordinates - halfway vector 55 | vec3 H; 56 | H.x = cos(phi) * sinTheta; 57 | H.y = sin(phi) * sinTheta; 58 | H.z = cosTheta; 59 | 60 | // from tangent-space H vector to world-space sample vector 61 | vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 62 | vec3 tangent = normalize(cross(up, N)); 63 | vec3 bitangent = cross(N, tangent); 64 | 65 | vec3 sampleVec = tangent * H.x + bitangent * H.y + N * H.z; 66 | return normalize(sampleVec); 67 | } 68 | 69 | // ------------------------------------------------------------------ 70 | 71 | float geometry_schlick_ggx(float NdotV, float roughness) 72 | { 73 | // note that we use a different k for IBL 74 | float a = roughness; 75 | float k = (a * a) / 2.0; 76 | 77 | float nom = NdotV; 78 | float denom = NdotV * (1.0 - k) + k; 79 | 80 | return nom / denom; 81 | } 82 | 83 | // ------------------------------------------------------------------ 84 | 85 | float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) 86 | { 87 | float NdotV = max(dot(N, V), 0.0); 88 | float NdotL = max(dot(N, L), 0.0); 89 | float ggx2 = geometry_schlick_ggx(NdotV, roughness); 90 | float ggx1 = geometry_schlick_ggx(NdotL, roughness); 91 | 92 | return ggx1 * ggx2; 93 | } 94 | 95 | // ------------------------------------------------------------------ 96 | 97 | vec2 integrate_brdf(float NdotV, float roughness) 98 | { 99 | vec3 V; 100 | V.x = sqrt(1.0 - NdotV * NdotV); 101 | V.y = 0.0; 102 | V.z = NdotV; 103 | 104 | float A = 0.0; 105 | float B = 0.0; 106 | 107 | vec3 N = vec3(0.0, 0.0, 1.0); 108 | 109 | const uint SAMPLE_COUNT = 1024u; 110 | for (uint i = 0u; i < SAMPLE_COUNT; ++i) 111 | { 112 | // generates a sample vector that's biased towards the 113 | // preferred alignment direction (importance sampling). 114 | vec2 Xi = hammersley(i, SAMPLE_COUNT); 115 | vec3 H = importance_sample_ggx(Xi, N, roughness); 116 | vec3 L = normalize(2.0 * dot(V, H) * H - V); 117 | 118 | float NdotL = max(L.z, 0.0); 119 | float NdotH = max(H.z, 0.0); 120 | float VdotH = max(dot(V, H), 0.0); 121 | 122 | if (NdotL > 0.0) 123 | { 124 | float G = geometry_smith(N, V, L, roughness); 125 | float G_Vis = (G * VdotH) / (NdotH * NdotV); 126 | float Fc = pow(1.0 - VdotH, 5.0); 127 | 128 | A += (1.0 - Fc) * G_Vis; 129 | B += Fc * G_Vis; 130 | } 131 | } 132 | A /= float(SAMPLE_COUNT); 133 | B /= float(SAMPLE_COUNT); 134 | return vec2(A, B); 135 | } 136 | 137 | // ------------------------------------------------------------------ 138 | // MAIN ------------------------------------------------------------- 139 | // ------------------------------------------------------------------ 140 | 141 | void main() 142 | { 143 | vec2 tex_coord = vec2(float(gl_GlobalInvocationID.x), float(gl_GlobalInvocationID.y)) / float(BRDF_LUT_SIZE - 1); 144 | vec2 brdf = integrate_brdf(tex_coord.x, tex_coord.y); 145 | 146 | imageStore(i_BRDF, ivec2(gl_GlobalInvocationID.xy), vec4(brdf, 0.0, 0.0)); 147 | } 148 | 149 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/equirectangular_to_cubemap_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES ------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | in vec3 FS_IN_WorldPos; 6 | 7 | // ------------------------------------------------------------------ 8 | // OUTPUT VARIABLES ------------------------------------------------ 9 | // ------------------------------------------------------------------ 10 | 11 | out vec3 FS_OUT_Color; 12 | 13 | // ------------------------------------------------------------------ 14 | // SAMPLERS --------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | uniform sampler2D s_EnvMap; 18 | 19 | // ------------------------------------------------------------------ 20 | // FUNCTIONS ------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | const vec2 kInvATan = vec2(0.1591, 0.3183); 24 | 25 | vec2 sample_spherical_map(vec3 v) 26 | { 27 | vec2 uv = vec2(atan(v.z, v.x), asin(v.y)); 28 | uv *= kInvATan; 29 | uv += 0.5; 30 | return uv; 31 | } 32 | 33 | // ------------------------------------------------------------------ 34 | // MAIN ------------------------------------------------------------ 35 | // ------------------------------------------------------------------ 36 | 37 | void main() 38 | { 39 | vec2 uv = sample_spherical_map(normalize(FS_IN_WorldPos)); 40 | vec3 color = texture(s_EnvMap, uv).rgb; 41 | 42 | FS_OUT_Color = color; 43 | } 44 | 45 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/equirectangular_to_cubemap_vs.glsl: -------------------------------------------------------------------------------- 1 | layout(location = 0) in vec3 VS_IN_Position; 2 | 3 | // ------------------------------------------------------------------ 4 | // OUTPUT VARIABLES ------------------------------------------------ 5 | // ------------------------------------------------------------------ 6 | 7 | out vec3 FS_IN_WorldPos; 8 | 9 | // ------------------------------------------------------------------ 10 | // UNIFORMS -------------------------------------------------------- 11 | // ------------------------------------------------------------------ 12 | 13 | uniform mat4 u_Projection; 14 | uniform mat4 u_View; 15 | 16 | // ------------------------------------------------------------------ 17 | // MAIN ------------------------------------------------------------ 18 | // ------------------------------------------------------------------ 19 | 20 | void main(void) 21 | { 22 | FS_IN_WorldPos = VS_IN_Position; 23 | gl_Position = u_Projection * u_View * vec4(VS_IN_Position, 1.0); 24 | } 25 | 26 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /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 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/mesh_fs.glsl: -------------------------------------------------------------------------------- 1 | const float Pi = 3.141592654; 2 | const float CosineA0 = Pi; 3 | const float CosineA1 = (2.0 * Pi) / 3.0; 4 | const float CosineA2 = Pi * 0.25; 5 | 6 | out vec4 PS_OUT_Color; 7 | 8 | in vec3 PS_IN_FragPos; 9 | in vec3 PS_IN_Normal; 10 | in vec2 PS_IN_TexCoord; 11 | 12 | uniform sampler2D s_Roughness; 13 | 14 | uniform sampler2D s_BRDF; 15 | uniform sampler2D s_IrradianceSH; 16 | uniform samplerCube s_Prefiltered; 17 | 18 | uniform vec3 u_CameraPos; 19 | 20 | struct SH9 21 | { 22 | float c[9]; 23 | }; 24 | 25 | // ------------------------------------------------------------------ 26 | 27 | struct SH9Color 28 | { 29 | vec3 c[9]; 30 | }; 31 | 32 | // ---------------------------------------------------------------------------- 33 | float distribution_ggx(vec3 N, vec3 H, float roughness) 34 | { 35 | float a = roughness * roughness; 36 | float a2 = a * a; 37 | float NdotH = max(dot(N, H), 0.0); 38 | float NdotH2 = NdotH * NdotH; 39 | 40 | float nom = a2; 41 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 42 | denom = Pi * denom * denom; 43 | 44 | return nom / denom; 45 | } 46 | // ---------------------------------------------------------------------------- 47 | float geometry_schlick_ggx(float NdotV, float roughness) 48 | { 49 | float r = (roughness + 1.0); 50 | float k = (r * r) / 8.0; 51 | 52 | float nom = NdotV; 53 | float denom = NdotV * (1.0 - k) + k; 54 | 55 | return nom / denom; 56 | } 57 | // ---------------------------------------------------------------------------- 58 | float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness) 59 | { 60 | float NdotV = max(dot(N, V), 0.0); 61 | float NdotL = max(dot(N, L), 0.0); 62 | float ggx2 = geometry_schlick_ggx(NdotV, roughness); 63 | float ggx1 = geometry_schlick_ggx(NdotL, roughness); 64 | 65 | return ggx1 * ggx2; 66 | } 67 | // ---------------------------------------------------------------------------- 68 | vec3 fresnel_schlick(float cosTheta, vec3 F0) 69 | { 70 | return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0); 71 | } 72 | // ---------------------------------------------------------------------------- 73 | vec3 fresnel_schlick_roughness(float cosTheta, vec3 F0, float roughness) 74 | { 75 | return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0); 76 | } 77 | 78 | void project_onto_sh9(in vec3 dir, inout SH9 sh) 79 | { 80 | // Band 0 81 | sh.c[0] = 0.282095; 82 | 83 | // Band 1 84 | sh.c[1] = -0.488603 * dir.y; 85 | sh.c[2] = 0.488603 * dir.z; 86 | sh.c[3] = -0.488603 * dir.x; 87 | 88 | // Band 2 89 | sh.c[4] = 1.092548 * dir.x * dir.y; 90 | sh.c[5] = -1.092548 * dir.y * dir.z; 91 | sh.c[6] = 0.315392 * (3.0 * dir.z * dir.z - 1.0); 92 | sh.c[7] = -1.092548 * dir.x * dir.z; 93 | sh.c[8] = 0.546274 * (dir.x * dir.x - dir.y * dir.y); 94 | } 95 | 96 | vec3 evaluate_sh9_irradiance(in vec3 direction) 97 | { 98 | SH9 basis; 99 | 100 | project_onto_sh9(direction, basis); 101 | 102 | basis.c[0] *= CosineA0; 103 | basis.c[1] *= CosineA1; 104 | basis.c[2] *= CosineA1; 105 | basis.c[3] *= CosineA1; 106 | basis.c[4] *= CosineA2; 107 | basis.c[5] *= CosineA2; 108 | basis.c[6] *= CosineA2; 109 | basis.c[7] *= CosineA2; 110 | basis.c[8] *= CosineA2; 111 | 112 | vec3 color = vec3(0.0); 113 | 114 | for (int i = 0; i < 9; i++) 115 | color += texelFetch(s_IrradianceSH, ivec2(i, 0), 0).rgb * basis.c[i]; 116 | 117 | color.x = max(0.0, color.x); 118 | color.y = max(0.0, color.y); 119 | color.z = max(0.0, color.z); 120 | 121 | return color / Pi; 122 | } 123 | 124 | void main() 125 | { 126 | // material properties 127 | vec3 albedo = vec3(1.0); 128 | float metallic = 1.0; 129 | float roughness = texture(s_Roughness, PS_IN_TexCoord).r; 130 | 131 | // input lighting data 132 | vec3 N = PS_IN_Normal; 133 | vec3 V = normalize(u_CameraPos - PS_IN_FragPos); 134 | vec3 R = reflect(-V, N); 135 | 136 | // calculate reflectance at normal incidence; if dia-electric (like plastic) use F0 137 | // of 0.04 and if it's a metal, use the albedo color as F0 (metallic workflow) 138 | vec3 F0 = vec3(0.04); 139 | F0 = mix(F0, albedo, metallic); 140 | 141 | // reflectance equation 142 | vec3 Lo = vec3(0.0); 143 | 144 | // ambient lighting (we now use IBL as the ambient term) 145 | vec3 F = fresnel_schlick_roughness(max(dot(N, V), 0.0), F0, roughness); 146 | 147 | vec3 kS = F; 148 | vec3 kD = 1.0 - kS; 149 | kD *= 1.0 - metallic; 150 | 151 | vec3 irradiance = evaluate_sh9_irradiance(N); 152 | vec3 diffuse = irradiance * albedo; 153 | 154 | // sample both the pre-filter map and the BRDF lut and combine them together as per the Split-Sum approximation to get the IBL specular part. 155 | const float MAX_REFLECTION_LOD = 4.0; 156 | vec3 prefilteredColor = textureLod(s_Prefiltered, R, roughness * MAX_REFLECTION_LOD).rgb; 157 | vec2 brdf = texture(s_BRDF, vec2(max(dot(N, V), 0.0), roughness)).rg; 158 | vec3 specular = prefilteredColor * (F * brdf.x + brdf.y); 159 | 160 | vec3 ambient = (kD * diffuse + specular) * 0.3; 161 | 162 | vec3 color = ambient + Lo; 163 | 164 | // HDR tonemapping 165 | color = color / (color + vec3(1.0)); 166 | // gamma correct 167 | color = pow(color, vec3(1.0 / 2.2)); 168 | 169 | PS_OUT_Color = vec4(color, 1.0); 170 | } 171 | -------------------------------------------------------------------------------- /src/shader/mesh_vs.glsl: -------------------------------------------------------------------------------- 1 | layout(location = 0) in vec3 VS_IN_Position; 2 | layout(location = 1) in vec2 VS_IN_Texcoord; 3 | layout(location = 2) in vec3 VS_IN_Normal; 4 | layout(location = 3) in vec3 VS_IN_Tangent; 5 | layout(location = 4) in vec3 VS_IN_Bitangent; 6 | 7 | out vec3 PS_IN_FragPos; 8 | out vec3 PS_IN_Normal; 9 | out vec2 PS_IN_TexCoord; 10 | 11 | uniform mat4 u_Model; 12 | uniform mat4 u_View; 13 | uniform mat4 u_Projection; 14 | 15 | void main() 16 | { 17 | vec4 world_pos = u_Model * vec4(VS_IN_Position, 1.0f); 18 | PS_IN_FragPos = world_pos.xyz; 19 | PS_IN_TexCoord = VS_IN_Texcoord * 4.0; 20 | 21 | mat3 model_mat = mat3(u_Model); 22 | 23 | PS_IN_Normal = normalize(model_mat * VS_IN_Normal); 24 | 25 | gl_Position = u_Projection * u_View * world_pos; 26 | } 27 | -------------------------------------------------------------------------------- /src/shader/prefilter_cs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // CONSTANTS -------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | #define LOCAL_SIZE 8 6 | #define POS_X 0 7 | #define NEG_X 1 8 | #define POS_Y 2 9 | #define NEG_Y 3 10 | #define POS_Z 4 11 | #define NEG_Z 5 12 | #define PI 3.14159265359 13 | #define MAX_SAMPLES 64 14 | 15 | // ------------------------------------------------------------------ 16 | // INPUTS ----------------------------------------------------------- 17 | // ------------------------------------------------------------------ 18 | 19 | layout(local_size_x = LOCAL_SIZE, local_size_y = LOCAL_SIZE, local_size_z = 1) in; 20 | 21 | // ------------------------------------------------------------------ 22 | // OUTPUTS ---------------------------------------------------------- 23 | // ------------------------------------------------------------------ 24 | 25 | layout(binding = 0, rgba16f) uniform imageCube i_Prefiltered; 26 | 27 | // ------------------------------------------------------------------ 28 | // UNIFORM BUFFERS -------------------------------------------------- 29 | // ------------------------------------------------------------------ 30 | 31 | layout(std140) uniform u_SampleDirections 32 | { 33 | vec4 sample_directions[MAX_SAMPLES]; 34 | }; 35 | 36 | // ------------------------------------------------------------------ 37 | // SAMPLERS --------------------------------------------------------- 38 | // ------------------------------------------------------------------ 39 | 40 | uniform samplerCube s_EnvMap; 41 | uniform float u_Roughness; 42 | uniform float u_Width; 43 | uniform float u_Height; 44 | uniform int u_StartMipLevel; 45 | uniform int u_SampleCount; 46 | 47 | // ------------------------------------------------------------------ 48 | // FUNCTIONS -------------------------------------------------------- 49 | // ------------------------------------------------------------------ 50 | 51 | float unlerp(float val, float max_val) 52 | { 53 | return (val + 0.5) / max_val; 54 | } 55 | 56 | // ------------------------------------------------------------------ 57 | 58 | vec3 calculate_direction(uint face, uint face_x, uint face_y) 59 | { 60 | float s = unlerp(float(face_x), u_Width) * 2.0 - 1.0; 61 | float t = unlerp(float(face_y), u_Height) * 2.0 - 1.0; 62 | float x, y, z; 63 | 64 | switch (face) 65 | { 66 | case POS_Z: 67 | x = s; 68 | y = -t; 69 | z = 1; 70 | break; 71 | case NEG_Z: 72 | x = -s; 73 | y = -t; 74 | z = -1; 75 | break; 76 | case NEG_X: 77 | x = -1; 78 | y = -t; 79 | z = s; 80 | break; 81 | case POS_X: 82 | x = 1; 83 | y = -t; 84 | z = -s; 85 | break; 86 | case POS_Y: 87 | x = s; 88 | y = 1; 89 | z = t; 90 | break; 91 | case NEG_Y: 92 | x = s; 93 | y = -1; 94 | z = -t; 95 | break; 96 | } 97 | 98 | vec3 d; 99 | float inv_len = 1.0 / sqrt(x * x + y * y + z * z); 100 | d.x = x * inv_len; 101 | d.y = y * inv_len; 102 | d.z = z * inv_len; 103 | 104 | return d; 105 | } 106 | 107 | // ---------------------------------------------------------------------------- 108 | 109 | float distribution_ggx(vec3 N, vec3 H, float roughness) 110 | { 111 | float a = roughness * roughness; 112 | float a2 = a * a; 113 | float NdotH = max(dot(N, H), 0.0); 114 | float NdotH2 = NdotH * NdotH; 115 | 116 | float nom = a2; 117 | float denom = (NdotH2 * (a2 - 1.0) + 1.0); 118 | denom = PI * denom * denom; 119 | 120 | return nom / denom; 121 | } 122 | 123 | // ------------------------------------------------------------------ 124 | // MAIN ------------------------------------------------------------- 125 | // ------------------------------------------------------------------ 126 | 127 | void main() 128 | { 129 | vec3 N = calculate_direction(gl_GlobalInvocationID.z, gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); 130 | 131 | // make the simplyfying assumption that V equals R equals the normal 132 | vec3 R = N; 133 | vec3 V = R; 134 | ivec2 size = textureSize(s_EnvMap, u_StartMipLevel); 135 | float resolution = float(size.x); 136 | 137 | vec3 prefiltered_color = vec3(0.0); 138 | float total_weight = 0.0; 139 | 140 | // Compute a matrix to rotate the samples 141 | vec3 up = abs(N.z) < 0.999 ? vec3(0.0, 0.0, 1.0) : vec3(1.0, 0.0, 0.0); 142 | vec3 tangent = normalize(cross(up, N)); 143 | vec3 bitangent = cross(N, tangent); 144 | 145 | mat3 tangent_to_world = mat3(tangent, bitangent, N); 146 | 147 | for (uint i = 0u; i < u_SampleCount; ++i) 148 | { 149 | // generates a sample vector that's biased towards the preferred alignment direction (importance sampling). 150 | vec3 H = tangent_to_world * sample_directions[i].xyz; 151 | vec3 L = normalize(2.0 * dot(V, H) * H - V); 152 | 153 | float NdotL = max(dot(N, L), 0.0); 154 | 155 | if (NdotL > 0.0) 156 | { 157 | // sample from the environment's mip level based on roughness/pdf 158 | float D = distribution_ggx(N, H, u_Roughness); 159 | float NdotH = max(dot(N, H), 0.0); 160 | float HdotV = max(dot(H, V), 0.0); 161 | float pdf = D * NdotH / (4.0 * HdotV) + 0.0001; 162 | 163 | float sa_texel = 4.0 * PI / (6.0 * resolution * resolution); 164 | float sa_sample = 1.0 / (float(u_SampleCount) * pdf + 0.0001); 165 | 166 | float mip_level = u_Roughness == 0.0 ? 0.0 : 0.5 * log2(sa_sample / sa_texel); 167 | 168 | prefiltered_color += textureLod(s_EnvMap, L, u_StartMipLevel + mip_level).rgb * NdotL; 169 | total_weight += NdotL; 170 | } 171 | } 172 | 173 | prefiltered_color = prefiltered_color / total_weight; 174 | 175 | imageStore(i_Prefiltered, ivec3(gl_GlobalInvocationID), vec4(prefiltered_color, 1.0)); 176 | } 177 | 178 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/sh_add_cs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // CONSTANTS --------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | #define LOCAL_SIZE 8 6 | #define ENVIRONMENT_MAP_SIZE 128 7 | #define SH_INTERMEDIATE_SIZE 16 8 | #define NUM_CUBEMAP_FACES 6 9 | 10 | const float Pi = 3.141592654; 11 | 12 | // ------------------------------------------------------------------ 13 | // INPUTS ----------------------------------------------------------- 14 | // ------------------------------------------------------------------ 15 | 16 | layout(local_size_x = 1, local_size_y = SH_INTERMEDIATE_SIZE, local_size_z = NUM_CUBEMAP_FACES) in; 17 | 18 | // ------------------------------------------------------------------ 19 | // OUTPUTS ---------------------------------------------------------- 20 | // ------------------------------------------------------------------ 21 | 22 | layout(binding = 0, rgba32f) uniform image2D i_SH; 23 | 24 | // ------------------------------------------------------------------ 25 | // SAMPLERS --------------------------------------------------------- 26 | // ------------------------------------------------------------------ 27 | 28 | uniform sampler2DArray s_SHIntermediate; 29 | 30 | // ------------------------------------------------------------------ 31 | // SHARED ----------------------------------------------------------- 32 | // ------------------------------------------------------------------ 33 | 34 | shared vec3 g_sh_coeffs[SH_INTERMEDIATE_SIZE][NUM_CUBEMAP_FACES]; 35 | shared float g_weights[SH_INTERMEDIATE_SIZE][NUM_CUBEMAP_FACES]; 36 | 37 | // ------------------------------------------------------------------ 38 | // MAIN ------------------------------------------------------------- 39 | // ------------------------------------------------------------------ 40 | 41 | void main() 42 | { 43 | g_sh_coeffs[gl_GlobalInvocationID.y][gl_GlobalInvocationID.z] = vec3(0.0); 44 | g_weights[gl_GlobalInvocationID.y][gl_GlobalInvocationID.z] = 0.0; 45 | 46 | barrier(); 47 | 48 | // Add up coefficients along X axis. 49 | for (uint i = 0; i < SH_INTERMEDIATE_SIZE; i++) 50 | { 51 | ivec3 p = ivec3(gl_GlobalInvocationID.x * SH_INTERMEDIATE_SIZE + i, gl_GlobalInvocationID.y, gl_GlobalInvocationID.z); 52 | vec4 val = texelFetch(s_SHIntermediate, p, 0); 53 | 54 | g_sh_coeffs[gl_GlobalInvocationID.y][gl_GlobalInvocationID.z] += val.rgb; 55 | g_weights[gl_GlobalInvocationID.y][gl_GlobalInvocationID.z] += val.a; 56 | } 57 | 58 | barrier(); 59 | 60 | if (gl_GlobalInvocationID.z == 0) 61 | { 62 | // Add up coefficients along Z axis. 63 | for (uint i = 1; i < NUM_CUBEMAP_FACES; i++) 64 | { 65 | g_sh_coeffs[gl_GlobalInvocationID.y][0] += g_sh_coeffs[gl_GlobalInvocationID.y][i]; 66 | g_weights[gl_GlobalInvocationID.y][0] += g_weights[gl_GlobalInvocationID.y][i]; 67 | } 68 | } 69 | 70 | barrier(); 71 | 72 | if (gl_GlobalInvocationID.y == 0 && gl_GlobalInvocationID.z == 0) 73 | { 74 | // Add up coefficients along Y axis. 75 | for (uint i = 1; i < SH_INTERMEDIATE_SIZE; i++) 76 | { 77 | g_sh_coeffs[0][0] += g_sh_coeffs[i][0]; 78 | g_weights[0][0] += g_weights[i][0]; 79 | } 80 | 81 | float scale = (4.0 * Pi) / g_weights[0][0]; 82 | 83 | // Write out the coefficents. 84 | imageStore(i_SH, ivec2(gl_GlobalInvocationID.x, 0), vec4(g_sh_coeffs[0][0] * scale, g_weights[0][0])); 85 | } 86 | } 87 | 88 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/sh_projection_cs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // CONSTANTS -------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | #define LOCAL_SIZE 8 6 | #define ENVIRONMENT_MAP_SIZE 128 7 | #define SH_INTERMEDIATE_SIZE (ENVIRONMENT_MAP_SIZE / LOCAL_SIZE) 8 | #define CUBEMAP_MIP_LEVEL 2.0 9 | #define POS_X 0 10 | #define NEG_X 1 11 | #define POS_Y 2 12 | #define NEG_Y 3 13 | #define POS_Z 4 14 | #define NEG_Z 5 15 | 16 | // ------------------------------------------------------------------ 17 | // INPUTS ----------------------------------------------------------- 18 | // ------------------------------------------------------------------ 19 | 20 | layout(local_size_x = LOCAL_SIZE, local_size_y = LOCAL_SIZE, local_size_z = 1) in; 21 | 22 | // ------------------------------------------------------------------ 23 | // OUTPUTS ---------------------------------------------------------- 24 | // ------------------------------------------------------------------ 25 | 26 | layout(binding = 0, rgba32f) uniform image2DArray i_Cubemap; 27 | 28 | // ------------------------------------------------------------------ 29 | // SAMPLERS --------------------------------------------------------- 30 | // ------------------------------------------------------------------ 31 | 32 | uniform samplerCube s_Cubemap; 33 | uniform float u_Width; 34 | uniform float u_Height; 35 | 36 | // ------------------------------------------------------------------ 37 | // STRUCTURES ------------------------------------------------------- 38 | // ------------------------------------------------------------------ 39 | 40 | struct SH9 41 | { 42 | float c[9]; 43 | }; 44 | 45 | // ------------------------------------------------------------------ 46 | 47 | struct SH9Color 48 | { 49 | vec3 c[9]; 50 | }; 51 | 52 | // ------------------------------------------------------------------ 53 | // FUNCTIONS -------------------------------------------------------- 54 | // ------------------------------------------------------------------ 55 | 56 | float area_integral(float x, float y) 57 | { 58 | return atan(x * y, sqrt(x * x + y * y + 1)); 59 | } 60 | 61 | // ------------------------------------------------------------------ 62 | 63 | float unlerp(float val, float max_val) 64 | { 65 | return (val + 0.5) / max_val; 66 | } 67 | 68 | // ------------------------------------------------------------------ 69 | 70 | void project_onto_sh9(in vec3 dir, inout SH9 sh) 71 | { 72 | // Band 0 73 | sh.c[0] = 0.282095; 74 | 75 | // Band 1 76 | sh.c[1] = -0.488603 * dir.y; 77 | sh.c[2] = 0.488603 * dir.z; 78 | sh.c[3] = -0.488603 * dir.x; 79 | 80 | // Band 2 81 | sh.c[4] = 1.092548 * dir.x * dir.y; 82 | sh.c[5] = -1.092548 * dir.y * dir.z; 83 | sh.c[6] = 0.315392 * (3.0 * dir.z * dir.z - 1.0); 84 | sh.c[7] = -1.092548 * dir.x * dir.z; 85 | sh.c[8] = 0.546274 * (dir.x * dir.x - dir.y * dir.y); 86 | } 87 | 88 | // ------------------------------------------------------------------ 89 | 90 | float calculate_solid_angle(uint x, uint y) 91 | { 92 | float s = unlerp(float(x), u_Width) * 2.0 - 1.0; 93 | float t = unlerp(float(y), u_Height) * 2.0 - 1.0; 94 | 95 | // assumes square face 96 | float half_texel_size = 1.0 / u_Width; 97 | float x0 = s - half_texel_size; 98 | float y0 = t - half_texel_size; 99 | float x1 = s + half_texel_size; 100 | float y1 = t + half_texel_size; 101 | 102 | return area_integral(x0, y0) - area_integral(x0, y1) - area_integral(x1, y0) + area_integral(x1, y1); 103 | } 104 | 105 | // ------------------------------------------------------------------ 106 | 107 | vec3 calculate_direction(uint face, uint face_x, uint face_y) 108 | { 109 | float s = unlerp(float(face_x), u_Width) * 2.0 - 1.0; 110 | float t = unlerp(float(face_y), u_Height) * 2.0 - 1.0; 111 | float x, y, z; 112 | 113 | switch (face) 114 | { 115 | case POS_Z: 116 | x = s; 117 | y = -t; 118 | z = 1; 119 | break; 120 | case NEG_Z: 121 | x = -s; 122 | y = -t; 123 | z = -1; 124 | break; 125 | case NEG_X: 126 | x = -1; 127 | y = -t; 128 | z = s; 129 | break; 130 | case POS_X: 131 | x = 1; 132 | y = -t; 133 | z = -s; 134 | break; 135 | case POS_Y: 136 | x = s; 137 | y = 1; 138 | z = t; 139 | break; 140 | case NEG_Y: 141 | x = s; 142 | y = -1; 143 | z = -t; 144 | break; 145 | } 146 | 147 | vec3 d; 148 | float inv_len = 1.0 / sqrt(x * x + y * y + z * z); 149 | d.x = x * inv_len; 150 | d.y = y * inv_len; 151 | d.z = z * inv_len; 152 | 153 | return d; 154 | } 155 | 156 | // ------------------------------------------------------------------ 157 | // SHARED ----------------------------------------------------------- 158 | // ------------------------------------------------------------------ 159 | 160 | shared SH9Color g_sh_coeffs[LOCAL_SIZE][LOCAL_SIZE]; 161 | shared float g_weights[LOCAL_SIZE][LOCAL_SIZE]; 162 | 163 | // ------------------------------------------------------------------ 164 | // MAIN ------------------------------------------------------------- 165 | // ------------------------------------------------------------------ 166 | 167 | void main() 168 | { 169 | // Initialize shared memory 170 | for (int i = 0; i < 9; i++) 171 | g_sh_coeffs[gl_LocalInvocationID.x][gl_LocalInvocationID.y].c[i] = vec3(0.0); 172 | 173 | barrier(); 174 | 175 | // Generate spherical harmonics basis 176 | SH9 basis; 177 | 178 | vec3 dir = calculate_direction(gl_GlobalInvocationID.z, gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); 179 | float solid_angle = calculate_solid_angle(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y); 180 | vec3 texel = textureLod(s_Cubemap, dir, CUBEMAP_MIP_LEVEL).rgb; 181 | 182 | project_onto_sh9(dir, basis); 183 | 184 | g_weights[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = solid_angle; 185 | 186 | for (int i = 0; i < 9; i++) 187 | g_sh_coeffs[gl_LocalInvocationID.x][gl_LocalInvocationID.y].c[i] += texel * basis.c[i] * solid_angle; 188 | 189 | barrier(); 190 | 191 | // Add up the coefficients and weights along the X axis. 192 | if (gl_LocalInvocationID.x == 0) 193 | { 194 | for (int shared_idx = 1; shared_idx < LOCAL_SIZE; shared_idx++) 195 | { 196 | g_weights[0][gl_LocalInvocationID.y] += g_weights[shared_idx][gl_LocalInvocationID.y]; 197 | 198 | for (int coef_idx = 0; coef_idx < 9; coef_idx++) 199 | g_sh_coeffs[0][gl_LocalInvocationID.y].c[coef_idx] += g_sh_coeffs[shared_idx][gl_LocalInvocationID.y].c[coef_idx]; 200 | } 201 | } 202 | 203 | barrier(); 204 | 205 | // Add up the coefficients and weights along the Y axis. 206 | if (gl_LocalInvocationID.x == 0 && gl_LocalInvocationID.y == 0) 207 | { 208 | for (int shared_idx = 1; shared_idx < LOCAL_SIZE; shared_idx++) 209 | { 210 | g_weights[0][0] += g_weights[0][shared_idx]; 211 | 212 | for (int coef_idx = 0; coef_idx < 9; coef_idx++) 213 | g_sh_coeffs[0][0].c[coef_idx] += g_sh_coeffs[0][shared_idx].c[coef_idx]; 214 | } 215 | 216 | // Write out the SH9 coefficients. 217 | for (int coef_idx = 0; coef_idx < 9; coef_idx++) 218 | { 219 | ivec3 p = ivec3((SH_INTERMEDIATE_SIZE * coef_idx) + (gl_GlobalInvocationID.x / LOCAL_SIZE), gl_GlobalInvocationID.y / LOCAL_SIZE, gl_GlobalInvocationID.z); 220 | imageStore(i_Cubemap, p, vec4(g_sh_coeffs[0][0].c[coef_idx], g_weights[0][0])); 221 | } 222 | } 223 | } 224 | 225 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/sky_envmap_fs.glsl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ------------------------------------------------------------------ 4 | // OUPUT ------------------------------------------------------------ 5 | // ------------------------------------------------------------------ 6 | 7 | out vec4 PS_OUT_Color; 8 | 9 | // ------------------------------------------------------------------ 10 | // INPUT ------------------------------------------------------------ 11 | // ------------------------------------------------------------------ 12 | 13 | in vec3 PS_IN_WorldPos; 14 | 15 | // ------------------------------------------------------------------ 16 | // UNIFORM ---------------------------------------------------------- 17 | // ------------------------------------------------------------------ 18 | 19 | uniform vec3 u_CameraPos; 20 | 21 | // ------------------------------------------------------------------ 22 | // MAIN ------------------------------------------------------------- 23 | // ------------------------------------------------------------------ 24 | 25 | void main() 26 | { 27 | vec3 dir = normalize(PS_IN_WorldPos); 28 | 29 | float sun = step(cos(M_PI / 360.0), dot(dir, SUN_DIR)); 30 | 31 | vec3 sunColor = vec3(sun, sun, sun) * SUN_INTENSITY; 32 | 33 | vec3 extinction; 34 | vec3 inscatter = SkyRadiance(u_CameraPos, dir, extinction); 35 | vec3 col = sunColor * extinction + inscatter; 36 | 37 | PS_OUT_Color = vec4(col, 1.0); 38 | } 39 | 40 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/sky_envmap_vs.glsl: -------------------------------------------------------------------------------- 1 | layout(location = 0) in vec3 position; 2 | layout(location = 1) in vec3 normal; 3 | layout(location = 2) in vec2 texcoord; 4 | 5 | uniform mat4 u_Projection; 6 | uniform mat4 u_View; 7 | 8 | out vec3 PS_IN_WorldPos; 9 | 10 | void main(void) 11 | { 12 | PS_IN_WorldPos = position; 13 | gl_Position = u_Projection * u_View * vec4(position, 1.0f); 14 | } 15 | -------------------------------------------------------------------------------- /src/shader/sky_fs.glsl: -------------------------------------------------------------------------------- 1 | #define SH_DEBUG 2 | 3 | out vec3 PS_OUT_Color; 4 | 5 | in vec3 FS_IN_WorldPos; 6 | 7 | uniform samplerCube s_Cubemap; 8 | uniform samplerCube s_Prefilter; 9 | uniform sampler2D s_SH; 10 | 11 | uniform int u_Type; 12 | uniform float u_Roughness; 13 | uniform vec3 u_CameraPos; 14 | 15 | const float Pi = 3.141592654; 16 | const float CosineA0 = Pi; 17 | const float CosineA1 = (2.0 * Pi) / 3.0; 18 | const float CosineA2 = Pi * 0.25; 19 | 20 | // ------------------------------------------------------------------ 21 | // STRUCTURES ------------------------------------------------------- 22 | // ------------------------------------------------------------------ 23 | 24 | struct SH9 25 | { 26 | float c[9]; 27 | }; 28 | 29 | // ------------------------------------------------------------------ 30 | 31 | struct SH9Color 32 | { 33 | vec3 c[9]; 34 | }; 35 | 36 | // ------------------------------------------------------------------ 37 | // FUNCTIONS -------------------------------------------------------- 38 | // ------------------------------------------------------------------ 39 | 40 | void project_onto_sh9(in vec3 dir, inout SH9 sh) 41 | { 42 | // Band 0 43 | sh.c[0] = 0.282095; 44 | 45 | // Band 1 46 | sh.c[1] = -0.488603 * dir.y; 47 | sh.c[2] = 0.488603 * dir.z; 48 | sh.c[3] = -0.488603 * dir.x; 49 | 50 | // Band 2 51 | sh.c[4] = 1.092548 * dir.x * dir.y; 52 | sh.c[5] = -1.092548 * dir.y * dir.z; 53 | sh.c[6] = 0.315392 * (3.0 * dir.z * dir.z - 1.0); 54 | sh.c[7] = -1.092548 * dir.x * dir.z; 55 | sh.c[8] = 0.546274 * (dir.x * dir.x - dir.y * dir.y); 56 | } 57 | 58 | // ------------------------------------------------------------------ 59 | 60 | vec3 evaluate_sh9_irradiance(in vec3 direction) 61 | { 62 | SH9 basis; 63 | 64 | project_onto_sh9(direction, basis); 65 | 66 | basis.c[0] *= CosineA0; 67 | basis.c[1] *= CosineA1; 68 | basis.c[2] *= CosineA1; 69 | basis.c[3] *= CosineA1; 70 | basis.c[4] *= CosineA2; 71 | basis.c[5] *= CosineA2; 72 | basis.c[6] *= CosineA2; 73 | basis.c[7] *= CosineA2; 74 | basis.c[8] *= CosineA2; 75 | 76 | vec3 color = vec3(0.0); 77 | 78 | for (int i = 0; i < 9; i++) 79 | color += texelFetch(s_SH, ivec2(i, 0), 0).rgb * basis.c[i]; 80 | 81 | color.x = max(0.0, color.x); 82 | color.y = max(0.0, color.y); 83 | color.z = max(0.0, color.z); 84 | 85 | return color; 86 | } 87 | 88 | vec3 evaluate_sh9_irradiance(in SH9Color coef, in vec3 direction) 89 | { 90 | SH9 basis; 91 | 92 | project_onto_sh9(direction, basis); 93 | 94 | basis.c[0] *= CosineA0; 95 | basis.c[1] *= CosineA1; 96 | basis.c[2] *= CosineA1; 97 | basis.c[3] *= CosineA1; 98 | basis.c[4] *= CosineA2; 99 | basis.c[5] *= CosineA2; 100 | basis.c[6] *= CosineA2; 101 | basis.c[7] *= CosineA2; 102 | basis.c[8] *= CosineA2; 103 | 104 | vec3 color = vec3(0.0); 105 | 106 | for (int i = 0; i < 9; i++) 107 | color += coef.c[i] * basis.c[i]; 108 | 109 | color.x = max(0.0, color.x); 110 | color.y = max(0.0, color.y); 111 | color.z = max(0.0, color.z); 112 | 113 | return color; 114 | } 115 | 116 | // ------------------------------------------------------------------ 117 | // MAIN ------------------------------------------------------------- 118 | // ------------------------------------------------------------------ 119 | 120 | void main() 121 | { 122 | vec3 env_color; 123 | 124 | if (u_Type == 0) // Environment Map 125 | env_color = texture(s_Cubemap, FS_IN_WorldPos).rgb; 126 | else if (u_Type == 1) // Irradiance 127 | { 128 | env_color = evaluate_sh9_irradiance(normalize(FS_IN_WorldPos)); 129 | 130 | env_color = env_color / Pi; 131 | } 132 | else if (u_Type == 2) // Prefilter 133 | env_color = textureLod(s_Prefilter, FS_IN_WorldPos, u_Roughness).rgb; 134 | 135 | // HDR tonemap and gamma correct 136 | env_color = env_color / (env_color + vec3(1.0)); 137 | env_color = pow(env_color, vec3(1.0 / 2.2)); 138 | 139 | PS_OUT_Color = env_color; 140 | } 141 | 142 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shader/sky_vs.glsl: -------------------------------------------------------------------------------- 1 | layout(location = 0) in vec3 VS_IN_Position; 2 | 3 | // ------------------------------------------------------------------ 4 | // OUTPUT VARIABLES ------------------------------------------------ 5 | // ------------------------------------------------------------------ 6 | 7 | out vec3 FS_IN_WorldPos; 8 | 9 | // ------------------------------------------------------------------ 10 | // UNIFORMS -------------------------------------------------------- 11 | // ------------------------------------------------------------------ 12 | 13 | uniform mat4 u_Projection; 14 | uniform mat4 u_View; 15 | 16 | // ------------------------------------------------------------------ 17 | // MAIN ------------------------------------------------------------ 18 | // ------------------------------------------------------------------ 19 | 20 | void main() 21 | { 22 | FS_IN_WorldPos = VS_IN_Position; 23 | 24 | mat4 rotView = mat4(mat3(u_View)); 25 | vec4 clipPos = u_Projection * rotView * vec4(VS_IN_Position, 1.0); 26 | 27 | gl_Position = clipPos.xyww; 28 | } --------------------------------------------------------------------------------