├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE ├── README.md ├── data └── screenshot.jpg └── src ├── .clang-format ├── CMakeLists.txt ├── main.cpp └── shaders ├── common.glsl ├── light_injection_cs.glsl ├── mesh_fs.glsl ├── mesh_vs.glsl ├── ray_march_cs.glsl ├── shadow_map_fs.glsl ├── shadow_map_vs.glsl ├── skybox_fs.glsl └── skybox_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 | external/dwSampleFramwork 36 | bin/ 37 | bin/* 38 | build/ 39 | build/* 40 | lib/ 41 | lib/* 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("VolumetricLighting") 4 | 5 | set(ASSIMP_BUILD_ASSIMP_TOOLS OFF CACHE BOOL "ASSIMP_BUILD_ASSIMP_TOOLS") 6 | set(ASSIMP_BUILD_ASSIMP_VIEW OFF CACHE BOOL "ASSIMP_BUILD_ASSIMP_VIEW") 7 | set(ASSIMP_BUILD_TESTS OFF CACHE BOOL "ASSIMP_BUILD_TESTS") 8 | set(ASSIMP_INSTALL OFF CACHE BOOL "ASSIMP_INSTALL") 9 | set(ASSIMP_INSTALL_PDB OFF CACHE BOOL "ASSIMP_INSTALL_PDB") 10 | set(ASSIMP_NO_EXPORT ON CACHE BOOL "ASSIMP_NO_EXPORT") 11 | set(BUILD_SAMPLES OFF CACHE BOOL "BUILD_SAMPLES") 12 | set(GLFW_BUILD_DOCS OFF CACHE BOOL "GLFW_BUILD_DOCS") 13 | set(GLFW_INSTALL OFF CACHE BOOL "GLFW_INSTALL") 14 | 15 | IF(APPLE) 16 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LANGUAGE_STANDARD "c++14") 17 | set(CMAKE_XCODE_ATTRIBUTE_CLANG_CXX_LIBRARY "libc++") 18 | ENDIF() 19 | 20 | set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib") 21 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/lib") 22 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin) 23 | 24 | add_subdirectory(external/dwSampleFramework) 25 | 26 | include_directories("${DW_SAMPLE_FRAMEWORK_INCLUDES}") 27 | 28 | add_subdirectory(src) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Dihara Wijetunga 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![License: MIT](https://img.shields.io/packagist/l/doctrine/orm.svg)](https://opensource.org/licenses/MIT) 2 | 3 | # Volumetric Fog 4 | An OpenGL sample that demonstrates Volumetric Fog using a frustum-aligned voxel grid and compute shaders. 5 | 6 | ![VolumetricFog](data/screenshot.jpg) 7 | 8 | ## Usage 9 | 10 | 1. Download the latest release [here](https://github.com/diharaw/VolumetricLighting/releases/download/1.0.0/VolumetricLighting_1.0.0.zip). 11 | 2. Extract archive contents. 12 | 3. Run VolumetricLighting.exe. 13 | 14 | ### Controls 15 | 16 | * `W`/`A`/`S`/`D` - camera movement. 17 | * `RMB` - hold to look around. 18 | * `G` - toggle UI. 19 | * `ESC` - close application. 20 | 21 | ## Building 22 | 23 | ### Windows 24 | Tested on: Windows 10 version 21H1 25 | 26 | Prerequisites 27 | * MSVC 19.00 or higher 28 | * CMake 3.8 or higher 29 | 30 | ``` 31 | git clone --recursive https://github.com/diharaw/VolumetricLighting.git 32 | cd VolumetricLighting 33 | mkdir build 34 | cd build 35 | cmake -G "Visual Studio 16 2019" .. 36 | ``` 37 | Note: To obtain the assets please download the release and copy the *meshes* and *textures* into the folder containing the built executable. 38 | 39 | ## Dependencies 40 | * [dwSampleFramework](https://github.com/diharaw/dwSampleFramework) 41 | 42 | ## References 43 | * [Volumetric fog: Unified, compute shader based solution to atmospheric scattering](https://bartwronski.files.wordpress.com/2014/08/bwronski_volumetric_fog_siggraph2014.pdf) 44 | * [Physically-based & Unified Volumetric Rendering in Frostbite](https://www.ea.com/frostbite/news/physically-based-unified-volumetric-rendering-in-frostbite) 45 | 46 | ## License 47 | ``` 48 | Copyright (c) 2021 Dihara Wijetunga 49 | 50 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 51 | associated documentation files (the "Software"), to deal in the Software without restriction, 52 | including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, 53 | and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, 54 | subject to the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be included in all copies or substantial 57 | portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT 60 | LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 61 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 62 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 63 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 64 | ``` 65 | -------------------------------------------------------------------------------- /data/screenshot.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/diharaw/volumetric-fog/4e6418ca40281ebd5d865f56c4e0e837f3b17d48/data/screenshot.jpg -------------------------------------------------------------------------------- /src/.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | BasedOnStyle: WebKit 3 | AlignAfterOpenBracket: Align 4 | AlignConsecutiveAssignments: 'true' 5 | AlignConsecutiveDeclarations: 'true' 6 | AlignEscapedNewlines: Left 7 | AlignOperands: 'false' 8 | AlignTrailingComments: 'true' 9 | AllowAllParametersOfDeclarationOnNextLine: 'true' 10 | AllowShortBlocksOnASingleLine: 'true' 11 | AllowShortCaseLabelsOnASingleLine: 'true' 12 | AllowShortFunctionsOnASingleLine: All 13 | AllowShortIfStatementsOnASingleLine: 'true' 14 | AllowShortLoopsOnASingleLine: 'true' 15 | AlwaysBreakAfterReturnType: None 16 | AlwaysBreakBeforeMultilineStrings: 'false' 17 | AlwaysBreakTemplateDeclarations: 'true' 18 | BinPackArguments: 'false' 19 | BinPackParameters: 'false' 20 | BreakBeforeBraces: Allman 21 | BreakBeforeInheritanceComma: 'false' 22 | BreakBeforeTernaryOperators: 'false' 23 | BreakConstructorInitializers: AfterColon 24 | BreakStringLiterals: 'true' 25 | CompactNamespaces: 'true' 26 | ConstructorInitializerAllOnOneLineOrOnePerLine: 'false' 27 | Cpp11BracedListStyle: 'false' 28 | FixNamespaceComments: 'true' 29 | IncludeBlocks: Regroup 30 | IndentCaseLabels: 'true' 31 | IndentPPDirectives: AfterHash 32 | IndentWrappedFunctionNames: 'true' 33 | KeepEmptyLinesAtTheStartOfBlocks: 'false' 34 | Language: Cpp 35 | NamespaceIndentation: None 36 | PointerAlignment: Left 37 | ReflowComments: 'true' 38 | SortIncludes: 'false' 39 | SortUsingDeclarations: 'true' 40 | SpaceAfterCStyleCast: 'false' 41 | SpaceBeforeAssignmentOperators: 'true' 42 | SpaceBeforeParens: ControlStatements 43 | SpaceInEmptyParentheses: 'false' 44 | SpacesInAngles: 'false' 45 | SpacesInCStyleCastParentheses: 'false' 46 | SpacesInContainerLiterals: 'true' 47 | SpacesInParentheses: 'false' 48 | SpacesInSquareBrackets: 'false' 49 | 50 | ... 51 | -------------------------------------------------------------------------------- /src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8 FATAL_ERROR) 2 | 3 | find_program(CLANG_FORMAT_EXE NAMES "clang-format" DOC "Path to clang-format executable") 4 | 5 | set(CMAKE_CXX_STANDARD 14) 6 | set(CMAKE_CXX_STANDARD_REQUIRED TRUE) 7 | 8 | set(VOLUMETRIC_LIGHTING_SOURCES ${PROJECT_SOURCE_DIR}/src/main.cpp 9 | ${PROJECT_SOURCE_DIR}/external/dwSampleFramework/extras/shadow_map.cpp 10 | ${PROJECT_SOURCE_DIR}/external/dwSampleFramework/extras/shadow_map.h 11 | ${PROJECT_SOURCE_DIR}/external/dwSampleFramework/extras/hosek_wilkie_sky_model.cpp 12 | ${PROJECT_SOURCE_DIR}/external/dwSampleFramework/extras/hosek_wilkie_sky_model.h) 13 | file(GLOB_RECURSE SHADER_SOURCES ${PROJECT_SOURCE_DIR}/src/*.glsl) 14 | 15 | if (APPLE) 16 | add_executable(VolumetricLighting MACOSX_BUNDLE ${VOLUMETRIC_LIGHTING_SOURCES} ${SHADER_SOURCES} ${ASSET_SOURCES}) 17 | set(MACOSX_BUNDLE_BUNDLE_NAME "VolumetricLighting") 18 | set_source_files_properties(${SHADER_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources/shaders) 19 | set_source_files_properties(${ASSET_SOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION Resources) 20 | else() 21 | add_executable(VolumetricLighting ${VOLUMETRIC_LIGHTING_SOURCES}) 22 | endif() 23 | 24 | target_link_libraries(VolumetricLighting dwSampleFramework) 25 | 26 | if (NOT APPLE) 27 | add_custom_command(TARGET VolumetricLighting POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_SOURCE_DIR}/src/shaders $/shaders) 28 | endif() 29 | 30 | if(CLANG_FORMAT_EXE) 31 | add_custom_target(VolumetricLighting-clang-format COMMAND ${CLANG_FORMAT_EXE} -i -style=file ${VOLUMETRIC_LIGHTING_SOURCES} ${SHADER_SOURCES}) 32 | endif() 33 | 34 | set_property(TARGET VolumetricLighting PROPERTY VS_DEBUGGER_WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/bin/$(Configuration)") -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #define _USE_MATH_DEFINES 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #define CAMERA_NEAR_PLANE 1.0f 19 | #define CAMERA_FAR_PLANE 500.0f 20 | #define VOXEL_GRID_SIZE_X 160 21 | #define VOXEL_GRID_SIZE_Y 90 22 | #define VOXEL_GRID_SIZE_Z 128 23 | #define NUM_BLUE_NOISE_TEXTURES 16 24 | 25 | struct UBO 26 | { 27 | glm::mat4 view; 28 | glm::mat4 projection; 29 | glm::mat4 view_proj; 30 | glm::mat4 prev_view_proj; 31 | glm::mat4 light_view_proj; 32 | glm::mat4 inv_view_proj; 33 | glm::vec4 light_direction; 34 | glm::vec4 light_color; 35 | glm::vec4 camera_position; 36 | glm::vec4 bias_near_far_pow; 37 | glm::vec4 aniso_density_scattering_absorption; 38 | glm::vec4 time; 39 | glm::ivec4 width_height; 40 | }; 41 | 42 | class VolumetricLighting : public dw::Application 43 | { 44 | protected: 45 | // ----------------------------------------------------------------------------------------------------------------------------------- 46 | 47 | bool init(int argc, const char* argv[]) override 48 | { 49 | m_shadow_map = std::unique_ptr(new dw::ShadowMap(2048)); 50 | m_sky_model = std::unique_ptr(new dw::HosekWilkieSkyModel()); 51 | 52 | m_shadow_map->set_extents(180.0f); 53 | m_shadow_map->set_near_plane(1.0f); 54 | m_shadow_map->set_far_plane(370.0f); 55 | m_shadow_map->set_backoff_distance(200.0f); 56 | m_shadow_map->texture()->set_compare_mode(GL_COMPARE_REF_TO_TEXTURE); 57 | m_shadow_map->texture()->set_compare_func(GL_LESS); 58 | 59 | m_sun_angle = glm::radians(-58.0f); 60 | 61 | // Create GPU resources. 62 | if (!create_shaders()) 63 | return false; 64 | 65 | // Create volume textures. 66 | create_textures(); 67 | 68 | // Load blue noise textures. 69 | load_blue_noise_textures(); 70 | 71 | // Create UBO 72 | create_uniform_buffer(); 73 | 74 | // Load scene. 75 | if (!load_scene()) 76 | return false; 77 | 78 | // Create camera. 79 | create_camera(); 80 | 81 | return true; 82 | } 83 | 84 | // ----------------------------------------------------------------------------------------------------------------------------------- 85 | 86 | void update(double delta) override 87 | { 88 | if (m_debug_gui) 89 | debug_gui(); 90 | 91 | // Update camera. 92 | update_camera(); 93 | 94 | update_uniforms(); 95 | 96 | m_sky_model->update(-m_light_direction); 97 | 98 | render_shadow_map(); 99 | 100 | volumetric_light_injection(); 101 | 102 | volumetric_ray_march(); 103 | 104 | render_main_camera(); 105 | 106 | render_skybox(); 107 | 108 | m_debug_draw.render(nullptr, m_width, m_height, m_main_camera->m_view_projection, m_main_camera->m_position); 109 | 110 | m_frame_idx++; 111 | } 112 | 113 | // ----------------------------------------------------------------------------------------------------------------------------------- 114 | 115 | void debug_gui() 116 | { 117 | ImGui::SliderFloat("Anisotropy", &m_anisotropy, 0.0f, 1.0f); 118 | ImGui::SliderFloat("Density", &m_density, 0.1f, 10.0f); 119 | ImGui::Checkbox("Temporal Accumulation", &m_temporal_accumulation); 120 | ImGui::Checkbox("Tricubic Filtering", &m_tricubic_filtering); 121 | ImGui::SliderAngle("Sun Angle", &m_sun_angle, 0.0f, -180.0f); 122 | ImGui::InputFloat("Bias", &m_bias); 123 | ImGui::InputFloat("Light Intensity", &m_light_intensity); 124 | ImGui::InputFloat("Ambient Light Intensity", &m_ambient_light_intensity); 125 | ImGui::ColorEdit3("Light Color", &m_light_color.x); 126 | 127 | m_light_direction = glm::normalize(glm::vec3(0.0f, sin(m_sun_angle), cos(m_sun_angle))); 128 | m_shadow_map->set_direction(m_light_direction); 129 | } 130 | 131 | // ----------------------------------------------------------------------------------------------------------------------------------- 132 | 133 | void window_resized(int width, int height) override 134 | { 135 | // Override window resized method to update camera projection. 136 | m_main_camera->update_projection(60.0f, CAMERA_NEAR_PLANE, CAMERA_FAR_PLANE, float(m_width) / float(m_height)); 137 | } 138 | 139 | // ----------------------------------------------------------------------------------------------------------------------------------- 140 | 141 | void key_pressed(int code) override 142 | { 143 | // Handle forward movement. 144 | if (code == GLFW_KEY_W) 145 | m_heading_speed = m_camera_speed; 146 | else if (code == GLFW_KEY_S) 147 | m_heading_speed = -m_camera_speed; 148 | 149 | // Handle sideways movement. 150 | if (code == GLFW_KEY_A) 151 | m_sideways_speed = -m_camera_speed; 152 | else if (code == GLFW_KEY_D) 153 | m_sideways_speed = m_camera_speed; 154 | 155 | if (code == GLFW_KEY_SPACE) 156 | m_mouse_look = true; 157 | 158 | if (code == GLFW_KEY_G) 159 | m_debug_gui = !m_debug_gui; 160 | } 161 | 162 | // ----------------------------------------------------------------------------------------------------------------------------------- 163 | 164 | void key_released(int code) override 165 | { 166 | // Handle forward movement. 167 | if (code == GLFW_KEY_W || code == GLFW_KEY_S) 168 | m_heading_speed = 0.0f; 169 | 170 | // Handle sideways movement. 171 | if (code == GLFW_KEY_A || code == GLFW_KEY_D) 172 | m_sideways_speed = 0.0f; 173 | 174 | if (code == GLFW_KEY_SPACE) 175 | m_mouse_look = false; 176 | } 177 | 178 | // ----------------------------------------------------------------------------------------------------------------------------------- 179 | 180 | void mouse_pressed(int code) override 181 | { 182 | // Enable mouse look. 183 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 184 | m_mouse_look = true; 185 | } 186 | 187 | // ----------------------------------------------------------------------------------------------------------------------------------- 188 | 189 | void mouse_released(int code) override 190 | { 191 | // Disable mouse look. 192 | if (code == GLFW_MOUSE_BUTTON_RIGHT) 193 | m_mouse_look = false; 194 | } 195 | 196 | // ----------------------------------------------------------------------------------------------------------------------------------- 197 | 198 | protected: 199 | // ----------------------------------------------------------------------------------------------------------------------------------- 200 | 201 | dw::AppSettings intial_app_settings() override 202 | { 203 | dw::AppSettings settings; 204 | 205 | settings.maximized = false; 206 | settings.major_ver = 4; 207 | settings.width = 1920; 208 | settings.height = 1080; 209 | settings.title = "Volumetric Lighting"; 210 | settings.enable_debug_callback = false; 211 | 212 | return settings; 213 | } 214 | 215 | // ----------------------------------------------------------------------------------------------------------------------------------- 216 | 217 | private: 218 | // ----------------------------------------------------------------------------------------------------------------------------------- 219 | 220 | bool create_shaders() 221 | { 222 | // Create general shaders 223 | m_mesh_vs = dw::gl::Shader::create_from_file(GL_VERTEX_SHADER, "shaders/mesh_vs.glsl"); 224 | m_mesh_fs = dw::gl::Shader::create_from_file(GL_FRAGMENT_SHADER, "shaders/mesh_fs.glsl"); 225 | m_skybox_vs = dw::gl::Shader::create_from_file(GL_VERTEX_SHADER, "shaders/skybox_vs.glsl"); 226 | m_skybox_fs = dw::gl::Shader::create_from_file(GL_FRAGMENT_SHADER, "shaders/skybox_fs.glsl"); 227 | m_shadow_map_vs = dw::gl::Shader::create_from_file(GL_VERTEX_SHADER, "shaders/shadow_map_vs.glsl"); 228 | m_shadow_map_fs = dw::gl::Shader::create_from_file(GL_FRAGMENT_SHADER, "shaders/shadow_map_fs.glsl"); 229 | m_light_injection_cs = dw::gl::Shader::create_from_file(GL_COMPUTE_SHADER, "shaders/light_injection_cs.glsl"); 230 | m_ray_march_cs = dw::gl::Shader::create_from_file(GL_COMPUTE_SHADER, "shaders/ray_march_cs.glsl"); 231 | 232 | if (!m_mesh_vs || !m_mesh_fs || !m_skybox_vs || !m_skybox_fs || !m_shadow_map_vs || !m_shadow_map_fs || !m_light_injection_cs || !m_ray_march_cs) 233 | { 234 | DW_LOG_FATAL("Failed to create Shaders"); 235 | return false; 236 | } 237 | 238 | // Create mesh shader program 239 | m_mesh_program = dw::gl::Program::create({ m_mesh_vs, m_mesh_fs }); 240 | 241 | if (!m_mesh_program) 242 | { 243 | DW_LOG_FATAL("Failed to create Shader Program"); 244 | return false; 245 | } 246 | 247 | // Create skybox shader program 248 | m_skybox_program = dw::gl::Program::create({ m_skybox_vs, m_skybox_fs }); 249 | 250 | if (!m_skybox_program) 251 | { 252 | DW_LOG_FATAL("Failed to create Shader Program"); 253 | return false; 254 | } 255 | 256 | // Create shadow map shader program 257 | m_shadow_map_program = dw::gl::Program::create({ m_shadow_map_vs, m_shadow_map_fs }); 258 | 259 | if (!m_shadow_map_program) 260 | { 261 | DW_LOG_FATAL("Failed to create Shader Program"); 262 | return false; 263 | } 264 | 265 | // Create volume lighting shader program 266 | m_light_injection_program = dw::gl::Program::create({ m_light_injection_cs }); 267 | 268 | if (!m_light_injection_program) 269 | { 270 | DW_LOG_FATAL("Failed to create Shader Program"); 271 | return false; 272 | } 273 | 274 | // Create solve scattering shader program 275 | m_ray_march_program = dw::gl::Program::create({ m_ray_march_cs }); 276 | 277 | if (!m_ray_march_program) 278 | { 279 | DW_LOG_FATAL("Failed to create Shader Program"); 280 | return false; 281 | } 282 | 283 | return true; 284 | } 285 | 286 | // ----------------------------------------------------------------------------------------------------------------------------------- 287 | 288 | void create_textures() 289 | { 290 | m_ray_march_voxel_grid = dw::gl::Texture3D::create(VOXEL_GRID_SIZE_X, VOXEL_GRID_SIZE_Y, VOXEL_GRID_SIZE_Z, 1, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); 291 | 292 | m_ray_march_voxel_grid->set_min_filter(GL_LINEAR); 293 | m_ray_march_voxel_grid->set_mag_filter(GL_LINEAR); 294 | m_ray_march_voxel_grid->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 295 | 296 | for (int i = 0; i < 2; i++) 297 | { 298 | m_temporal_integration_voxel_grid[i] = dw::gl::Texture3D::create(VOXEL_GRID_SIZE_X, VOXEL_GRID_SIZE_Y, VOXEL_GRID_SIZE_Z, 1, GL_RGBA16F, GL_RGBA, GL_HALF_FLOAT); 299 | 300 | m_temporal_integration_voxel_grid[i]->set_min_filter(GL_LINEAR); 301 | m_temporal_integration_voxel_grid[i]->set_mag_filter(GL_LINEAR); 302 | m_temporal_integration_voxel_grid[i]->set_wrapping(GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE); 303 | } 304 | } 305 | 306 | // ----------------------------------------------------------------------------------------------------------------------------------- 307 | 308 | void load_blue_noise_textures() 309 | { 310 | m_blue_noise_textures.resize(NUM_BLUE_NOISE_TEXTURES); 311 | 312 | for (int i = 0; i < NUM_BLUE_NOISE_TEXTURES; i++) 313 | { 314 | auto texture = dw::gl::Texture2D::create_from_file("textures/blue_noise/LDR_LLL1_" + std::to_string(i) + ".png"); 315 | 316 | texture->set_min_filter(GL_NEAREST); 317 | texture->set_mag_filter(GL_NEAREST); 318 | texture->set_wrapping(GL_REPEAT, GL_REPEAT, GL_REPEAT); 319 | 320 | m_blue_noise_textures[i] = texture; 321 | } 322 | } 323 | 324 | // ----------------------------------------------------------------------------------------------------------------------------------- 325 | 326 | void create_uniform_buffer() 327 | { 328 | m_ubo = dw::gl::Buffer::create(GL_UNIFORM_BUFFER, GL_MAP_WRITE_BIT, sizeof(UBO)); 329 | } 330 | 331 | // ----------------------------------------------------------------------------------------------------------------------------------- 332 | 333 | void update_uniforms() 334 | { 335 | UBO* ubo = (UBO*)m_ubo->map(GL_WRITE_ONLY); 336 | 337 | ubo->view = m_main_camera->m_view; 338 | ubo->projection = m_main_camera->m_projection; 339 | ubo->view_proj = m_main_camera->m_view_projection; 340 | ubo->prev_view_proj = m_frame_idx == 0 ? m_main_camera->m_view_projection : m_prev_view_projection; 341 | ubo->light_view_proj = m_shadow_map->projection() * m_shadow_map->view(); 342 | ubo->inv_view_proj = glm::inverse(m_main_camera->m_view_projection); 343 | ubo->light_direction = glm::vec4(m_light_direction, 0.0f); 344 | ubo->light_color = glm::vec4(m_light_color * m_light_intensity, m_ambient_light_intensity); 345 | ubo->camera_position = glm::vec4(m_main_camera->m_position, 0.0f); 346 | ubo->bias_near_far_pow = glm::vec4(m_bias, CAMERA_NEAR_PLANE, CAMERA_FAR_PLANE, 1.0f); 347 | ubo->aniso_density_scattering_absorption = glm::vec4(m_anisotropy, m_density, 0.0f, 0.0f); 348 | ubo->time = glm::vec4(static_cast(glfwGetTime()), 0.0f, 0.0f, 0.0f); 349 | ubo->width_height = glm::ivec4(m_width, m_height, m_frame_idx, 0); 350 | 351 | m_ubo->unmap(); 352 | 353 | m_prev_view_projection = m_main_camera->m_view_projection; 354 | } 355 | 356 | // ----------------------------------------------------------------------------------------------------------------------------------- 357 | 358 | bool load_scene() 359 | { 360 | m_mesh = dw::Mesh::load("meshes/sponza.obj"); 361 | 362 | if (!m_mesh) 363 | { 364 | DW_LOG_FATAL("Failed to load mesh"); 365 | return false; 366 | } 367 | 368 | m_transform = glm::scale(glm::mat4(1.0f), glm::vec3(0.1f)); 369 | 370 | return true; 371 | } 372 | 373 | // ----------------------------------------------------------------------------------------------------------------------------------- 374 | 375 | void create_camera() 376 | { 377 | m_main_camera = std::make_unique(60.0f, CAMERA_NEAR_PLANE, CAMERA_FAR_PLANE, float(m_width) / float(m_height), glm::vec3(91.3629837f, 56.2090416f, 55.6918716f), glm::vec3(-1.0f, 0.0, 0.0f)); 378 | m_main_camera->set_rotatation_delta(glm::vec3(0.0f, -45.0f, 0.0f)); 379 | m_main_camera->update(); 380 | } 381 | 382 | // ----------------------------------------------------------------------------------------------------------------------------------- 383 | 384 | void render_mesh(dw::Mesh::Ptr mesh, dw::gl::Program::Ptr program, glm::mat4 projection, glm::mat4 view, glm::mat4 model) 385 | { 386 | program->set_uniform("u_Model", model); 387 | 388 | // Bind vertex array. 389 | mesh->mesh_vertex_array()->bind(); 390 | 391 | const auto& submeshes = mesh->sub_meshes(); 392 | const auto& materials = mesh->materials(); 393 | 394 | for (uint32_t i = 0; i < submeshes.size(); i++) 395 | { 396 | const dw::SubMesh& submesh = submeshes[i]; 397 | const dw::Material::Ptr material = materials[submesh.mat_idx]; 398 | 399 | if (material->albedo_texture() && program->set_uniform("s_Albedo", 0)) 400 | material->albedo_texture()->bind(0); 401 | 402 | if (material->normal_texture() && program->set_uniform("s_Normal", 1)) 403 | material->normal_texture()->bind(1); 404 | 405 | if (material->metallic_texture() && program->set_uniform("s_Metallic", 2)) 406 | material->metallic_texture()->bind(2); 407 | 408 | if (material->roughness_texture() && program->set_uniform("s_Roughness", 3)) 409 | material->roughness_texture()->bind(3); 410 | 411 | // Issue draw call. 412 | glDrawElementsBaseVertex(GL_TRIANGLES, submesh.index_count, GL_UNSIGNED_INT, (void*)(sizeof(unsigned int) * submesh.base_index), submesh.base_vertex); 413 | } 414 | } 415 | 416 | // ----------------------------------------------------------------------------------------------------------------------------------- 417 | 418 | void render_skybox() 419 | { 420 | DW_SCOPED_SAMPLE("Render Sky Box"); 421 | 422 | glEnable(GL_DEPTH_TEST); 423 | glDepthFunc(GL_LEQUAL); 424 | glDisable(GL_CULL_FACE); 425 | 426 | m_skybox_program->use(); 427 | 428 | m_sky_model->cube_vao()->bind(); 429 | 430 | m_ubo->bind_base(0); 431 | 432 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 433 | glViewport(0, 0, m_width, m_height); 434 | 435 | if (m_skybox_program->set_uniform("s_Cubemap", 0)) 436 | m_sky_model->texture()->bind(0); 437 | 438 | if (m_skybox_program->set_uniform("s_VoxelGrid", 1)) 439 | m_ray_march_voxel_grid->bind(1); 440 | 441 | glDrawArrays(GL_TRIANGLES, 0, 36); 442 | 443 | glDepthFunc(GL_LESS); 444 | } 445 | 446 | // ----------------------------------------------------------------------------------------------------------------------------------- 447 | 448 | void render_shadow_map() 449 | { 450 | DW_SCOPED_SAMPLE("Render Shadow Map"); 451 | 452 | m_shadow_map->begin_render(); 453 | 454 | m_ubo->bind_base(0); 455 | 456 | m_shadow_map_program->use(); 457 | 458 | // Draw scene. 459 | render_mesh(m_mesh, m_shadow_map_program, m_shadow_map->projection(), m_shadow_map->view(), m_transform); 460 | 461 | m_shadow_map->end_render(); 462 | } 463 | 464 | // ----------------------------------------------------------------------------------------------------------------------------------- 465 | 466 | void render_main_camera() 467 | { 468 | DW_SCOPED_SAMPLE("Render Main Camera"); 469 | 470 | glEnable(GL_DEPTH_TEST); 471 | glDisable(GL_BLEND); 472 | glEnable(GL_CULL_FACE); 473 | glCullFace(GL_BACK); 474 | 475 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 476 | glViewport(0, 0, m_width, m_height); 477 | 478 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 479 | glClearDepth(1.0); 480 | glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 481 | 482 | m_ubo->bind_base(0); 483 | 484 | // Bind shader program. 485 | m_mesh_program->use(); 486 | 487 | if (m_mesh_program->set_uniform("s_ShadowMap", 4)) 488 | m_shadow_map->texture()->bind(4); 489 | 490 | if (m_mesh_program->set_uniform("s_VoxelGrid", 5)) 491 | m_ray_march_voxel_grid->bind(5); 492 | 493 | if (m_mesh_program->set_uniform("s_BlueNoise", 6)) 494 | m_blue_noise_textures[m_temporal_accumulation ? m_frame_idx % NUM_BLUE_NOISE_TEXTURES : 0]->bind(6); 495 | 496 | m_mesh_program->set_uniform("u_Tricubic", m_tricubic_filtering); 497 | 498 | // Draw scene. 499 | render_mesh(m_mesh, m_mesh_program, m_main_camera->m_projection, m_main_camera->m_view, m_transform); 500 | } 501 | 502 | // ----------------------------------------------------------------------------------------------------------------------------------- 503 | 504 | void volumetric_light_injection() 505 | { 506 | DW_SCOPED_SAMPLE("Volumetric Light Injection"); 507 | 508 | m_ubo->bind_base(0); 509 | 510 | m_light_injection_program->use(); 511 | 512 | uint32_t read_idx = static_cast(m_ping_pong); 513 | uint32_t write_idx = static_cast(!m_ping_pong); 514 | 515 | m_temporal_integration_voxel_grid[write_idx]->bind_image(0, 0, 0, GL_WRITE_ONLY, m_temporal_integration_voxel_grid[write_idx]->internal_format()); 516 | 517 | if (m_light_injection_program->set_uniform("s_ShadowMap", 0)) 518 | m_shadow_map->texture()->bind(0); 519 | 520 | if (m_light_injection_program->set_uniform("s_BlueNoise", 1)) 521 | m_blue_noise_textures[m_temporal_accumulation ? m_frame_idx % NUM_BLUE_NOISE_TEXTURES : 0]->bind(1); 522 | 523 | if (m_light_injection_program->set_uniform("s_History", 2)) 524 | m_temporal_integration_voxel_grid[read_idx]->bind(2); 525 | 526 | m_light_injection_program->set_uniform("u_Accumulation", m_frame_idx == 0 ? false : m_temporal_accumulation); 527 | 528 | const uint32_t LOCAL_SIZE_X = 8; 529 | const uint32_t LOCAL_SIZE_Y = 8; 530 | const uint32_t LOCAL_SIZE_Z = 1; 531 | 532 | uint32_t size_x = static_cast(ceil(float(VOXEL_GRID_SIZE_X) / float(LOCAL_SIZE_X))); 533 | uint32_t size_y = static_cast(ceil(float(VOXEL_GRID_SIZE_Y) / float(LOCAL_SIZE_Y))); 534 | uint32_t size_z = static_cast(ceil(float(VOXEL_GRID_SIZE_Z) / float(LOCAL_SIZE_Z))); 535 | 536 | glDispatchCompute(size_x, size_y, size_z); 537 | 538 | m_ping_pong = !m_ping_pong; 539 | } 540 | 541 | // ----------------------------------------------------------------------------------------------------------------------------------- 542 | 543 | void volumetric_ray_march() 544 | { 545 | DW_SCOPED_SAMPLE("Volumetric Ray March"); 546 | 547 | m_ubo->bind_base(0); 548 | 549 | m_ray_march_program->use(); 550 | 551 | m_ray_march_voxel_grid->bind_image(0, 0, 0, GL_WRITE_ONLY, m_ray_march_voxel_grid->internal_format()); 552 | 553 | uint32_t read_idx = static_cast(m_ping_pong); 554 | 555 | if (m_ray_march_program->set_uniform("s_VoxelGrid", 0)) 556 | m_temporal_integration_voxel_grid[read_idx]->bind(0); 557 | 558 | const uint32_t LOCAL_SIZE_X = 8; 559 | const uint32_t LOCAL_SIZE_Y = 8; 560 | const uint32_t LOCAL_SIZE_Z = 1; 561 | 562 | uint32_t size_x = static_cast(ceil(float(VOXEL_GRID_SIZE_X) / float(LOCAL_SIZE_X))); 563 | uint32_t size_y = static_cast(ceil(float(VOXEL_GRID_SIZE_Y) / float(LOCAL_SIZE_Y))); 564 | uint32_t size_z = 1; 565 | 566 | glDispatchCompute(size_x, size_y, size_z); 567 | } 568 | 569 | // ----------------------------------------------------------------------------------------------------------------------------------- 570 | 571 | void update_camera() 572 | { 573 | dw::Camera* current = m_main_camera.get(); 574 | 575 | float forward_delta = m_heading_speed * m_delta; 576 | float right_delta = m_sideways_speed * m_delta; 577 | 578 | current->set_translation_delta(current->m_forward, forward_delta); 579 | current->set_translation_delta(current->m_right, right_delta); 580 | 581 | m_camera_x = m_mouse_delta_x * m_camera_sensitivity; 582 | m_camera_y = m_mouse_delta_y * m_camera_sensitivity; 583 | 584 | if (m_mouse_look) 585 | { 586 | // Activate Mouse Look 587 | current->set_rotatation_delta(glm::vec3((float)(m_camera_y), 588 | (float)(m_camera_x), 589 | (float)(0.0f))); 590 | } 591 | else 592 | { 593 | current->set_rotatation_delta(glm::vec3((float)(0), 594 | (float)(0), 595 | (float)(0))); 596 | } 597 | 598 | current->update(); 599 | } 600 | 601 | // ----------------------------------------------------------------------------------------------------------------------------------- 602 | 603 | private: 604 | // General GPU resources. 605 | std::unique_ptr m_shadow_map; 606 | std::unique_ptr m_sky_model; 607 | dw::gl::Shader::Ptr m_shadow_map_vs; 608 | dw::gl::Shader::Ptr m_shadow_map_fs; 609 | dw::gl::Shader::Ptr m_mesh_fs; 610 | dw::gl::Shader::Ptr m_mesh_vs; 611 | dw::gl::Shader::Ptr m_skybox_fs; 612 | dw::gl::Shader::Ptr m_skybox_vs; 613 | dw::gl::Shader::Ptr m_ray_march_cs; 614 | dw::gl::Shader::Ptr m_light_injection_cs; 615 | dw::gl::Program::Ptr m_shadow_map_program; 616 | dw::gl::Program::Ptr m_mesh_program; 617 | dw::gl::Program::Ptr m_skybox_program; 618 | dw::gl::Program::Ptr m_ray_march_program; 619 | dw::gl::Program::Ptr m_light_injection_program; 620 | dw::gl::Texture3D::Ptr m_ray_march_voxel_grid; 621 | dw::gl::Texture3D::Ptr m_temporal_integration_voxel_grid[2]; 622 | dw::gl::Buffer::Ptr m_ubo; 623 | std::vector m_blue_noise_textures; 624 | 625 | dw::Mesh::Ptr m_mesh; 626 | glm::mat4 m_transform; 627 | std::unique_ptr m_main_camera; 628 | glm::mat4 m_prev_view_projection; 629 | 630 | // Volumetrics 631 | float m_anisotropy = 0.7f; 632 | float m_density = 5.0f; 633 | int m_frame_idx = 0; 634 | bool m_ping_pong = false; 635 | bool m_temporal_accumulation = true; 636 | bool m_tricubic_filtering = true; 637 | 638 | // Light 639 | glm::vec3 m_light_direction; 640 | glm::vec3 m_light_color = glm::vec3(1.0f); 641 | float m_light_intensity = 50.0f; 642 | float m_ambient_light_intensity = 0.0001f; 643 | float m_sun_angle = 0.0f; 644 | float m_bias = 0.002f; 645 | 646 | // Camera controls. 647 | bool m_mouse_look = false; 648 | float m_heading_speed = 0.0f; 649 | float m_sideways_speed = 0.0f; 650 | float m_camera_sensitivity = 0.05f; 651 | float m_camera_speed = 0.05f; 652 | bool m_debug_gui = true; 653 | 654 | // Camera orientation. 655 | float m_camera_x; 656 | float m_camera_y; 657 | }; 658 | 659 | DW_DECLARE_MAIN(VolumetricLighting) -------------------------------------------------------------------------------- /src/shaders/common.glsl: -------------------------------------------------------------------------------- 1 | #define VOXEL_GRID_SIZE_X 160 2 | #define VOXEL_GRID_SIZE_Y 90 3 | #define VOXEL_GRID_SIZE_Z 128 4 | #define BLUE_NOISE_TEXTURE_SIZE 128 5 | 6 | // ------------------------------------------------------------------ 7 | 8 | float exp_01_to_linear_01_depth(float z, float n, float f) 9 | { 10 | float z_buffer_params_y = f / n; 11 | float z_buffer_params_x = 1.0f - z_buffer_params_y; 12 | 13 | return 1.0f / (z_buffer_params_x * z + z_buffer_params_y); 14 | } 15 | 16 | // ------------------------------------------------------------------ 17 | 18 | float linear_01_to_exp_01_depth(float z, float n, float f) 19 | { 20 | float z_buffer_params_y = f / n; 21 | float z_buffer_params_x = 1.0f - z_buffer_params_y; 22 | 23 | return (1.0f / z - z_buffer_params_y) / z_buffer_params_x; 24 | } 25 | 26 | // ------------------------------------------------------------------ 27 | 28 | vec3 world_to_ndc(vec3 world_pos, mat4 vp) 29 | { 30 | vec4 p = vp * vec4(world_pos, 1.0f); 31 | 32 | if (p.w > 0.0f) 33 | { 34 | p.x /= p.w; 35 | p.y /= p.w; 36 | p.z /= p.w; 37 | } 38 | 39 | return p.xyz; 40 | } 41 | 42 | // ------------------------------------------------------------------ 43 | 44 | vec3 ndc_to_uv(vec3 ndc, float n, float f, float depth_power) 45 | { 46 | vec3 uv; 47 | 48 | uv.x = ndc.x * 0.5f + 0.5f; 49 | uv.y = ndc.y * 0.5f + 0.5f; 50 | uv.z = exp_01_to_linear_01_depth(ndc.z * 0.5f + 0.5f, n, f); 51 | 52 | // Exponential View-Z 53 | vec2 params = vec2(float(VOXEL_GRID_SIZE_Z) / log2(f / n), -(float(VOXEL_GRID_SIZE_Z) * log2(n) / log2(f / n))); 54 | 55 | float view_z = uv.z * f; 56 | uv.z = (max(log2(view_z) * params.x + params.y, 0.0f)) / VOXEL_GRID_SIZE_Z; 57 | 58 | return uv; 59 | } 60 | 61 | // ------------------------------------------------------------------ 62 | 63 | vec3 world_to_uv(vec3 world_pos, float n, float f, float depth_power, mat4 vp) 64 | { 65 | vec3 ndc = world_to_ndc(world_pos, vp); 66 | return ndc_to_uv(ndc, n, f, depth_power); 67 | } 68 | 69 | // ------------------------------------------------------------------ 70 | 71 | vec3 uv_to_ndc(vec3 uv, float n, float f, float depth_power) 72 | { 73 | vec3 ndc; 74 | 75 | ndc.x = 2.0f * uv.x - 1.0f; 76 | ndc.y = 2.0f * uv.y - 1.0f; 77 | ndc.z = 2.0f * linear_01_to_exp_01_depth(uv.z, n, f) - 1.0f; 78 | 79 | return ndc; 80 | } 81 | 82 | // ------------------------------------------------------------------ 83 | 84 | vec3 ndc_to_world(vec3 ndc, mat4 inv_vp) 85 | { 86 | vec4 p = inv_vp * vec4(ndc, 1.0f); 87 | 88 | p.x /= p.w; 89 | p.y /= p.w; 90 | p.z /= p.w; 91 | 92 | return p.xyz; 93 | } 94 | 95 | // ------------------------------------------------------------------ 96 | 97 | vec3 id_to_uv(ivec3 id, float n, float f) 98 | { 99 | // Exponential View-Z 100 | float view_z = n * pow(f / n, (float(id.z) + 0.5f) / float(VOXEL_GRID_SIZE_Z)); 101 | 102 | return vec3((float(id.x) + 0.5f) / float(VOXEL_GRID_SIZE_X), 103 | (float(id.y) + 0.5f) / float(VOXEL_GRID_SIZE_Y), 104 | view_z / f); 105 | } 106 | 107 | // ------------------------------------------------------------------ 108 | 109 | vec3 id_to_uv_with_jitter(ivec3 id, float n, float f, float jitter) 110 | { 111 | // Exponential View-Z 112 | float view_z = n * pow(f / n, (float(id.z) + 0.5f + jitter) / float(VOXEL_GRID_SIZE_Z)); 113 | 114 | return vec3((float(id.x) + 0.5f) / float(VOXEL_GRID_SIZE_X), 115 | (float(id.y) + 0.5f) / float(VOXEL_GRID_SIZE_Y), 116 | view_z / f); 117 | } 118 | 119 | // ------------------------------------------------------------------ 120 | 121 | vec3 id_to_world(ivec3 id, float n, float f, float depth_power, mat4 inv_vp) 122 | { 123 | vec3 uv = id_to_uv(id, n, f); 124 | vec3 ndc = uv_to_ndc(uv, n, f, depth_power); 125 | return ndc_to_world(ndc, inv_vp); 126 | } 127 | 128 | // ------------------------------------------------------------------ 129 | 130 | vec3 id_to_world_with_jitter(ivec3 id, float jitter, float n, float f, float depth_power, mat4 inv_vp) 131 | { 132 | vec3 uv = id_to_uv_with_jitter(id, n, f, jitter); 133 | vec3 ndc = uv_to_ndc(uv, n, f, depth_power); 134 | return ndc_to_world(ndc, inv_vp); 135 | } 136 | 137 | // ------------------------------------------------------------------ 138 | 139 | // https://gist.github.com/Fewes/59d2c831672040452aa77da6eaab2234 140 | vec4 textureTricubic(sampler3D tex, vec3 coord) 141 | { 142 | // Shift the coordinate from [0,1] to [-0.5, texture_size-0.5] 143 | vec3 texture_size = vec3(textureSize(tex, 0)); 144 | vec3 coord_grid = coord * texture_size - 0.5; 145 | vec3 index = floor(coord_grid); 146 | vec3 fraction = coord_grid - index; 147 | vec3 one_frac = 1.0 - fraction; 148 | 149 | vec3 w0 = 1.0/6.0 * one_frac*one_frac*one_frac; 150 | vec3 w1 = 2.0/3.0 - 0.5 * fraction*fraction*(2.0-fraction); 151 | vec3 w2 = 2.0/3.0 - 0.5 * one_frac*one_frac*(2.0-one_frac); 152 | vec3 w3 = 1.0/6.0 * fraction*fraction*fraction; 153 | 154 | vec3 g0 = w0 + w1; 155 | vec3 g1 = w2 + w3; 156 | vec3 mult = 1.0 / texture_size; 157 | vec3 h0 = mult * ((w1 / g0) - 0.5 + index); //h0 = w1/g0 - 1, move from [-0.5, texture_size-0.5] to [0,1] 158 | vec3 h1 = mult * ((w3 / g1) + 1.5 + index); //h1 = w3/g1 + 1, move from [-0.5, texture_size-0.5] to [0,1] 159 | 160 | // Fetch the eight linear interpolations 161 | // Weighting and fetching is interleaved for performance and stability reasons 162 | vec4 tex000 = texture(tex, h0, 0.0f); 163 | vec4 tex100 = texture(tex, vec3(h1.x, h0.y, h0.z), 0.0f); 164 | tex000 = mix(tex100, tex000, g0.x); // Weigh along the x-direction 165 | 166 | vec4 tex010 = texture(tex, vec3(h0.x, h1.y, h0.z), 0.0f); 167 | vec4 tex110 = texture(tex, vec3(h1.x, h1.y, h0.z), 0.0f); 168 | tex010 = mix(tex110, tex010, g0.x); // Weigh along the x-direction 169 | tex000 = mix(tex010, tex000, g0.y); // Weigh along the y-direction 170 | 171 | vec4 tex001 = texture(tex, vec3(h0.x, h0.y, h1.z), 0.0f); 172 | vec4 tex101 = texture(tex, vec3(h1.x, h0.y, h1.z), 0.0f); 173 | tex001 = mix(tex101, tex001, g0.x); // Weigh along the x-direction 174 | 175 | vec4 tex011 = texture(tex, vec3(h0.x, h1.y, h1.z), 0.0f); 176 | vec4 tex111 = texture(tex, vec3(h1), 0.0f); 177 | tex011 = mix(tex111, tex011, g0.x); // Weigh along the x-direction 178 | tex001 = mix(tex011, tex001, g0.y); // Weigh along the y-direction 179 | 180 | return mix(tex001, tex000, g0.z); // Weigh along the z-direction 181 | } 182 | 183 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/light_injection_cs.glsl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ------------------------------------------------------------------ 4 | // DEFINES ---------------------------------------------------------- 5 | // ------------------------------------------------------------------ 6 | 7 | #define LOCAL_SIZE_X 8 8 | #define LOCAL_SIZE_Y 8 9 | #define LOCAL_SIZE_Z 1 10 | #define M_PI 3.14159265359 11 | #define EPSILON 0.0001f 12 | 13 | // ------------------------------------------------------------------ 14 | // INPUTS ----------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | layout(local_size_x = LOCAL_SIZE_X, local_size_y = LOCAL_SIZE_Y, local_size_z = LOCAL_SIZE_Z) in; 18 | 19 | // ------------------------------------------------------------------ 20 | // OUTPUT ----------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | layout(binding = 0, rgba16f) uniform writeonly image3D i_VoxelGrid; 24 | 25 | // ------------------------------------------------------------------ 26 | // UNIFORMS --------------------------------------------------------- 27 | // ------------------------------------------------------------------ 28 | 29 | layout(std140, binding = 0) uniform Uniforms 30 | { 31 | mat4 view; 32 | mat4 projection; 33 | mat4 view_proj; 34 | mat4 prev_view_proj; 35 | mat4 light_view_proj; 36 | mat4 inv_view_proj; 37 | vec4 light_direction; 38 | vec4 light_color; 39 | vec4 camera_position; 40 | vec4 bias_near_far_pow; 41 | vec4 aniso_density_scattering_absorption; 42 | vec4 time; 43 | ivec4 width_height; 44 | }; 45 | 46 | uniform sampler2DShadow s_ShadowMap; 47 | uniform sampler2D s_BlueNoise; 48 | uniform sampler3D s_History; 49 | 50 | uniform bool u_Accumulation; 51 | 52 | // ------------------------------------------------------------------ 53 | // FUNCTIONS -------------------------------------------------------- 54 | // ------------------------------------------------------------------ 55 | 56 | float sample_shadow_map(vec2 coord, float z) 57 | { 58 | float current_depth = z; 59 | float bias = bias_near_far_pow.x; 60 | 61 | return texture(s_ShadowMap, vec3(coord, current_depth - bias)); 62 | } 63 | 64 | // ------------------------------------------------------------------ 65 | 66 | float visibility(vec3 p) 67 | { 68 | // Transform frag position into Light-space. 69 | vec4 light_space_pos = light_view_proj * vec4(p, 1.0); 70 | 71 | // Perspective divide 72 | vec3 proj_coords = light_space_pos.xyz / light_space_pos.w; 73 | 74 | // Transform to [0,1] range 75 | proj_coords = proj_coords * 0.5 + 0.5; 76 | 77 | if (any(greaterThan(proj_coords.xy, vec2(1.0f))) || any(lessThan(proj_coords.xy, vec2(0.0f)))) 78 | return 1.0f; 79 | 80 | return sample_shadow_map(proj_coords.xy, proj_coords.z); 81 | } 82 | 83 | // ------------------------------------------------------------------ 84 | 85 | // Henyey-Greenstein 86 | float phase_function(vec3 Wo, vec3 Wi, float g) 87 | { 88 | float cos_theta = dot(Wo, Wi); 89 | float denom = 1.0f + g * g + 2.0f * g * cos_theta; 90 | return (1.0f / (4.0f * M_PI)) * (1.0f - g * g) / max(pow(denom, 1.5f), EPSILON); 91 | } 92 | 93 | // ------------------------------------------------------------------ 94 | 95 | float z_slice_thickness(int z) 96 | { 97 | //return 1.0f; //linear depth 98 | return exp(-float(VOXEL_GRID_SIZE_Z - z - 1) / float(VOXEL_GRID_SIZE_Z)); 99 | } 100 | 101 | // ------------------------------------------------------------------ 102 | 103 | float sample_blue_noise(ivec3 coord) 104 | { 105 | ivec2 noise_coord = (coord.xy + ivec2(0, 1) * coord.z * BLUE_NOISE_TEXTURE_SIZE) % BLUE_NOISE_TEXTURE_SIZE; 106 | return texelFetch(s_BlueNoise, noise_coord, 0).r; 107 | } 108 | 109 | // ------------------------------------------------------------------ 110 | // MAIN ------------------------------------------------------------- 111 | // ------------------------------------------------------------------ 112 | 113 | void main() 114 | { 115 | ivec3 coord = ivec3(gl_GlobalInvocationID.xyz); 116 | 117 | if (all(lessThan(coord, ivec3(VOXEL_GRID_SIZE_X, VOXEL_GRID_SIZE_Y, VOXEL_GRID_SIZE_Z)))) 118 | { 119 | // Get jitter for the current pixel, remapped to -0.5 to +0.5 range. 120 | float jitter = (sample_blue_noise(coord) - 0.5f) * 0.999f; 121 | 122 | // Get the world position of the current voxel. 123 | vec3 world_pos = id_to_world_with_jitter(coord, jitter, bias_near_far_pow.y, bias_near_far_pow.z, bias_near_far_pow.w, inv_view_proj); 124 | 125 | // Get the view direction from the current voxel. 126 | vec3 Wo = normalize(camera_position.xyz - world_pos); 127 | 128 | // Density and coefficient estimation. 129 | float thickness = z_slice_thickness(coord.z); 130 | float density = aniso_density_scattering_absorption.y; 131 | 132 | // Perform lighting. 133 | vec3 lighting = light_color.rgb * light_color.a; 134 | 135 | float visibility_value = visibility(world_pos); 136 | 137 | if (visibility_value > EPSILON) 138 | lighting += visibility_value * light_color.xyz * phase_function(Wo, -light_direction.xyz, aniso_density_scattering_absorption.x); 139 | 140 | // RGB = Amount of in-scattered light, A = Density. 141 | vec4 color_and_density = vec4(lighting * density, density); 142 | 143 | // Temporal accumulation 144 | if (u_Accumulation) 145 | { 146 | vec3 world_pos_without_jitter = id_to_world(coord, bias_near_far_pow.y, bias_near_far_pow.z, bias_near_far_pow.w, inv_view_proj); 147 | 148 | // Find the history UV 149 | vec3 history_uv = world_to_uv(world_pos_without_jitter, bias_near_far_pow.y, bias_near_far_pow.z, bias_near_far_pow.w, prev_view_proj); 150 | 151 | // If history UV is outside the frustum, skip history 152 | if (all(greaterThanEqual(history_uv, vec3(0.0f))) && all(lessThanEqual(history_uv, vec3(1.0f)))) 153 | { 154 | // Fetch history sample 155 | vec4 history = textureLod(s_History, history_uv, 0.0f); 156 | 157 | color_and_density = mix(history, color_and_density, 0.05f); 158 | } 159 | } 160 | 161 | // Write out lighting. 162 | imageStore(i_VoxelGrid, coord, color_and_density); 163 | } 164 | } 165 | 166 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/mesh_fs.glsl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ------------------------------------------------------------------ 4 | // DEFINES --------------------------------------------------------- 5 | // ------------------------------------------------------------------ 6 | 7 | #define M_PI 3.14159265359 8 | #define EPSILON 0.0001f 9 | 10 | // ------------------------------------------------------------------ 11 | // OUTPUT VARIABLES ------------------------------------------------ 12 | // ------------------------------------------------------------------ 13 | 14 | out vec3 FS_OUT_Color; 15 | 16 | // ------------------------------------------------------------------ 17 | // INPUT VARIABLES ------------------------------------------------- 18 | // ------------------------------------------------------------------ 19 | 20 | in vec3 FS_IN_WorldPos; 21 | in vec3 FS_IN_Normal; 22 | in vec2 FS_IN_TexCoord; 23 | in vec3 FS_IN_Tangent; 24 | in vec3 FS_IN_Bitangent; 25 | 26 | // ------------------------------------------------------------------ 27 | // UNIFORMS --------------------------------------------------------- 28 | // ------------------------------------------------------------------ 29 | 30 | layout(std140, binding = 0) uniform Uniforms 31 | { 32 | mat4 view; 33 | mat4 projection; 34 | mat4 view_proj; 35 | mat4 prev_view_proj; 36 | mat4 light_view_proj; 37 | mat4 inv_view_proj; 38 | vec4 light_direction; 39 | vec4 light_color; 40 | vec4 camera_position; 41 | vec4 bias_near_far_pow; 42 | vec4 aniso_density_scattering_absorption; 43 | vec4 time; 44 | ivec4 width_height; 45 | }; 46 | 47 | uniform sampler2D s_Albedo; 48 | uniform sampler2D s_Normal; 49 | uniform sampler2D s_Metallic; 50 | uniform sampler2D s_Roughness; 51 | uniform sampler2DShadow s_ShadowMap; 52 | uniform sampler3D s_VoxelGrid; 53 | uniform sampler2D s_BlueNoise; 54 | 55 | uniform bool u_Tricubic; 56 | 57 | // ------------------------------------------------------------------ 58 | // FUNCTIONS -------------------------------------------------------- 59 | // ------------------------------------------------------------------ 60 | 61 | float sample_shadow_map(vec2 coord, float u, float v, float z, float inv_shadow_map_size) 62 | { 63 | vec2 uv = coord + vec2(u, v) * inv_shadow_map_size; 64 | 65 | float current_depth = z; 66 | float bias = bias_near_far_pow.x; 67 | 68 | return texture(s_ShadowMap, vec3(uv, current_depth - bias)); 69 | } 70 | 71 | // ------------------------------------------------------------------ 72 | 73 | // http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/ 74 | float visibility(vec3 p) 75 | { 76 | // Transform frag position into Light-space. 77 | vec4 light_space_pos = light_view_proj * vec4(p, 1.0); 78 | 79 | // Perspective divide 80 | vec3 proj_coords = light_space_pos.xyz / light_space_pos.w; 81 | 82 | // Transform to [0,1] range 83 | proj_coords = proj_coords * 0.5 + 0.5; 84 | 85 | if (any(greaterThan(proj_coords.xy, vec2(1.0f))) || any(lessThan(proj_coords.xy, vec2(0.0f)))) 86 | return 1.0f; 87 | 88 | const float shadow_map_size = 2048; 89 | const float inv_shadow_map_size = 1.0f / shadow_map_size; 90 | 91 | vec2 uv = proj_coords.xy * shadow_map_size; // 1 unit - 1 texel 92 | 93 | vec2 base_uv; 94 | base_uv.x = floor(uv.x + 0.5); 95 | base_uv.y = floor(uv.y + 0.5); 96 | 97 | float s = (uv.x + 0.5 - base_uv.x); 98 | float t = (uv.y + 0.5 - base_uv.y); 99 | 100 | base_uv -= vec2(0.5, 0.5); 101 | base_uv *= inv_shadow_map_size; 102 | 103 | float uw0 = (5 * s - 6); 104 | float uw1 = (11 * s - 28); 105 | float uw2 = -(11 * s + 17); 106 | float uw3 = -(5 * s + 1); 107 | 108 | float u0 = (4 * s - 5) / uw0 - 3; 109 | float u1 = (4 * s - 16) / uw1 - 1; 110 | float u2 = -(7 * s + 5) / uw2 + 1; 111 | float u3 = -s / uw3 + 3; 112 | 113 | float vw0 = (5 * t - 6); 114 | float vw1 = (11 * t - 28); 115 | float vw2 = -(11 * t + 17); 116 | float vw3 = -(5 * t + 1); 117 | 118 | float v0 = (4 * t - 5) / vw0 - 3; 119 | float v1 = (4 * t - 16) / vw1 - 1; 120 | float v2 = -(7 * t + 5) / vw2 + 1; 121 | float v3 = -t / vw3 + 3; 122 | 123 | float sum = 0.0f; 124 | 125 | sum += uw0 * vw0 * sample_shadow_map(base_uv, u0, v0, proj_coords.z, inv_shadow_map_size); 126 | sum += uw1 * vw0 * sample_shadow_map(base_uv, u1, v0, proj_coords.z, inv_shadow_map_size); 127 | sum += uw2 * vw0 * sample_shadow_map(base_uv, u2, v0, proj_coords.z, inv_shadow_map_size); 128 | sum += uw3 * vw0 * sample_shadow_map(base_uv, u3, v0, proj_coords.z, inv_shadow_map_size); 129 | 130 | sum += uw0 * vw1 * sample_shadow_map(base_uv, u0, v1, proj_coords.z, inv_shadow_map_size); 131 | sum += uw1 * vw1 * sample_shadow_map(base_uv, u1, v1, proj_coords.z, inv_shadow_map_size); 132 | sum += uw2 * vw1 * sample_shadow_map(base_uv, u2, v1, proj_coords.z, inv_shadow_map_size); 133 | sum += uw3 * vw1 * sample_shadow_map(base_uv, u3, v1, proj_coords.z, inv_shadow_map_size); 134 | 135 | sum += uw0 * vw2 * sample_shadow_map(base_uv, u0, v2, proj_coords.z, inv_shadow_map_size); 136 | sum += uw1 * vw2 * sample_shadow_map(base_uv, u1, v2, proj_coords.z, inv_shadow_map_size); 137 | sum += uw2 * vw2 * sample_shadow_map(base_uv, u2, v2, proj_coords.z, inv_shadow_map_size); 138 | sum += uw3 * vw2 * sample_shadow_map(base_uv, u3, v2, proj_coords.z, inv_shadow_map_size); 139 | 140 | sum += uw0 * vw3 * sample_shadow_map(base_uv, u0, v3, proj_coords.z, inv_shadow_map_size); 141 | sum += uw1 * vw3 * sample_shadow_map(base_uv, u1, v3, proj_coords.z, inv_shadow_map_size); 142 | sum += uw2 * vw3 * sample_shadow_map(base_uv, u2, v3, proj_coords.z, inv_shadow_map_size); 143 | sum += uw3 * vw3 * sample_shadow_map(base_uv, u3, v3, proj_coords.z, inv_shadow_map_size); 144 | 145 | return sum * 1.0f / 2704; 146 | } 147 | 148 | // ------------------------------------------------------------------ 149 | 150 | vec3 get_normal_from_map(vec3 tangent, vec3 bitangent, vec3 normal, vec2 tex_coord, sampler2D normal_map) 151 | { 152 | // Create TBN matrix. 153 | mat3 TBN = mat3(normalize(tangent), normalize(bitangent), normalize(normal)); 154 | 155 | // Sample tangent space normal vector from normal map and remap it from [0, 1] to [-1, 1] range. 156 | vec3 n = texture(normal_map, tex_coord).xyz; 157 | 158 | n.y = 1.0 - n.y; 159 | 160 | n = normalize(n * 2.0 - 1.0); 161 | 162 | // Multiple vector by the TBN matrix to transform the normal from tangent space to world space. 163 | n = normalize(TBN * n); 164 | 165 | return n; 166 | } 167 | 168 | // ------------------------------------------------------------------------ 169 | 170 | vec3 F_schlick(in vec3 f0, in float vdoth) 171 | { 172 | return f0 + (vec3(1.0) - f0) * (pow(1.0 - vdoth, 5.0)); 173 | } 174 | 175 | // ------------------------------------------------------------------------ 176 | 177 | float D_ggx(in float ndoth, in float alpha) 178 | { 179 | float a2 = alpha * alpha; 180 | float denom = (ndoth * ndoth) * (a2 - 1.0) + 1.0; 181 | 182 | return a2 / max(EPSILON, (M_PI * denom * denom)); 183 | } 184 | 185 | // ------------------------------------------------------------------------ 186 | 187 | float G1_schlick_ggx(in float roughness, in float ndotv) 188 | { 189 | float k = ((roughness + 1) * (roughness + 1)) / 8.0; 190 | 191 | return ndotv / max(EPSILON, (ndotv * (1 - k) + k)); 192 | } 193 | 194 | // ------------------------------------------------------------------------ 195 | 196 | float G_schlick_ggx(in float ndotl, in float ndotv, in float roughness) 197 | { 198 | return G1_schlick_ggx(roughness, ndotl) * G1_schlick_ggx(roughness, ndotv); 199 | } 200 | 201 | // ------------------------------------------------------------------------ 202 | 203 | vec3 evaluate_specular_brdf(in float roughness, in vec3 F, in float ndoth, in float ndotl, in float ndotv) 204 | { 205 | float alpha = roughness * roughness; 206 | return (D_ggx(ndoth, alpha) * F * G_schlick_ggx(ndotl, ndotv, roughness)) / max(EPSILON, (4.0 * ndotl * ndotv)); 207 | } 208 | 209 | // ------------------------------------------------------------------------ 210 | 211 | vec3 evaluate_diffuse_brdf(in vec3 diffuse_color) 212 | { 213 | return diffuse_color / M_PI; 214 | } 215 | 216 | // ------------------------------------------------------------------------ 217 | 218 | vec3 evaluate_uber_brdf(in vec3 diffuse_color, in float roughness, in vec3 N, in vec3 F0, in vec3 Wo, in vec3 Wh, in vec3 Wi) 219 | { 220 | float NdotL = max(dot(N, Wi), 0.0); 221 | float NdotV = max(dot(N, Wo), 0.0); 222 | float NdotH = max(dot(N, Wh), 0.0); 223 | float VdotH = max(dot(Wi, Wh), 0.0); 224 | 225 | vec3 F = F_schlick(F0, VdotH); 226 | vec3 specular = evaluate_specular_brdf(roughness, F, NdotH, NdotL, NdotV); 227 | vec3 diffuse = evaluate_diffuse_brdf(diffuse_color.xyz); 228 | 229 | return (vec3(1.0) - F) * diffuse + specular; 230 | } 231 | 232 | // ------------------------------------------------------------------------ 233 | 234 | vec3 direct_lighting(in vec3 Wo, in vec3 N, in vec3 P, in vec3 F0, in vec3 diffuse_color, in float roughness) 235 | { 236 | const vec3 Wi = -light_direction.xyz; 237 | const vec3 Wh = normalize(Wo + Wi); 238 | 239 | vec3 brdf = evaluate_uber_brdf(diffuse_color, roughness, N, F0, Wo, Wh, Wi); 240 | 241 | vec3 Li = light_color.xyz; 242 | 243 | return brdf * Li * visibility(FS_IN_WorldPos); 244 | } 245 | 246 | // ------------------------------------------------------------------ 247 | 248 | vec3 add_inscattered_light(vec3 color, vec3 world_pos) 249 | { 250 | vec3 uv = world_to_uv(world_pos, bias_near_far_pow.y, bias_near_far_pow.z, bias_near_far_pow.w, view_proj); 251 | 252 | vec4 scattered_light = u_Tricubic ? textureTricubic(s_VoxelGrid, uv) : textureLod(s_VoxelGrid, uv, 0.0f); 253 | float transmittance = scattered_light.a; 254 | 255 | return color * transmittance + scattered_light.rgb; 256 | } 257 | 258 | // ------------------------------------------------------------------ 259 | // MAIN ------------------------------------------------------------- 260 | // ------------------------------------------------------------------ 261 | 262 | void main() 263 | { 264 | const vec3 diffuse = texture(s_Albedo, FS_IN_TexCoord).rgb; 265 | const float metallic = texture(s_Metallic, FS_IN_TexCoord).r; 266 | const float roughness = texture(s_Roughness, FS_IN_TexCoord).r; 267 | const vec3 N = get_normal_from_map(FS_IN_Tangent, FS_IN_Bitangent, FS_IN_Normal, FS_IN_TexCoord, s_Normal); 268 | 269 | const vec3 Wo = normalize(camera_position.xyz.xyz - FS_IN_WorldPos); 270 | const vec3 F0 = mix(vec3(0.04f), diffuse, metallic); 271 | 272 | vec3 color = direct_lighting(Wo, N, FS_IN_WorldPos, F0, diffuse, roughness); 273 | 274 | // Ambient 275 | color += diffuse * 0.2f; 276 | 277 | // Volumetric Light 278 | color = add_inscattered_light(color, FS_IN_WorldPos); 279 | 280 | // Tone Map 281 | color = color / (color + vec3(1.0)); 282 | 283 | // Gamma Correct 284 | color = pow(color, vec3(1.0 / 2.2)); 285 | 286 | FS_OUT_Color = color; 287 | } 288 | 289 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/mesh_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES -------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec4 VS_IN_Position; 6 | layout(location = 1) in vec4 VS_IN_TexCoord; 7 | layout(location = 2) in vec4 VS_IN_Normal; 8 | layout(location = 3) in vec4 VS_IN_Tangent; 9 | layout(location = 4) in vec4 VS_IN_Bitangent; 10 | 11 | // ------------------------------------------------------------------ 12 | // OUTPUT VARIABLES ------------------------------------------------- 13 | // ------------------------------------------------------------------ 14 | 15 | out vec3 FS_IN_WorldPos; 16 | out vec3 FS_IN_Normal; 17 | out vec2 FS_IN_TexCoord; 18 | out vec3 FS_IN_Tangent; 19 | out vec3 FS_IN_Bitangent; 20 | 21 | // ------------------------------------------------------------------ 22 | // UNIFORMS --------------------------------------------------------- 23 | // ------------------------------------------------------------------ 24 | 25 | layout(std140, binding = 0) uniform Uniforms 26 | { 27 | mat4 view; 28 | mat4 projection; 29 | mat4 view_proj; 30 | mat4 prev_view_proj; 31 | mat4 light_view_proj; 32 | mat4 inv_view_proj; 33 | vec4 light_direction; 34 | vec4 light_color; 35 | vec4 camera_position; 36 | vec4 bias_near_far_pow; 37 | vec4 aniso_density_scattering_absorption; 38 | vec4 time; 39 | ivec4 width_height; 40 | }; 41 | 42 | uniform mat4 u_Model; 43 | 44 | // ------------------------------------------------------------------ 45 | // MAIN ------------------------------------------------------------- 46 | // ------------------------------------------------------------------ 47 | 48 | void main() 49 | { 50 | vec4 world_pos = u_Model * vec4(VS_IN_Position.xyz, 1.0f); 51 | FS_IN_WorldPos = world_pos.xyz; 52 | FS_IN_TexCoord = VS_IN_TexCoord.xy; 53 | 54 | mat3 normal_mat = mat3(u_Model); 55 | 56 | FS_IN_Normal = normalize(normal_mat * VS_IN_Normal.xyz); 57 | FS_IN_Tangent = normal_mat * VS_IN_Tangent.xyz; 58 | FS_IN_Bitangent = normal_mat * VS_IN_Bitangent.xyz; 59 | 60 | gl_Position = view_proj * world_pos; 61 | } 62 | 63 | // ------------------------------------------------------------------ 64 | -------------------------------------------------------------------------------- /src/shaders/ray_march_cs.glsl: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | // ------------------------------------------------------------------ 4 | // DEFINES ---------------------------------------------------------- 5 | // ------------------------------------------------------------------ 6 | 7 | #define LOCAL_SIZE_X 8 8 | #define LOCAL_SIZE_Y 8 9 | #define LOCAL_SIZE_Z 1 10 | #define M_PI 3.14159265359 11 | #define EPSILON 0.0001f 12 | 13 | // ------------------------------------------------------------------ 14 | // INPUTS ----------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | layout(local_size_x = LOCAL_SIZE_X, local_size_y = LOCAL_SIZE_Y, local_size_z = LOCAL_SIZE_Z) in; 18 | 19 | // ------------------------------------------------------------------ 20 | // OUTPUT ----------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | layout(binding = 0, rgba16f) uniform writeonly image3D i_VoxelGrid; 24 | 25 | // ------------------------------------------------------------------ 26 | // UNIFORMS --------------------------------------------------------- 27 | // ------------------------------------------------------------------ 28 | 29 | layout(std140, binding = 0) uniform Uniforms 30 | { 31 | mat4 view; 32 | mat4 projection; 33 | mat4 view_proj; 34 | mat4 prev_view_proj; 35 | mat4 light_view_proj; 36 | mat4 inv_view_proj; 37 | vec4 light_direction; 38 | vec4 light_color; 39 | vec4 camera_position; 40 | vec4 bias_near_far_pow; 41 | vec4 aniso_density_scattering_absorption; 42 | vec4 time; 43 | ivec4 width_height; 44 | }; 45 | 46 | uniform sampler3D s_VoxelGrid; 47 | 48 | // ------------------------------------------------------------------ 49 | // FUNCTIONS -------------------------------------------------------- 50 | // ------------------------------------------------------------------ 51 | 52 | float slice_distance(int z) 53 | { 54 | float n = bias_near_far_pow.y; 55 | float f = bias_near_far_pow.z; 56 | 57 | return n * pow(f / n, (float(z) + 0.5f) / float(VOXEL_GRID_SIZE_Z)); 58 | } 59 | 60 | // ------------------------------------------------------------------ 61 | 62 | float slice_thickness(int z) 63 | { 64 | return abs(slice_distance(z + 1) - slice_distance(z)); 65 | } 66 | 67 | // ------------------------------------------------------------------ 68 | 69 | // https://github.com/Unity-Technologies/VolumetricLighting/blob/master/Assets/VolumetricFog/Shaders/Scatter.compute 70 | vec4 accumulate(int z, vec3 accum_scattering, float accum_transmittance, vec3 slice_scattering, float slice_density) 71 | { 72 | const float thickness = slice_thickness(z); 73 | const float slice_transmittance = exp(-slice_density * thickness * 0.01f); 74 | 75 | vec3 slice_scattering_integral = slice_scattering * (1.0 - slice_transmittance) / slice_density; 76 | 77 | accum_scattering += slice_scattering_integral * accum_transmittance; 78 | accum_transmittance *= slice_transmittance; 79 | 80 | return vec4(accum_scattering, accum_transmittance); 81 | } 82 | 83 | // ------------------------------------------------------------------ 84 | // MAIN ------------------------------------------------------------- 85 | // ------------------------------------------------------------------ 86 | 87 | void main() 88 | { 89 | vec4 accum_scattering_transmittance = vec4(0.0f, 0.0f, 0.0f, 1.0f); 90 | 91 | // Accumulate scattering 92 | for (int z = 0; z < VOXEL_GRID_SIZE_Z; z++) 93 | { 94 | ivec3 coord = ivec3(gl_GlobalInvocationID.xy, z); 95 | 96 | vec4 slice_scattering_density = texelFetch(s_VoxelGrid, coord, 0); 97 | 98 | accum_scattering_transmittance = accumulate(z, 99 | accum_scattering_transmittance.rgb, 100 | accum_scattering_transmittance.a, 101 | slice_scattering_density.rgb, 102 | slice_scattering_density.a); 103 | 104 | imageStore(i_VoxelGrid, coord, accum_scattering_transmittance); 105 | } 106 | } 107 | 108 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/shadow_map_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // MAIN ------------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | void main() 6 | { 7 | } 8 | 9 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/shadow_map_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUT VARIABLES -------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec4 VS_IN_Position; 6 | layout(location = 1) in vec4 VS_IN_TexCoord; 7 | layout(location = 2) in vec4 VS_IN_Normal; 8 | layout(location = 3) in vec4 VS_IN_Tangent; 9 | layout(location = 4) in vec4 VS_IN_Bitangent; 10 | 11 | // ------------------------------------------------------------------ 12 | // UNIFORMS --------------------------------------------------------- 13 | // ------------------------------------------------------------------ 14 | 15 | layout(std140, binding = 0) uniform Uniforms 16 | { 17 | mat4 view; 18 | mat4 projection; 19 | mat4 view_proj; 20 | mat4 prev_view_proj; 21 | mat4 light_view_proj; 22 | mat4 inv_view_proj; 23 | vec4 light_direction; 24 | vec4 light_color; 25 | vec4 camera_position; 26 | vec4 bias_near_far_pow; 27 | vec4 aniso_density_scattering_absorption; 28 | vec4 time; 29 | ivec4 width_height; 30 | }; 31 | 32 | uniform mat4 u_Model; 33 | 34 | // ------------------------------------------------------------------ 35 | // MAIN ------------------------------------------------------------- 36 | // ------------------------------------------------------------------ 37 | 38 | void main() 39 | { 40 | gl_Position = light_view_proj * u_Model * vec4(VS_IN_Position.xyz, 1.0f); 41 | } 42 | 43 | // ------------------------------------------------------------------ 44 | -------------------------------------------------------------------------------- /src/shaders/skybox_fs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // DEFINES ---------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | #define VOXEL_GRID_SIZE_Z 128 6 | 7 | // ------------------------------------------------------------------ 8 | // INPUTS ---------------------------------------------------------- 9 | // ------------------------------------------------------------------ 10 | 11 | out vec3 FS_OUT_Color; 12 | 13 | // ------------------------------------------------------------------ 14 | // OUTPUTS --------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | in vec3 FS_IN_WorldPos; 18 | 19 | // ------------------------------------------------------------------ 20 | // UNIFORMS -------------------------------------------------------- 21 | // ------------------------------------------------------------------ 22 | 23 | layout(std140, binding = 0) uniform Uniforms 24 | { 25 | mat4 view; 26 | mat4 projection; 27 | mat4 view_proj; 28 | mat4 prev_view_proj; 29 | mat4 light_view_proj; 30 | mat4 inv_view_proj; 31 | vec4 light_direction; 32 | vec4 light_color; 33 | vec4 camera_position; 34 | vec4 bias_near_far_pow; 35 | vec4 aniso_density_scattering_absorption; 36 | vec4 time; 37 | ivec4 width_height; 38 | }; 39 | 40 | uniform samplerCube s_Cubemap; 41 | uniform sampler3D s_VoxelGrid; 42 | 43 | // ------------------------------------------------------------------ 44 | // FUNCTIONS -------------------------------------------------------- 45 | // ------------------------------------------------------------------ 46 | 47 | vec3 add_inscattered_light(vec3 color) 48 | { 49 | vec4 scattered_light = textureLod(s_VoxelGrid, vec3(float(gl_FragCoord.x)/(width_height.x - 1), float(gl_FragCoord.y)/(width_height.y - 1), 1.0f), 0.0f); 50 | float transmittance = scattered_light.a; 51 | 52 | return color * transmittance + scattered_light.rgb; 53 | } 54 | 55 | // ------------------------------------------------------------------ 56 | // MAIN ------------------------------------------------------------- 57 | // ------------------------------------------------------------------ 58 | 59 | void main() 60 | { 61 | vec3 env_color = texture(s_Cubemap, FS_IN_WorldPos).rgb; 62 | 63 | env_color = add_inscattered_light(env_color); 64 | 65 | // HDR tonemap and gamma correct 66 | env_color = env_color / (env_color + vec3(1.0)); 67 | env_color = pow(env_color, vec3(1.0 / 2.2)); 68 | 69 | FS_OUT_Color = env_color; 70 | } 71 | 72 | // ------------------------------------------------------------------ -------------------------------------------------------------------------------- /src/shaders/skybox_vs.glsl: -------------------------------------------------------------------------------- 1 | // ------------------------------------------------------------------ 2 | // INPUTS ---------------------------------------------------------- 3 | // ------------------------------------------------------------------ 4 | 5 | layout(location = 0) in vec3 VS_IN_Position; 6 | 7 | // ------------------------------------------------------------------ 8 | // OUTPUTS --------------------------------------------------------- 9 | // ------------------------------------------------------------------ 10 | 11 | out vec3 FS_IN_WorldPos; 12 | 13 | // ------------------------------------------------------------------ 14 | // UNIFORMS -------------------------------------------------------- 15 | // ------------------------------------------------------------------ 16 | 17 | layout(std140, binding = 0) uniform Uniforms 18 | { 19 | mat4 view; 20 | mat4 projection; 21 | mat4 view_proj; 22 | mat4 prev_view_proj; 23 | mat4 light_view_proj; 24 | mat4 inv_view_proj; 25 | vec4 light_direction; 26 | vec4 light_color; 27 | vec4 camera_position; 28 | vec4 bias_near_far_pow; 29 | vec4 aniso_density_scattering_absorption; 30 | vec4 time; 31 | ivec4 width_height; 32 | }; 33 | 34 | // ------------------------------------------------------------------ 35 | // MAIN ------------------------------------------------------------ 36 | // ------------------------------------------------------------------ 37 | 38 | void main() 39 | { 40 | FS_IN_WorldPos = VS_IN_Position; 41 | 42 | mat4 rotView = mat4(mat3(view)); 43 | vec4 clipPos = projection * rotView * vec4(VS_IN_Position, 1.0); 44 | 45 | gl_Position = clipPos.xyww; 46 | } 47 | 48 | // ------------------------------------------------------------------ --------------------------------------------------------------------------------