├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING ├── LICENSE ├── README.md ├── bilateralblur.frag.glsl ├── common.h ├── depthlinearize.frag.glsl ├── displaytex.frag.glsl ├── doc ├── bluroff.jpg └── sample.jpg ├── fullscreenquad.geo.glsl ├── fullscreenquad.vert.glsl ├── hbao.frag.glsl ├── hbao_blur.frag.glsl ├── hbao_deinterleave.frag.glsl ├── hbao_reinterleave.frag.glsl ├── scene.frag.glsl ├── scene.vert.glsl ├── ssao.cpp └── viewnormal.frag.glsl /.gitignore: -------------------------------------------------------------------------------- 1 | .clang-format 2 | .editorconfig 3 | 4 | ############################# 5 | #Spirv 6 | ############################# 7 | *.spv 8 | *.spva 9 | *.sass 10 | *.sassbin 11 | *.bat 12 | 13 | ############################# 14 | #specific to the project 15 | ############################# 16 | cmake_built 17 | cmake_build 18 | build 19 | _install 20 | bin_x64 21 | NVPRO_EXTERNAL 22 | nvpro_core -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 3 | Project(${PROJNAME}) 4 | Message(STATUS "-------------------------------") 5 | Message(STATUS "Processing Project ${PROJNAME}:") 6 | 7 | ##################################################################################### 8 | # look for nvpro_core 1) as a sub-folder 2) at some other locations 9 | # this cannot be put anywhere else since we still didn't find setup.cmake yet 10 | # 11 | if(NOT BASE_DIRECTORY) 12 | 13 | find_path(BASE_DIRECTORY 14 | NAMES nvpro_core/cmake/setup.cmake 15 | PATHS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../.. 16 | REQUIRED 17 | DOC "Directory containing nvpro_core" 18 | ) 19 | endif() 20 | if(EXISTS ${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake) 21 | include(${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake) 22 | else() 23 | message(FATAL_ERROR "could not find base directory, please set BASE_DIRECTORY to folder containing nvpro_core") 24 | endif() 25 | 26 | _add_project_definitions(${PROJNAME}) 27 | 28 | ##################################################################################### 29 | # additions from packages needed for this sample 30 | # add refs in LIBRARIES_OPTIMIZED 31 | # add refs in LIBRARIES_DEBUG 32 | # add files in PACKAGE_SOURCE_FILES 33 | # 34 | _add_package_OpenGL() 35 | _add_package_ImGUI() 36 | 37 | ##################################################################################### 38 | # process the rest of some cmake code that needs to be done *after* the packages add 39 | _add_nvpro_core_lib() 40 | 41 | ##################################################################################### 42 | # Source files for this project 43 | # 44 | file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c) 45 | file(GLOB GLSL_FILES *.glsl) 46 | 47 | 48 | ##################################################################################### 49 | # Executable 50 | # 51 | if(WIN32 AND NOT GLUT_FOUND) 52 | add_definitions(/wd4267) #remove size_t to int warning 53 | add_definitions(/wd4996) #remove printf warning 54 | add_definitions(/wd4244) #remove double to float conversion warning 55 | add_definitions(/wd4305) #remove double to float truncation warning 56 | endif() 57 | 58 | add_executable(${PROJNAME} ${SOURCE_FILES} ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES} ${GLSL_FILES}) 59 | 60 | ##################################################################################### 61 | # common source code needed for this sample 62 | # 63 | source_group(common FILES 64 | ${COMMON_SOURCE_FILES} 65 | ${PACKAGE_SOURCE_FILES} 66 | ) 67 | source_group(shaders FILES 68 | ${GLSL_FILES} 69 | ) 70 | 71 | ##################################################################################### 72 | # Linkage 73 | # 74 | target_link_libraries(${PROJNAME} ${PLATFORM_LIBRARIES} nvpro_core) 75 | 76 | foreach(DEBUGLIB ${LIBRARIES_DEBUG}) 77 | target_link_libraries(${PROJNAME} debug ${DEBUGLIB}) 78 | endforeach(DEBUGLIB) 79 | 80 | foreach(RELEASELIB ${LIBRARIES_OPTIMIZED}) 81 | target_link_libraries(${PROJNAME} optimized ${RELEASELIB}) 82 | endforeach(RELEASELIB) 83 | 84 | ##################################################################################### 85 | # copies binaries that need to be put next to the exe files (ZLib, etc.) 86 | # 87 | _finalize_target( ${PROJNAME} ) 88 | LIST(APPEND GLSL_FILES "common.h") 89 | install(FILES ${GLSL_FILES} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/GLSL_${PROJNAME}") 90 | install(FILES ${GLSL_FILES} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/GLSL_${PROJNAME}") 91 | -------------------------------------------------------------------------------- /CONTRIBUTING: -------------------------------------------------------------------------------- 1 | https://developercertificate.org/ 2 | 3 | Developer Certificate of Origin 4 | Version 1.1 5 | 6 | Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 7 | 8 | Everyone is permitted to copy and distribute verbatim copies of this 9 | license document, but changing it is not allowed. 10 | 11 | 12 | Developer's Certificate of Origin 1.1 13 | 14 | By making a contribution to this project, I certify that: 15 | 16 | (a) The contribution was created in whole or in part by me and I 17 | have the right to submit it under the open source license 18 | indicated in the file; or 19 | 20 | (b) The contribution is based upon previous work that, to the best 21 | of my knowledge, is covered under an appropriate open source 22 | license and I have the right under that license to submit that 23 | work with modifications, whether created in whole or in part 24 | by me, under the same open source license (unless I am 25 | permitted to submit under a different license), as indicated 26 | in the file; or 27 | 28 | (c) The contribution was provided directly to me by some other 29 | person who certified (a), (b) or (c) and I have not modified 30 | it. 31 | 32 | (d) I understand and agree that this project and the contribution 33 | are public and that a record of the contribution (including all 34 | personal information I submit with it, including my sign-off) is 35 | maintained indefinitely and may be redistributed consistent with 36 | this project or the open source license(s) involved. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # gl ssao 2 | 3 | This sample implements screen space ambient occlusion (SSAO) using horizon-based ambient occlusion (HBAO). You can find some details about HBAO [here](http://www.nvidia.com/object/siggraph-2008-HBAO.html). It provides two alternative implementations the original hbao as well as an enhanced version that is more efficient in improved leveraging of the hardware's texture sampling cache, using [de-interleaved texturing](https://developer.nvidia.com/sites/default/files/akamai/gameworks/samples/DeinterleavedTexturing.pdf). 4 | 5 | ![sample screenshot](https://github.com/nvpro-samples/gl_ssao/blob/master/doc/sample.jpg) 6 | 7 | > Note: This sample provides an improved HBAO algorithm, however it is not same as HBAO+ which is part of [NVIDIA ShadowWorks ](https://developer.nvidia.com/shadowworks) and improves the quality and performance of the algorithm further. 8 | 9 | 10 | *HBAO - Classic*: 11 | - To achieve the effect a 4x4 texture that contains random directions is tiled across the screen and used to sample the neighborhood of a pixel's depth values. 12 | - The distance of the sampling depends on a customize-able world-size radius, for which the depth-buffer values are typically linearized first. 13 | - As the sampling radius depends on the pixel's depth, a big variability in the texture lookups can exist from one pixel to another. 14 | - To reduce the costs the effect can be computed at lower-resolution and up-scaled for final display. As AO is typically a low-frequency effect this often can be sufficient. 15 | - Dithering artifacts can occur due to the 4x4 texture tiling. The image is blurred using cross-bilateral filtering that takes the depth values into account, to further improve quality. 16 | 17 | *HBAO - Cache-Aware*: 18 | - The performance is vastly improved by grouping all pixels that share the same direction values. This means the screen-space linear depth buffer is stored in 16 layers each representing one direction of the 4x4 texture. Each layer has a quarter of the original resolution. The total amount of pixels is not reduced, but the sampling is performed in equal directions for the entire layer, yielding better texture cache utilization. 19 | - Linearizing the depth-buffer now stores into 16 texture layers. 20 | - The actual HBAO effect is performed in each layer individually, however all layers are independent of each other, allowing them to be processed in parallel. 21 | - Finally the results are stored scattered to their original locations in screen-space. 22 | - Compared to the regular HBAO approach, the efficiency gains allow using the effect on full-resolution, improving the image quality. 23 | 24 | *MSAA support*: 25 | - The effect is run on a per-sample level N times (N matching the MSAA level). 26 | - For each pass **glSampleMask( 1 << sample);** is used to update only the relevant samples in the target framebuffer. 27 | 28 | *Blur*: 29 | - A cross-bilteral blur is used to eliminate the typical dithering artifacts. It makes use of the depth buffer to avoid smoothing over geometric discontinuities. 30 | 31 | ![sample screenshot](https://github.com/nvpro-samples/gl_ssao/blob/master/doc/bluroff.jpg) 32 | 33 | #### Performance 34 | 35 | The cache-aware technique pays off on larger AO radii or higher resolutions (full HD). 36 | 37 | Timings in microseconds via GL timer query taken on a Quadro M6000, no MSAA, 1080p (sample default is 720p, which may give less difference between the two). 38 | 39 | *Classic* 40 | 41 | ``` 42 | Timer ssao; GL 2434; 43 | Timer linearize; GL 54; 44 | Timer ssaocalc; GL 2177; 45 | Timer ssaoblur; GL 198; 46 | ``` 47 | 48 | *Cache-Aware* 49 | 50 | ``` 51 | Timer ssao; GL 1264; 52 | Timer linearize; GL 55; 53 | Timer viewnormal; GL 76; 54 | Timer deinterleave; GL 93; 55 | Timer ssaocalc; GL 762; 56 | Timer reinterleave; GL 100; 57 | Timer ssaoblur; GL 167; 58 | ``` 59 | 60 | #### Sample Highlights 61 | 62 | The user can change MSAA settings, blur settings and other parameters. 63 | 64 | Key functionality is found in 65 | 66 | - Sample::drawHbaoClassic() 67 | - Sample::drawHbaoCacheAware() 68 | 69 | As well as in helper functions 70 | 71 | - Sample::drawLinearDepth() 72 | - Sample::drawHbaoBlur() 73 | 74 | The sample contains alternate codepaths for two additional optimizations, which are enabled by default. 75 | 76 | * ```USE_AO_SPECIALBLUR```: Depth is stored with the ssao calculation, so that the blur can use a single instead of two texture fetches, which improves performance. 77 | * ```USE_AO_LAYERED_SINGLEPASS```: In the cache-aware technique we update the layers of the ssao calculation all at once using image stores and attachment-les fbo or a geometry shader with layers, instead of rendering to each layer individually. 78 | 79 | ### Building 80 | Ideally, clone this and other interesting [nvpro-samples](https://github.com/nvpro-samples) repositories into a common subdirectory. You will always need [nvpro_core](https://github.com/nvpro-samples/nvpro_core). The nvpro_core is searched either as a subdirectory of the sample, or one directory up. 81 | 82 | If you are interested in multiple samples, you can use [build_all](https://github.com/nvpro-samples/build_all) CMAKE as entry point, it will also give you options to enable/disable individual samples when creating the solutions. 83 | 84 | ### Providing Pull Requests 85 | NVIDIA is happy to review and consider pull requests for merging into the main tree of the nvpro-samples for bug fixes and features. Before providing a pull request to NVIDIA, please note the following: 86 | 87 | * A pull request provided to this repo by a developer constitutes permission from the developer for NVIDIA to merge the provided changes or any NVIDIA modified version of these changes to the repo. NVIDIA may remove or change the code at any time and in any way deemed appropriate. 88 | * Not all pull requests can be or will be accepted. NVIDIA will close pull requests that it does not intend to merge. 89 | The modified files and any new files must include the unmodified NVIDIA copyright header seen at the top of all shipping files. 90 | -------------------------------------------------------------------------------- /bilateralblur.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | 22 | #version 430 23 | 24 | const float KERNEL_RADIUS = 3; 25 | 26 | layout(location=0) uniform float g_Sharpness; 27 | layout(location=1) uniform vec2 g_InvResolutionDirection; // either set x to 1/width or y to 1/height 28 | 29 | layout(binding=0) uniform sampler2D texSource; 30 | layout(binding=1) uniform sampler2D texLinearDepth; 31 | 32 | in vec2 texCoord; 33 | 34 | layout(location=0,index=0) out vec4 out_Color; 35 | 36 | 37 | //------------------------------------------------------------------------- 38 | 39 | vec4 BlurFunction(vec2 uv, float r, vec4 center_c, float center_d, inout float w_total) 40 | { 41 | vec4 c = texture2D( texSource, uv ); 42 | float d = texture2D( texLinearDepth, uv).x; 43 | 44 | const float BlurSigma = float(KERNEL_RADIUS) * 0.5; 45 | const float BlurFalloff = 1.0 / (2.0*BlurSigma*BlurSigma); 46 | 47 | float ddiff = (d - center_d) * g_Sharpness; 48 | float w = exp2(-r*r*BlurFalloff - ddiff*ddiff); 49 | w_total += w; 50 | 51 | return c*w; 52 | } 53 | 54 | void main() 55 | { 56 | vec4 center_c = texture2D( texSource, texCoord ); 57 | float center_d = texture2D( texLinearDepth, texCoord).x; 58 | 59 | vec4 c_total = center_c; 60 | float w_total = 1.0; 61 | 62 | for (float r = 1; r <= KERNEL_RADIUS; ++r) 63 | { 64 | vec2 uv = texCoord + g_InvResolutionDirection * r; 65 | c_total += BlurFunction(uv, r, center_c, center_d, w_total); 66 | } 67 | 68 | for (float r = 1; r <= KERNEL_RADIUS; ++r) 69 | { 70 | vec2 uv = texCoord - g_InvResolutionDirection * r; 71 | c_total += BlurFunction(uv, r, center_c, center_d, w_total); 72 | } 73 | 74 | out_Color = c_total/w_total; 75 | } 76 | -------------------------------------------------------------------------------- /common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #define VERTEX_POS 0 22 | #define VERTEX_NORMAL 1 23 | #define VERTEX_COLOR 2 24 | 25 | #define UBO_SCENE 0 26 | 27 | #define AO_RANDOMTEX_SIZE 4 28 | 29 | #ifdef __cplusplus 30 | namespace ssao 31 | { 32 | using namespace glm; 33 | #endif 34 | 35 | struct SceneData { 36 | mat4 viewProjMatrix; 37 | mat4 viewMatrix; 38 | mat4 viewMatrixIT; 39 | 40 | uvec2 viewport; 41 | uvec2 _pad; 42 | }; 43 | 44 | struct HBAOData { 45 | float RadiusToScreen; // radius 46 | float R2; // 1/radius 47 | float NegInvR2; // radius * radius 48 | float NDotVBias; 49 | 50 | vec2 InvFullResolution; 51 | vec2 InvQuarterResolution; 52 | 53 | float AOMultiplier; 54 | float PowExponent; 55 | vec2 _pad0; 56 | 57 | vec4 projInfo; 58 | vec2 projScale; 59 | int projOrtho; 60 | int _pad1; 61 | 62 | vec4 float2Offsets[AO_RANDOMTEX_SIZE*AO_RANDOMTEX_SIZE]; 63 | vec4 jitters[AO_RANDOMTEX_SIZE*AO_RANDOMTEX_SIZE]; 64 | }; 65 | 66 | #ifdef __cplusplus 67 | } 68 | #endif 69 | -------------------------------------------------------------------------------- /depthlinearize.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | 23 | #ifndef DEPTHLINEARIZE_USEMSAA 24 | #define DEPTHLINEARIZE_USEMSAA 0 25 | #endif 26 | 27 | layout(location=0) uniform vec4 clipInfo; // z_n * z_f, z_n - z_f, z_f, perspective = 1 : 0 28 | 29 | #if DEPTHLINEARIZE_MSAA 30 | layout(location=1) uniform int sampleIndex; 31 | layout(binding=0) uniform sampler2DMS inputTexture; 32 | #else 33 | layout(binding=0) uniform sampler2D inputTexture; 34 | #endif 35 | 36 | layout(location=0,index=0) out float out_Color; 37 | 38 | float reconstructCSZ(float d, vec4 clipInfo) { 39 | if (clipInfo[3] != 0) { 40 | return (clipInfo[0] / (clipInfo[1] * d + clipInfo[2])); 41 | } 42 | else { 43 | return (clipInfo[1]+clipInfo[2] - d * clipInfo[1]); 44 | } 45 | } 46 | /* 47 | if (in_perspective == 1.0) // perspective 48 | { 49 | ze = (zNear * zFar) / (zFar - zb * (zFar - zNear)); 50 | } 51 | else // orthographic proj 52 | { 53 | ze = zNear + zb * (zFar - zNear); 54 | } 55 | */ 56 | void main() { 57 | #if DEPTHLINEARIZE_MSAA 58 | float depth = texelFetch(inputTexture, ivec2(gl_FragCoord.xy), sampleIndex).x; 59 | #else 60 | float depth = texelFetch(inputTexture, ivec2(gl_FragCoord.xy), 0).x; 61 | #endif 62 | 63 | out_Color = reconstructCSZ(depth, clipInfo); 64 | } 65 | -------------------------------------------------------------------------------- /displaytex.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | 22 | #version 430 23 | 24 | layout(binding=0) uniform sampler2D inputTexture; 25 | 26 | layout(location=0,index=0) out vec4 out_Color; 27 | 28 | in vec2 texCoord; 29 | 30 | void main() 31 | { 32 | out_Color = texture(inputTexture, texCoord); 33 | } 34 | -------------------------------------------------------------------------------- /doc/bluroff.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/gl_ssao/f6b010dc7a05346518cd13d3368d8d830a382ed9/doc/bluroff.jpg -------------------------------------------------------------------------------- /doc/sample.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/gl_ssao/f6b010dc7a05346518cd13d3368d8d830a382ed9/doc/sample.jpg -------------------------------------------------------------------------------- /fullscreenquad.geo.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | /**/ 23 | 24 | #extension GL_ARB_shading_language_include : enable 25 | #include "common.h" 26 | 27 | layout(triangles) in; 28 | 29 | #extension GL_NV_geometry_shader_passthrough : enable 30 | 31 | #if GL_NV_geometry_shader_passthrough 32 | 33 | layout(passthrough) in gl_PerVertex { 34 | vec4 gl_Position; 35 | } gl_in[]; 36 | layout(passthrough) in Inputs { 37 | vec2 texCoord; 38 | } IN[]; 39 | 40 | void main() 41 | { 42 | gl_Layer = gl_PrimitiveIDIn; 43 | gl_PrimitiveID = gl_PrimitiveIDIn; 44 | } 45 | 46 | #else 47 | 48 | layout(triangle_strip,max_vertices=3) out; 49 | 50 | in Inputs { 51 | vec2 texCoord; 52 | } IN[]; 53 | out vec2 texCoord; 54 | 55 | void main() 56 | { 57 | for (int i = 0; i < 3; i++){ 58 | texCoord = IN[i].texCoord; 59 | gl_Layer = gl_PrimitiveIDIn; 60 | gl_PrimitiveID = gl_PrimitiveIDIn; 61 | gl_Position = gl_in[i].gl_Position; 62 | EmitVertex(); 63 | } 64 | } 65 | 66 | #endif 67 | -------------------------------------------------------------------------------- /fullscreenquad.vert.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | /**/ 23 | 24 | #extension GL_ARB_shading_language_include : enable 25 | #include "common.h" 26 | 27 | out vec2 texCoord; 28 | 29 | void main() 30 | { 31 | uint idx = gl_VertexID % 3; // allows rendering multiple fullscreen triangles 32 | vec4 pos = vec4( 33 | (float( idx &1U)) * 4.0 - 1.0, 34 | (float((idx>>1U)&1U)) * 4.0 - 1.0, 35 | 0, 1.0); 36 | gl_Position = pos; 37 | texCoord = pos.xy * 0.5 + 0.5; 38 | } 39 | -------------------------------------------------------------------------------- /hbao.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | /* 22 | Based on DeinterleavedTexturing sample by Louis Bavoil 23 | https://github.com/NVIDIAGameWorks/D3DSamples/tree/master/samples/DeinterleavedTexturing 24 | 25 | */ 26 | 27 | #version 430 28 | 29 | #extension GL_ARB_shading_language_include : enable 30 | #include "common.h" 31 | 32 | // The pragma below is critical for optimal performance 33 | // in this fragment shader to let the shader compiler 34 | // fully optimize the maths and batch the texture fetches 35 | // optimally 36 | 37 | #pragma optionNV(unroll all) 38 | 39 | #ifndef AO_DEINTERLEAVED 40 | #define AO_DEINTERLEAVED 1 41 | #endif 42 | 43 | #ifndef AO_BLUR 44 | #define AO_BLUR 1 45 | #endif 46 | 47 | #ifndef AO_LAYERED 48 | #define AO_LAYERED 1 49 | #endif 50 | 51 | #define M_PI 3.14159265f 52 | 53 | // tweakables 54 | const float NUM_STEPS = 4; 55 | const float NUM_DIRECTIONS = 8; // texRandom/g_Jitter initialization depends on this 56 | 57 | layout(std140,binding=0) uniform controlBuffer { 58 | HBAOData control; 59 | }; 60 | 61 | #if AO_DEINTERLEAVED 62 | 63 | #if AO_LAYERED 64 | vec2 g_Float2Offset = control.float2Offsets[gl_PrimitiveID].xy; 65 | vec4 g_Jitter = control.jitters[gl_PrimitiveID]; 66 | 67 | layout(binding=0) uniform sampler2DArray texLinearDepth; 68 | layout(binding=1) uniform sampler2D texViewNormal; 69 | 70 | vec3 getQuarterCoord(vec2 UV){ 71 | return vec3(UV,float(gl_PrimitiveID)); 72 | } 73 | #if AO_LAYERED == 1 74 | 75 | #if AO_BLUR 76 | layout(binding=0,rg16f) uniform image2DArray imgOutput; 77 | #else 78 | layout(binding=0,r8) uniform image2DArray imgOutput; 79 | #endif 80 | 81 | void outputColor(vec4 color) { 82 | imageStore(imgOutput, ivec3(ivec2(gl_FragCoord.xy),gl_PrimitiveID), color); 83 | } 84 | #else 85 | layout(location=0,index=0) out vec4 out_Color; 86 | 87 | void outputColor(vec4 color) { 88 | out_Color = color; 89 | } 90 | #endif 91 | #else 92 | layout(location=0) uniform vec2 g_Float2Offset; 93 | layout(location=1) uniform vec4 g_Jitter; 94 | 95 | layout(binding=0) uniform sampler2D texLinearDepth; 96 | layout(binding=1) uniform sampler2D texViewNormal; 97 | 98 | vec2 getQuarterCoord(vec2 UV){ 99 | return UV; 100 | } 101 | 102 | layout(location=0,index=0) out vec4 out_Color; 103 | 104 | void outputColor(vec4 color) { 105 | out_Color = color; 106 | } 107 | #endif 108 | 109 | #else 110 | layout(binding=0) uniform sampler2D texLinearDepth; 111 | layout(binding=1) uniform sampler2D texRandom; 112 | 113 | layout(location=0,index=0) out vec4 out_Color; 114 | 115 | void outputColor(vec4 color) { 116 | out_Color = color; 117 | } 118 | #endif 119 | 120 | in vec2 texCoord; 121 | 122 | //---------------------------------------------------------------------------------- 123 | 124 | vec3 UVToView(vec2 uv, float eye_z) 125 | { 126 | return vec3((uv * control.projInfo.xy + control.projInfo.zw) * (control.projOrtho != 0 ? 1. : eye_z), eye_z); 127 | } 128 | 129 | #if AO_DEINTERLEAVED 130 | 131 | vec3 FetchQuarterResViewPos(vec2 UV) 132 | { 133 | float ViewDepth = textureLod(texLinearDepth,getQuarterCoord(UV),0).x; 134 | return UVToView(UV, ViewDepth); 135 | } 136 | 137 | #else //AO_DEINTERLEAVED 138 | 139 | vec3 FetchViewPos(vec2 UV) 140 | { 141 | float ViewDepth = textureLod(texLinearDepth,UV,0).x; 142 | return UVToView(UV, ViewDepth); 143 | } 144 | 145 | vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) 146 | { 147 | vec3 V1 = Pr - P; 148 | vec3 V2 = P - Pl; 149 | return (dot(V1,V1) < dot(V2,V2)) ? V1 : V2; 150 | } 151 | 152 | vec3 ReconstructNormal(vec2 UV, vec3 P) 153 | { 154 | vec3 Pr = FetchViewPos(UV + vec2(control.InvFullResolution.x, 0)); 155 | vec3 Pl = FetchViewPos(UV + vec2(-control.InvFullResolution.x, 0)); 156 | vec3 Pt = FetchViewPos(UV + vec2(0, control.InvFullResolution.y)); 157 | vec3 Pb = FetchViewPos(UV + vec2(0, -control.InvFullResolution.y)); 158 | return normalize(cross(MinDiff(P, Pr, Pl), MinDiff(P, Pt, Pb))); 159 | } 160 | 161 | #endif //AO_DEINTERLEAVED 162 | 163 | //---------------------------------------------------------------------------------- 164 | float Falloff(float DistanceSquare) 165 | { 166 | // 1 scalar mad instruction 167 | return DistanceSquare * control.NegInvR2 + 1.0; 168 | } 169 | 170 | //---------------------------------------------------------------------------------- 171 | // P = view-space position at the kernel center 172 | // N = view-space normal at the kernel center 173 | // S = view-space position of the current sample 174 | //---------------------------------------------------------------------------------- 175 | float ComputeAO(vec3 P, vec3 N, vec3 S) 176 | { 177 | vec3 V = S - P; 178 | float VdotV = dot(V, V); 179 | float NdotV = dot(N, V) * 1.0/sqrt(VdotV); 180 | 181 | // Use saturate(x) instead of max(x,0.f) because that is faster on Kepler 182 | return clamp(NdotV - control.NDotVBias,0,1) * clamp(Falloff(VdotV),0,1); 183 | } 184 | 185 | //---------------------------------------------------------------------------------- 186 | vec2 RotateDirection(vec2 Dir, vec2 CosSin) 187 | { 188 | return vec2(Dir.x*CosSin.x - Dir.y*CosSin.y, 189 | Dir.x*CosSin.y + Dir.y*CosSin.x); 190 | } 191 | 192 | //---------------------------------------------------------------------------------- 193 | vec4 GetJitter() 194 | { 195 | #if AO_DEINTERLEAVED 196 | // Get the current jitter vector from the per-pass constant buffer 197 | return g_Jitter; 198 | #else 199 | // (cos(Alpha),sin(Alpha),rand1,rand2) 200 | return textureLod( texRandom, (gl_FragCoord.xy / AO_RANDOMTEX_SIZE), 0); 201 | #endif 202 | } 203 | 204 | //---------------------------------------------------------------------------------- 205 | float ComputeCoarseAO(vec2 FullResUV, float RadiusPixels, vec4 Rand, vec3 ViewPosition, vec3 ViewNormal) 206 | { 207 | #if AO_DEINTERLEAVED 208 | RadiusPixels /= 4.0; 209 | #endif 210 | 211 | // Divide by NUM_STEPS+1 so that the farthest samples are not fully attenuated 212 | float StepSizePixels = RadiusPixels / (NUM_STEPS + 1); 213 | 214 | const float Alpha = 2.0 * M_PI / NUM_DIRECTIONS; 215 | float AO = 0; 216 | 217 | for (float DirectionIndex = 0; DirectionIndex < NUM_DIRECTIONS; ++DirectionIndex) 218 | { 219 | float Angle = Alpha * DirectionIndex; 220 | 221 | // Compute normalized 2D direction 222 | vec2 Direction = RotateDirection(vec2(cos(Angle), sin(Angle)), Rand.xy); 223 | 224 | // Jitter starting sample within the first step 225 | float RayPixels = (Rand.z * StepSizePixels + 1.0); 226 | 227 | for (float StepIndex = 0; StepIndex < NUM_STEPS; ++StepIndex) 228 | { 229 | #if AO_DEINTERLEAVED 230 | vec2 SnappedUV = round(RayPixels * Direction) * control.InvQuarterResolution + FullResUV; 231 | vec3 S = FetchQuarterResViewPos(SnappedUV); 232 | #else 233 | vec2 SnappedUV = round(RayPixels * Direction) * control.InvFullResolution + FullResUV; 234 | vec3 S = FetchViewPos(SnappedUV); 235 | #endif 236 | 237 | RayPixels += StepSizePixels; 238 | 239 | AO += ComputeAO(ViewPosition, ViewNormal, S); 240 | } 241 | } 242 | 243 | AO *= control.AOMultiplier / (NUM_DIRECTIONS * NUM_STEPS); 244 | return clamp(1.0 - AO * 2.0,0,1); 245 | } 246 | 247 | //---------------------------------------------------------------------------------- 248 | void main() 249 | { 250 | 251 | #if AO_DEINTERLEAVED 252 | vec2 base = floor(gl_FragCoord.xy) * 4.0 + g_Float2Offset; 253 | vec2 uv = base * (control.InvQuarterResolution / 4.0); 254 | 255 | vec3 ViewPosition = FetchQuarterResViewPos(uv); 256 | vec4 NormalAndAO = texelFetch( texViewNormal, ivec2(base), 0); 257 | vec3 ViewNormal = -(NormalAndAO.xyz * 2.0 - 1.0); 258 | #else 259 | vec2 uv = texCoord; 260 | vec3 ViewPosition = FetchViewPos(uv); 261 | 262 | // Reconstruct view-space normal from nearest neighbors 263 | vec3 ViewNormal = -ReconstructNormal(uv, ViewPosition); 264 | #endif 265 | 266 | // Compute projection of disk of radius control.R into screen space 267 | float RadiusPixels = control.RadiusToScreen / (control.projOrtho != 0 ? 1.0 : ViewPosition.z); 268 | 269 | // Get jitter vector for the current full-res pixel 270 | vec4 Rand = GetJitter(); 271 | 272 | float AO = ComputeCoarseAO(uv, RadiusPixels, Rand, ViewPosition, ViewNormal); 273 | 274 | #if AO_BLUR 275 | outputColor(vec4(pow(AO, control.PowExponent), ViewPosition.z, 0, 0)); 276 | #else 277 | outputColor(vec4(pow(AO, control.PowExponent))); 278 | #endif 279 | 280 | } 281 | -------------------------------------------------------------------------------- /hbao_blur.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | 23 | const float KERNEL_RADIUS = 3; 24 | 25 | layout(location=0) uniform float g_Sharpness; 26 | layout(location=1) uniform vec2 g_InvResolutionDirection; // either set x to 1/width or y to 1/height 27 | 28 | layout(binding=0) uniform sampler2D texSource; 29 | 30 | in vec2 texCoord; 31 | 32 | layout(location=0,index=0) out vec4 out_Color; 33 | 34 | #ifndef AO_BLUR_PRESENT 35 | #define AO_BLUR_PRESENT 1 36 | #endif 37 | 38 | 39 | //------------------------------------------------------------------------- 40 | 41 | float BlurFunction(vec2 uv, float r, float center_c, float center_d, inout float w_total) 42 | { 43 | vec2 aoz = texture2D( texSource, uv ).xy; 44 | float c = aoz.x; 45 | float d = aoz.y; 46 | 47 | const float BlurSigma = float(KERNEL_RADIUS) * 0.5; 48 | const float BlurFalloff = 1.0 / (2.0*BlurSigma*BlurSigma); 49 | 50 | float ddiff = (d - center_d) * g_Sharpness; 51 | float w = exp2(-r*r*BlurFalloff - ddiff*ddiff); 52 | w_total += w; 53 | 54 | return c*w; 55 | } 56 | 57 | void main() 58 | { 59 | vec2 aoz = texture2D( texSource, texCoord ).xy; 60 | float center_c = aoz.x; 61 | float center_d = aoz.y; 62 | 63 | float c_total = center_c; 64 | float w_total = 1.0; 65 | 66 | for (float r = 1; r <= KERNEL_RADIUS; ++r) 67 | { 68 | vec2 uv = texCoord + g_InvResolutionDirection * r; 69 | c_total += BlurFunction(uv, r, center_c, center_d, w_total); 70 | } 71 | 72 | for (float r = 1; r <= KERNEL_RADIUS; ++r) 73 | { 74 | vec2 uv = texCoord - g_InvResolutionDirection * r; 75 | c_total += BlurFunction(uv, r, center_c, center_d, w_total); 76 | } 77 | 78 | #if AO_BLUR_PRESENT 79 | out_Color = vec4(c_total/w_total); 80 | #else 81 | out_Color = vec4(c_total/w_total, center_d, 0, 0); 82 | #endif 83 | } 84 | -------------------------------------------------------------------------------- /hbao_deinterleave.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | 23 | layout(location=0) uniform vec4 info; // xy 24 | vec2 uvOffset = info.xy; 25 | vec2 invResolution = info.zw; 26 | 27 | layout(binding=0) uniform sampler2D texLinearDepth; 28 | 29 | layout(location=0,index=0) out float out_Color[8]; 30 | 31 | //---------------------------------------------------------------------------------- 32 | 33 | #if 1 34 | void main() { 35 | vec2 uv = floor(gl_FragCoord.xy) * 4.0 + uvOffset + 0.5; 36 | uv *= invResolution; 37 | 38 | vec4 S0 = textureGather(texLinearDepth, uv, 0); 39 | vec4 S1 = textureGatherOffset(texLinearDepth, uv, ivec2(2,0), 0); 40 | 41 | out_Color[0] = S0.w; 42 | out_Color[1] = S0.z; 43 | out_Color[2] = S1.w; 44 | out_Color[3] = S1.z; 45 | out_Color[4] = S0.x; 46 | out_Color[5] = S0.y; 47 | out_Color[6] = S1.x; 48 | out_Color[7] = S1.y; 49 | } 50 | #else 51 | void main() { 52 | vec2 uv = floor(gl_FragCoord.xy) * 4.0 + uvOffset; 53 | ivec2 tc = ivec2(uv); 54 | 55 | out_Color[0] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(0,0)).x; 56 | out_Color[1] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(1,0)).x; 57 | out_Color[2] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(2,0)).x; 58 | out_Color[3] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(3,0)).x; 59 | out_Color[4] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(0,1)).x; 60 | out_Color[5] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(1,1)).x; 61 | out_Color[6] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(2,1)).x; 62 | out_Color[7] = texelFetchOffset(texLinearDepth, tc, 0, ivec2(3,1)).x; 63 | } 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /hbao_reinterleave.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | 23 | #ifndef AO_BLUR 24 | #define AO_BLUR 1 25 | #endif 26 | 27 | layout(binding=0) uniform sampler2DArray texResultsArray; 28 | 29 | layout(location=0,index=0) out vec4 out_Color; 30 | 31 | //---------------------------------------------------------------------------------- 32 | 33 | void main() { 34 | ivec2 FullResPos = ivec2(gl_FragCoord.xy); 35 | ivec2 Offset = FullResPos & 3; 36 | int SliceId = Offset.y * 4 + Offset.x; 37 | ivec2 QuarterResPos = FullResPos >> 2; 38 | 39 | #if AO_BLUR 40 | out_Color = vec4(texelFetch( texResultsArray, ivec3(QuarterResPos, SliceId), 0).xy,0,0); 41 | #else 42 | out_Color = vec4(texelFetch( texResultsArray, ivec3(QuarterResPos, SliceId), 0).x); 43 | #endif 44 | 45 | } 46 | -------------------------------------------------------------------------------- /scene.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | /**/ 23 | 24 | #extension GL_ARB_shading_language_include : enable 25 | #include "common.h" 26 | 27 | layout(std140,binding=UBO_SCENE) uniform sceneBuffer { 28 | SceneData scene; 29 | }; 30 | 31 | in Interpolants { 32 | vec3 pos; 33 | vec3 normal; 34 | flat vec4 color; 35 | } IN; 36 | 37 | layout(location=0,index=0) out vec4 out_Color; 38 | 39 | void main() 40 | { 41 | vec3 light = normalize(vec3(-1,2,1)); 42 | float intensity = dot(normalize(IN.normal),light) * 0.5 + 0.5; 43 | vec4 color = IN.color * mix(vec4(0,0.25,0.75,0),vec4(1,1,1,0),intensity); 44 | 45 | out_Color = color; 46 | } 47 | -------------------------------------------------------------------------------- /scene.vert.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | /**/ 23 | 24 | #extension GL_ARB_shading_language_include : enable 25 | #include "common.h" 26 | 27 | layout(std140,binding=UBO_SCENE) uniform sceneBuffer { 28 | SceneData scene; 29 | }; 30 | 31 | in layout(location=VERTEX_POS) vec3 pos; 32 | in layout(location=VERTEX_NORMAL) vec3 normal; 33 | in layout(location=VERTEX_COLOR) vec4 color; 34 | 35 | out Interpolants { 36 | vec3 pos; 37 | vec3 normal; 38 | flat vec4 color; 39 | } OUT; 40 | 41 | void main() 42 | { 43 | gl_Position = scene.viewProjMatrix * vec4(pos,1); 44 | OUT.pos = pos; 45 | OUT.normal = normal; 46 | OUT.color = color; 47 | } 48 | -------------------------------------------------------------------------------- /ssao.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | /* Contact ckubisch@nvidia.com (Christoph Kubisch) for feedback */ 22 | 23 | #define DEBUG_FILTER 1 24 | 25 | #include 26 | #include 27 | 28 | #include 29 | 30 | #include 31 | #include 32 | 33 | #include 34 | #include 35 | #include 36 | 37 | #include 38 | #include 39 | #include 40 | #include 41 | 42 | #include 43 | 44 | // optimizes blur, by storing depth along with ssao calculation 45 | // avoids accessing two different textures 46 | #define USE_AO_SPECIALBLUR 1 47 | 48 | // optimizes the cache-aware technique by rendering all temporary layers at once 49 | // instead of individually 50 | 51 | #define AO_LAYERED_OFF 0 52 | #define AO_LAYERED_IMAGE 1 53 | #define AO_LAYERED_GS 2 54 | 55 | #define USE_AO_LAYERED_SINGLEPASS AO_LAYERED_GS 56 | 57 | 58 | #include "common.h" 59 | 60 | namespace ssao { 61 | #if 0 62 | int const SAMPLE_SIZE_WIDTH(1280); 63 | int const SAMPLE_SIZE_HEIGHT(720); 64 | #else 65 | int const SAMPLE_SIZE_WIDTH(1920); 66 | int const SAMPLE_SIZE_HEIGHT(1080); 67 | #endif 68 | int const SAMPLE_MAJOR_VERSION(4); 69 | int const SAMPLE_MINOR_VERSION(5); 70 | 71 | static const int NUM_MRT = 8; 72 | static const int HBAO_RANDOM_SIZE = AO_RANDOMTEX_SIZE; 73 | static const int HBAO_RANDOM_ELEMENTS = HBAO_RANDOM_SIZE * HBAO_RANDOM_SIZE; 74 | static const int MAX_SAMPLES = 8; 75 | 76 | static const int grid = 32; 77 | static const float globalscale = 16.0f; 78 | 79 | class Sample : public nvgl::AppWindowProfilerGL 80 | { 81 | enum AlgorithmType 82 | { 83 | ALGORITHM_NONE, 84 | ALGORITHM_HBAO_CACHEAWARE, 85 | ALGORITHM_HBAO_CLASSIC, 86 | NUM_ALGORITHMS, 87 | }; 88 | 89 | enum GuiEnums 90 | { 91 | GUI_ALGORITHM, 92 | GUI_MSAA, 93 | }; 94 | 95 | struct 96 | { 97 | nvgl::ProgramID draw_scene, depth_linearize, depth_linearize_msaa, viewnormal, bilateralblur, displaytex, 98 | 99 | hbao_calc, hbao_calc_blur, hbao_blur, hbao_blur2, 100 | 101 | hbao2_deinterleave, hbao2_calc, hbao2_calc_blur, hbao2_reinterleave, hbao2_reinterleave_blur; 102 | 103 | } programs; 104 | 105 | struct 106 | { 107 | GLuint scene = 0; 108 | GLuint depthlinear = 0; 109 | GLuint viewnormal = 0; 110 | GLuint hbao_calc = 0; 111 | GLuint hbao2_deinterleave = 0; 112 | GLuint hbao2_calc = 0; 113 | } fbos; 114 | 115 | struct 116 | { 117 | GLuint scene_vbo = 0; 118 | GLuint scene_ibo = 0; 119 | GLuint scene_ubo = 0; 120 | GLuint hbao_ubo = 0; 121 | } buffers; 122 | 123 | struct 124 | { 125 | GLuint scene_color = 0; 126 | GLuint scene_depthstencil = 0; 127 | GLuint scene_depthlinear = 0; 128 | GLuint scene_viewnormal = 0; 129 | GLuint hbao_result = 0; 130 | GLuint hbao_blur = 0; 131 | GLuint hbao_random = 0; 132 | GLuint hbao_randomview[MAX_SAMPLES] = {0}; 133 | GLuint hbao2_deptharray = 0; 134 | GLuint hbao2_depthview[HBAO_RANDOM_ELEMENTS] = {0}; 135 | GLuint hbao2_resultarray = 0; 136 | } textures; 137 | 138 | struct Vertex 139 | { 140 | 141 | Vertex(const nvh::geometry::Vertex& vertex) 142 | { 143 | position = vertex.position; 144 | normal = vertex.normal; 145 | color = glm::vec4(1.0f); 146 | } 147 | 148 | glm::vec4 position; 149 | glm::vec4 normal; 150 | glm::vec4 color; 151 | }; 152 | 153 | 154 | struct Tweak 155 | { 156 | int samples = 1; 157 | AlgorithmType algorithm = ALGORITHM_HBAO_CACHEAWARE; 158 | float intensity = 1.5f; 159 | float bias = 0.1f; 160 | float radius = 2.0f; 161 | float blurSharpness = 40.0f; 162 | bool blur = true; 163 | bool ortho = false; 164 | }; 165 | 166 | struct Projection 167 | { 168 | float nearplane = 0.1f; 169 | float farplane = 100.0f; 170 | float fov = 45.0f; 171 | float orthoheight = 1.0f; 172 | bool ortho = false; 173 | mat4 matrix; 174 | 175 | void update(int width, int height) 176 | { 177 | float aspect = float(width) / float(height); 178 | if(ortho) 179 | { 180 | matrix = glm::ortho(-orthoheight * 0.5f * aspect, orthoheight * 0.5f * aspect, -orthoheight * 0.5f, 181 | orthoheight * 0.5f, nearplane, farplane); 182 | } 183 | else 184 | { 185 | matrix = glm::perspectiveRH_ZO(fov, aspect, nearplane, farplane); 186 | } 187 | } 188 | }; 189 | 190 | ImGuiH::Registry m_ui; 191 | double m_uiTime = 0; 192 | 193 | nvgl::ProgramManager m_progManager; 194 | nvh::CameraControl m_control; 195 | 196 | Tweak m_tweak; 197 | Tweak m_tweakLast; 198 | 199 | uint m_sceneTriangleIndices; 200 | uint m_sceneObjects; 201 | 202 | Projection m_projection; 203 | 204 | SceneData m_sceneUbo; 205 | HBAOData m_hbaoUbo; 206 | glm::vec4 m_hbaoRandom[HBAO_RANDOM_ELEMENTS * MAX_SAMPLES]; 207 | 208 | bool begin(); 209 | void processUI(double time); 210 | void think(double time); 211 | void resize(int width, int height); 212 | 213 | void prepareHbaoData(const Projection& projection, int width, int height); 214 | 215 | void drawLinearDepth(const Projection& projection, int width, int height, int sampleIdx); 216 | void drawHbaoBlur(const Projection& projection, int width, int height, int sampleIdx); 217 | void drawHbaoClassic(const Projection& projection, int width, int height, int sampleIdx); 218 | void drawHbaoCacheAware(const Projection& projection, int width, int height, int sampleIdx); 219 | 220 | bool initProgram(); 221 | bool initScene(); 222 | bool initMisc(); 223 | bool initFramebuffers(int width, int height, int samples); 224 | 225 | void end() { ImGui::ShutdownGL(); } 226 | // return true to prevent m_windowState updates 227 | bool mouse_pos(int x, int y) { return ImGuiH::mouse_pos(x, y); } 228 | bool mouse_button(int button, int action) { return ImGuiH::mouse_button(button, action); } 229 | bool mouse_wheel(int wheel) { return ImGuiH::mouse_wheel(wheel); } 230 | bool key_char(int button) { return ImGuiH::key_char(button); } 231 | bool key_button(int button, int action, int mods) { return ImGuiH::key_button(button, action, mods); } 232 | 233 | public: 234 | Sample() 235 | { 236 | m_parameterList.add("algorithm", (uint32_t*)&m_tweak.algorithm); 237 | m_parameterList.add("blur", &m_tweak.blur); 238 | m_parameterList.add("ortho", &m_tweak.ortho); 239 | m_parameterList.add("msaa", &m_tweak.samples); 240 | } 241 | }; 242 | 243 | bool Sample::initProgram() 244 | { 245 | bool validated(true); 246 | m_progManager.m_filetype = nvh::ShaderFileManager::FILETYPE_GLSL; 247 | m_progManager.addDirectory(std::string("GLSL_" PROJECT_NAME)); 248 | m_progManager.addDirectory(exePath() + std::string(PROJECT_RELDIRECTORY)); 249 | 250 | m_progManager.registerInclude("common.h"); 251 | 252 | m_progManager.m_prepend = nvgl::ProgramManager::format("#define AO_LAYERED %d\n", USE_AO_LAYERED_SINGLEPASS); 253 | 254 | programs.draw_scene = m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "scene.vert.glsl"), 255 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "scene.frag.glsl")); 256 | 257 | programs.bilateralblur = 258 | m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 259 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "bilateralblur.frag.glsl")); 260 | 261 | programs.depth_linearize = m_progManager.createProgram( 262 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 263 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define DEPTHLINEARIZE_MSAA 0\n", "depthlinearize.frag.glsl")); 264 | 265 | programs.depth_linearize_msaa = m_progManager.createProgram( 266 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 267 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define DEPTHLINEARIZE_MSAA 1\n", "depthlinearize.frag.glsl")); 268 | 269 | programs.viewnormal = 270 | m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 271 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "viewnormal.frag.glsl")); 272 | 273 | programs.displaytex = 274 | m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 275 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "displaytex.frag.glsl")); 276 | 277 | programs.hbao_calc = m_progManager.createProgram( 278 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 279 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_DEINTERLEAVED 0\n#define AO_BLUR 0\n", "hbao.frag.glsl")); 280 | 281 | programs.hbao_calc_blur = m_progManager.createProgram( 282 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 283 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_DEINTERLEAVED 0\n#define AO_BLUR 1\n", "hbao.frag.glsl")); 284 | 285 | programs.hbao_blur = m_progManager.createProgram( 286 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 287 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_BLUR_PRESENT 0\n", "hbao_blur.frag.glsl")); 288 | 289 | programs.hbao_blur2 = m_progManager.createProgram( 290 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 291 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_BLUR_PRESENT 1\n", "hbao_blur.frag.glsl")); 292 | 293 | programs.hbao2_calc = m_progManager.createProgram( 294 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 295 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_GS 296 | nvgl::ProgramManager::Definition(GL_GEOMETRY_SHADER, "fullscreenquad.geo.glsl"), 297 | #endif 298 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_DEINTERLEAVED 1\n#define AO_BLUR 0\n", "hbao.frag.glsl")); 299 | 300 | programs.hbao2_calc_blur = m_progManager.createProgram( 301 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 302 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_GS 303 | nvgl::ProgramManager::Definition(GL_GEOMETRY_SHADER, "fullscreenquad.geo.glsl"), 304 | #endif 305 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_DEINTERLEAVED 1\n#define AO_BLUR 1\n", "hbao.frag.glsl")); 306 | 307 | programs.hbao2_deinterleave = 308 | m_progManager.createProgram(nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 309 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "hbao_deinterleave.frag.glsl")); 310 | 311 | programs.hbao2_reinterleave = m_progManager.createProgram( 312 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 313 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_BLUR 0\n", "hbao_reinterleave.frag.glsl")); 314 | 315 | programs.hbao2_reinterleave_blur = m_progManager.createProgram( 316 | nvgl::ProgramManager::Definition(GL_VERTEX_SHADER, "fullscreenquad.vert.glsl"), 317 | nvgl::ProgramManager::Definition(GL_FRAGMENT_SHADER, "#define AO_BLUR 1\n", "hbao_reinterleave.frag.glsl")); 318 | 319 | validated = m_progManager.areProgramsValid(); 320 | 321 | return validated; 322 | } 323 | 324 | 325 | bool Sample::initMisc() 326 | { 327 | std::mt19937 rmt; 328 | 329 | float numDir = 8; // keep in sync to glsl 330 | 331 | signed short hbaoRandomShort[HBAO_RANDOM_ELEMENTS * MAX_SAMPLES * 4]; 332 | 333 | for(int i = 0; i < HBAO_RANDOM_ELEMENTS * MAX_SAMPLES; i++) 334 | { 335 | float Rand1 = static_cast(rmt()) / 4294967296.0f; 336 | float Rand2 = static_cast(rmt()) / 4294967296.0f; 337 | 338 | // Use random rotation angles in [0,2PI/NUM_DIRECTIONS) 339 | float Angle = glm::two_pi() * Rand1 / numDir; 340 | m_hbaoRandom[i].x = cosf(Angle); 341 | m_hbaoRandom[i].y = sinf(Angle); 342 | m_hbaoRandom[i].z = Rand2; 343 | m_hbaoRandom[i].w = 0; 344 | #define SCALE ((1 << 15)) 345 | hbaoRandomShort[i * 4 + 0] = (signed short)(SCALE * m_hbaoRandom[i].x); 346 | hbaoRandomShort[i * 4 + 1] = (signed short)(SCALE * m_hbaoRandom[i].y); 347 | hbaoRandomShort[i * 4 + 2] = (signed short)(SCALE * m_hbaoRandom[i].z); 348 | hbaoRandomShort[i * 4 + 3] = (signed short)(SCALE * m_hbaoRandom[i].w); 349 | #undef SCALE 350 | } 351 | 352 | nvgl::newTexture(textures.hbao_random, GL_TEXTURE_2D_ARRAY); 353 | glBindTexture(GL_TEXTURE_2D_ARRAY, textures.hbao_random); 354 | glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_RGBA16_SNORM, HBAO_RANDOM_SIZE, HBAO_RANDOM_SIZE, MAX_SAMPLES); 355 | glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, HBAO_RANDOM_SIZE, HBAO_RANDOM_SIZE, MAX_SAMPLES, GL_RGBA, GL_SHORT, hbaoRandomShort); 356 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 357 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 358 | glBindTexture(GL_TEXTURE_2D_ARRAY, 0); 359 | 360 | for(int i = 0; i < MAX_SAMPLES; i++) 361 | { 362 | glGenTextures(1, &textures.hbao_randomview[i]); 363 | glTextureView(textures.hbao_randomview[i], GL_TEXTURE_2D, textures.hbao_random, GL_RGBA16_SNORM, 0, 1, i, 1); 364 | glBindTexture(GL_TEXTURE_2D, textures.hbao_randomview[i]); 365 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 366 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 367 | glBindTexture(GL_TEXTURE_2D, 0); 368 | } 369 | 370 | nvgl::newBuffer(buffers.hbao_ubo); 371 | glNamedBufferStorage(buffers.hbao_ubo, sizeof(HBAOData), NULL, GL_DYNAMIC_STORAGE_BIT); 372 | 373 | return true; 374 | } 375 | 376 | bool Sample::initScene() 377 | { 378 | { // Scene Geometry 379 | nvh::geometry::Mesh scene; 380 | const int LEVELS = 4; 381 | 382 | m_sceneObjects = 0; 383 | for(int i = 0; i < grid * grid; i++) 384 | { 385 | 386 | vec4 color(nvh::frand(), nvh::frand(), nvh::frand(), 1.0f); 387 | color *= 0.25f; 388 | color += 0.75f; 389 | 390 | vec2 posxy(i % grid, i / grid); 391 | 392 | float depth = sin(posxy.x * 0.1f) * cos(posxy.y * 0.1f) * 2.0f; 393 | 394 | 395 | for(int l = 0; l < LEVELS; l++) 396 | { 397 | vec3 pos(posxy.x, posxy.y, depth); 398 | 399 | float scale = globalscale * 0.5f / float(grid); 400 | if(l != 0) 401 | { 402 | scale *= powf(0.9f, float(l)); 403 | scale *= nvh::frand() * 0.5f + 0.5f; 404 | } 405 | 406 | vec3 size = vec3(scale); 407 | 408 | 409 | size.z *= nvh::frand() * 1.0f + 1.0f; 410 | if(l != 0) 411 | { 412 | size.z *= powf(0.7f, float(l)); 413 | } 414 | 415 | pos -= vec3(grid / 2, grid / 2, 0); 416 | pos /= float(grid) / globalscale; 417 | 418 | depth += size.z; 419 | 420 | pos.z = depth; 421 | 422 | mat4 matrix = glm::translate(glm::mat4(1.f), pos) * glm::scale(glm::mat4(1.f), size); 423 | 424 | uint oldverts = scene.getVerticesCount(); 425 | uint oldinds = scene.getTriangleIndicesCount(); 426 | 427 | nvh::geometry::Box::add(scene, matrix, 2, 2, 2); 428 | 429 | for(uint v = oldverts; v < scene.getVerticesCount(); v++) 430 | { 431 | scene.m_vertices[v].color = color; 432 | } 433 | 434 | depth += size.z; 435 | } 436 | 437 | m_sceneObjects++; 438 | } 439 | 440 | m_sceneTriangleIndices = scene.getTriangleIndicesCount(); 441 | 442 | nvgl::newBuffer(buffers.scene_ibo); 443 | glNamedBufferStorage(buffers.scene_ibo, scene.getTriangleIndicesSize(), &scene.m_indicesTriangles[0], 0); 444 | 445 | nvgl::newBuffer(buffers.scene_vbo); 446 | glBindBuffer(GL_ARRAY_BUFFER, buffers.scene_vbo); 447 | glNamedBufferStorage(buffers.scene_vbo, scene.getVerticesSize(), &scene.m_vertices[0], 0); 448 | 449 | glVertexAttribFormat(VERTEX_COLOR, 4, GL_FLOAT, GL_FALSE, offsetof(Vertex, color)); 450 | glVertexAttribBinding(VERTEX_COLOR, 0); 451 | 452 | glVertexAttribFormat(VERTEX_POS, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex, position)); 453 | glVertexAttribFormat(VERTEX_NORMAL, 3, GL_FLOAT, GL_FALSE, offsetof(Vertex, normal)); 454 | glVertexAttribBinding(VERTEX_POS, 0); 455 | glVertexAttribBinding(VERTEX_NORMAL, 0); 456 | } 457 | 458 | { // Scene UBO 459 | nvgl::newBuffer(buffers.scene_ubo); 460 | glNamedBufferStorage(buffers.scene_ubo, sizeof(SceneData), NULL, GL_DYNAMIC_STORAGE_BIT); 461 | } 462 | 463 | return true; 464 | } 465 | 466 | bool Sample::initFramebuffers(int width, int height, int samples) 467 | { 468 | 469 | if(samples > 1) 470 | { 471 | nvgl::newTexture(textures.scene_color, GL_TEXTURE_2D_MULTISAMPLE); 472 | glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures.scene_color); 473 | glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGBA8, width, height, GL_FALSE); 474 | glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); 475 | 476 | nvgl::newTexture(textures.scene_depthstencil, GL_TEXTURE_2D_MULTISAMPLE); 477 | glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, textures.scene_depthstencil); 478 | glTexStorage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_DEPTH24_STENCIL8, width, height, GL_FALSE); 479 | glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0); 480 | } 481 | else 482 | { 483 | nvgl::newTexture(textures.scene_color, GL_TEXTURE_2D); 484 | glBindTexture(GL_TEXTURE_2D, textures.scene_color); 485 | glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); 486 | glBindTexture(GL_TEXTURE_2D, 0); 487 | 488 | nvgl::newTexture(textures.scene_depthstencil, GL_TEXTURE_2D); 489 | glBindTexture(GL_TEXTURE_2D, textures.scene_depthstencil); 490 | glTexStorage2D(GL_TEXTURE_2D, 1, GL_DEPTH24_STENCIL8, width, height); 491 | glBindTexture(GL_TEXTURE_2D, 0); 492 | } 493 | 494 | nvgl::newFramebuffer(fbos.scene); 495 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene); 496 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.scene_color, 0); 497 | glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, textures.scene_depthstencil, 0); 498 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 499 | 500 | 501 | nvgl::newTexture(textures.scene_depthlinear, GL_TEXTURE_2D); 502 | glBindTexture(GL_TEXTURE_2D, textures.scene_depthlinear); 503 | glTexStorage2D(GL_TEXTURE_2D, 1, GL_R32F, width, height); 504 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 505 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 506 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 507 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 508 | glBindTexture(GL_TEXTURE_2D, 0); 509 | 510 | nvgl::newFramebuffer(fbos.depthlinear); 511 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.depthlinear); 512 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.scene_depthlinear, 0); 513 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 514 | 515 | nvgl::newTexture(textures.scene_viewnormal, GL_TEXTURE_2D); 516 | glBindTexture(GL_TEXTURE_2D, textures.scene_viewnormal); 517 | glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8, width, height); 518 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 519 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 520 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 521 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 522 | glBindTexture(GL_TEXTURE_2D, 0); 523 | 524 | nvgl::newFramebuffer(fbos.viewnormal); 525 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.viewnormal); 526 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.scene_viewnormal, 0); 527 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 528 | 529 | // hbao 530 | 531 | #if USE_AO_SPECIALBLUR 532 | GLenum formatAO = GL_RG16F; 533 | GLint swizzle[4] = {GL_RED, GL_GREEN, GL_ZERO, GL_ZERO}; 534 | #else 535 | GLenum formatAO = GL_R8; 536 | GLint swizzle[4] = {GL_RED, GL_RED, GL_RED, GL_RED}; 537 | #endif 538 | 539 | nvgl::newTexture(textures.hbao_result, GL_TEXTURE_2D); 540 | glBindTexture(GL_TEXTURE_2D, textures.hbao_result); 541 | glTexStorage2D(GL_TEXTURE_2D, 1, formatAO, width, height); 542 | glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); 543 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 544 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 545 | glBindTexture(GL_TEXTURE_2D, 0); 546 | 547 | nvgl::newTexture(textures.hbao_blur, GL_TEXTURE_2D); 548 | glBindTexture(GL_TEXTURE_2D, textures.hbao_blur); 549 | glTexStorage2D(GL_TEXTURE_2D, 1, formatAO, width, height); 550 | glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzle); 551 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 552 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 553 | glBindTexture(GL_TEXTURE_2D, 0); 554 | 555 | nvgl::newFramebuffer(fbos.hbao_calc); 556 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao_calc); 557 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.hbao_result, 0); 558 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, textures.hbao_blur, 0); 559 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 560 | 561 | // interleaved hbao 562 | 563 | int quarterWidth = ((width + 3) / 4); 564 | int quarterHeight = ((height + 3) / 4); 565 | 566 | nvgl::newTexture(textures.hbao2_deptharray, GL_TEXTURE_2D_ARRAY); 567 | glBindTexture(GL_TEXTURE_2D_ARRAY, textures.hbao2_deptharray); 568 | glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, GL_R32F, quarterWidth, quarterHeight, HBAO_RANDOM_ELEMENTS); 569 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 570 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 571 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 572 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 573 | glBindTexture(GL_TEXTURE_2D_ARRAY, 0); 574 | 575 | for(int i = 0; i < HBAO_RANDOM_ELEMENTS; i++) 576 | { 577 | if(textures.hbao2_depthview[i]) 578 | { 579 | glDeleteTextures(1, &textures.hbao2_depthview[i]); 580 | } 581 | glGenTextures(1, &textures.hbao2_depthview[i]); 582 | glTextureView(textures.hbao2_depthview[i], GL_TEXTURE_2D, textures.hbao2_deptharray, GL_R32F, 0, 1, i, 1); 583 | glBindTexture(GL_TEXTURE_2D, textures.hbao2_depthview[i]); 584 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 585 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 586 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 587 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 588 | glBindTexture(GL_TEXTURE_2D, 0); 589 | } 590 | 591 | 592 | nvgl::newTexture(textures.hbao2_resultarray, GL_TEXTURE_2D_ARRAY); 593 | glBindTexture(GL_TEXTURE_2D_ARRAY, textures.hbao2_resultarray); 594 | glTexStorage3D(GL_TEXTURE_2D_ARRAY, 1, formatAO, quarterWidth, quarterHeight, HBAO_RANDOM_ELEMENTS); 595 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); 596 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); 597 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 598 | glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 599 | glBindTexture(GL_TEXTURE_2D_ARRAY, 0); 600 | 601 | 602 | GLenum drawbuffers[NUM_MRT]; 603 | for(int layer = 0; layer < NUM_MRT; layer++) 604 | { 605 | drawbuffers[layer] = GL_COLOR_ATTACHMENT0 + layer; 606 | } 607 | 608 | nvgl::newFramebuffer(fbos.hbao2_deinterleave); 609 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao2_deinterleave); 610 | glDrawBuffers(NUM_MRT, drawbuffers); 611 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 612 | 613 | nvgl::newFramebuffer(fbos.hbao2_calc); 614 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao2_calc); 615 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_IMAGE 616 | // this fbo will not have any attachments and therefore requires rasterizer to be configured 617 | // through default parameters 618 | glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_WIDTH, quarterWidth); 619 | glFramebufferParameteri(GL_FRAMEBUFFER, GL_FRAMEBUFFER_DEFAULT_HEIGHT, quarterHeight); 620 | #endif 621 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_GS 622 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.hbao2_resultarray, 0); 623 | #endif 624 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 625 | 626 | return true; 627 | } 628 | 629 | 630 | bool Sample::begin() 631 | { 632 | ImGuiH::Init(m_windowState.m_winSize[0], m_windowState.m_winSize[1], this, ImGuiH::FONT_MONOSPACED_SCALED); 633 | ImGui::InitGL(); 634 | 635 | glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 636 | glEnable(GL_CULL_FACE); 637 | glEnable(GL_DEPTH_TEST); 638 | 639 | bool validated(true); 640 | 641 | GLuint defaultVAO; 642 | glGenVertexArrays(1, &defaultVAO); 643 | glBindVertexArray(defaultVAO); 644 | 645 | validated = validated && initProgram(); 646 | validated = validated && initMisc(); 647 | validated = validated && initScene(); 648 | validated = validated && initFramebuffers(m_windowState.m_winSize[0], m_windowState.m_winSize[1], m_tweak.samples); 649 | 650 | m_ui.enumAdd(GUI_ALGORITHM, ALGORITHM_NONE, "none"); 651 | m_ui.enumAdd(GUI_ALGORITHM, ALGORITHM_HBAO_CACHEAWARE, "hbao cache-aware"); 652 | m_ui.enumAdd(GUI_ALGORITHM, ALGORITHM_HBAO_CLASSIC, "hbao classic"); 653 | 654 | m_ui.enumAdd(GUI_MSAA, 1, "none"); 655 | m_ui.enumAdd(GUI_MSAA, 2, "2x"); 656 | m_ui.enumAdd(GUI_MSAA, 4, "4x"); 657 | m_ui.enumAdd(GUI_MSAA, 8, "8x"); 658 | 659 | m_control.m_sceneOrbit = vec3(0.0f); 660 | m_control.m_sceneDimension = float(globalscale); 661 | m_control.m_sceneOrthoZoom = m_control.m_sceneDimension; 662 | m_control.m_viewMatrix = glm::lookAt(m_control.m_sceneOrbit - (vec3(0.4f, -0.35f, -0.6f) * m_control.m_sceneDimension * 0.9f), 663 | m_control.m_sceneOrbit, vec3(0, 1, 0)); 664 | 665 | m_projection.nearplane = m_control.m_sceneDimension * 0.01f; 666 | m_projection.farplane = m_control.m_sceneDimension * 10.0f; 667 | 668 | 669 | return validated; 670 | } 671 | 672 | void Sample::processUI(double time) 673 | { 674 | int width = m_windowState.m_winSize[0]; 675 | int height = m_windowState.m_winSize[1]; 676 | 677 | // Update imgui configuration 678 | auto& imgui_io = ImGui::GetIO(); 679 | imgui_io.DeltaTime = static_cast(time - m_uiTime); 680 | imgui_io.DisplaySize = ImVec2(width, height); 681 | 682 | m_uiTime = time; 683 | 684 | ImGui::NewFrame(); 685 | ImGui::SetNextWindowSize(ImGuiH::dpiScaled(320, 0), ImGuiCond_FirstUseEver); 686 | if(ImGui::Begin("NVIDIA " PROJECT_NAME, nullptr)) 687 | { 688 | m_ui.enumCombobox(GUI_MSAA, "msaa", &m_tweak.samples); 689 | m_ui.enumCombobox(GUI_ALGORITHM, "ssao algorithm", &m_tweak.algorithm); 690 | 691 | ImGui::Checkbox("orthographic", &m_tweak.ortho); 692 | ImGui::SliderFloat("radius", &m_tweak.radius, 0.0f, 4.0f); 693 | ImGui::SliderFloat("intensity", &m_tweak.intensity, 0.0f, 4.0f); 694 | ImGui::SliderFloat("bias", &m_tweak.bias, 0.0f, 0.9999f); 695 | ImGui::Checkbox("blur active", &m_tweak.blur); 696 | ImGui::SliderFloat("blur sharpness", &m_tweak.blurSharpness, 0.0f, 128.0f); 697 | } 698 | ImGui::End(); 699 | } 700 | 701 | void Sample::prepareHbaoData(const Projection& projection, int width, int height) 702 | { 703 | // projection 704 | const float* P = glm::value_ptr(projection.matrix); 705 | 706 | float projInfoPerspective[] = { 707 | 2.0f / (P[4 * 0 + 0]), // (x) * (R - L)/N 708 | 2.0f / (P[4 * 1 + 1]), // (y) * (T - B)/N 709 | -(1.0f - P[4 * 2 + 0]) / P[4 * 0 + 0], // L/N 710 | -(1.0f + P[4 * 2 + 1]) / P[4 * 1 + 1], // B/N 711 | }; 712 | 713 | float projInfoOrtho[] = { 714 | 2.0f / (P[4 * 0 + 0]), // ((x) * R - L) 715 | 2.0f / (P[4 * 1 + 1]), // ((y) * T - B) 716 | -(1.0f + P[4 * 3 + 0]) / P[4 * 0 + 0], // L 717 | -(1.0f - P[4 * 3 + 1]) / P[4 * 1 + 1], // B 718 | }; 719 | 720 | int useOrtho = projection.ortho ? 1 : 0; 721 | m_hbaoUbo.projOrtho = useOrtho; 722 | m_hbaoUbo.projInfo = useOrtho ? glm::make_vec4(projInfoOrtho) : glm::make_vec4(projInfoPerspective); 723 | 724 | float projScale; 725 | if(useOrtho) 726 | { 727 | projScale = float(height) / (projInfoOrtho[1]); 728 | } 729 | else 730 | { 731 | projScale = float(height) / (tanf(projection.fov * 0.5f) * 2.0f); 732 | } 733 | 734 | // radius 735 | float meters2viewspace = 1.0f; 736 | float R = m_tweak.radius * meters2viewspace; 737 | m_hbaoUbo.R2 = R * R; 738 | m_hbaoUbo.NegInvR2 = -1.0f / m_hbaoUbo.R2; 739 | m_hbaoUbo.RadiusToScreen = R * 0.5f * projScale; 740 | 741 | // ao 742 | m_hbaoUbo.PowExponent = std::max(m_tweak.intensity, 0.0f); 743 | m_hbaoUbo.NDotVBias = std::min(std::max(0.0f, m_tweak.bias), 1.0f); 744 | m_hbaoUbo.AOMultiplier = 1.0f / (1.0f - m_hbaoUbo.NDotVBias); 745 | 746 | // resolution 747 | int quarterWidth = ((width + 3) / 4); 748 | int quarterHeight = ((height + 3) / 4); 749 | 750 | m_hbaoUbo.InvQuarterResolution = vec2(1.0f / float(quarterWidth), 1.0f / float(quarterHeight)); 751 | m_hbaoUbo.InvFullResolution = vec2(1.0f / float(width), 1.0f / float(height)); 752 | 753 | #if USE_AO_LAYERED_SINGLEPASS 754 | for(int i = 0; i < HBAO_RANDOM_ELEMENTS; i++) 755 | { 756 | m_hbaoUbo.float2Offsets[i] = vec4(float(i % 4) + 0.5f, float(i / 4) + 0.5f, 0.0f, 0.0f); 757 | m_hbaoUbo.jitters[i] = m_hbaoRandom[i]; 758 | } 759 | #endif 760 | } 761 | 762 | void Sample::drawLinearDepth(const Projection& projection, int width, int height, int sampleIdx) 763 | { 764 | NV_PROFILE_GL_SECTION("linearize"); 765 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.depthlinear); 766 | 767 | if(m_tweak.samples > 1) 768 | { 769 | glUseProgram(m_progManager.get(programs.depth_linearize_msaa)); 770 | glUniform4f(0, projection.nearplane * projection.farplane, projection.nearplane - projection.farplane, 771 | projection.farplane, projection.ortho ? 0.0f : 1.0f); 772 | glUniform1i(1, sampleIdx); 773 | 774 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D_MULTISAMPLE, textures.scene_depthstencil); 775 | glDrawArrays(GL_TRIANGLES, 0, 3); 776 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D_MULTISAMPLE, 0); 777 | } 778 | else 779 | { 780 | glUseProgram(m_progManager.get(programs.depth_linearize)); 781 | glUniform4f(0, projection.nearplane * projection.farplane, projection.nearplane - projection.farplane, 782 | projection.farplane, projection.ortho ? 0.0f : 1.0f); 783 | 784 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.scene_depthstencil); 785 | glDrawArrays(GL_TRIANGLES, 0, 3); 786 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, 0); 787 | } 788 | } 789 | 790 | void Sample::drawHbaoBlur(const Projection& projection, int width, int height, int sampleIdx) 791 | { 792 | NV_PROFILE_GL_SECTION("ssaoblur"); 793 | 794 | float meters2viewspace = 1.0f; 795 | 796 | glUseProgram(m_progManager.get(USE_AO_SPECIALBLUR ? programs.hbao_blur : programs.bilateralblur)); 797 | nvgl::bindMultiTexture(GL_TEXTURE1, GL_TEXTURE_2D, textures.scene_depthlinear); 798 | 799 | glUniform1f(0, m_tweak.blurSharpness / meters2viewspace); 800 | 801 | glDrawBuffer(GL_COLOR_ATTACHMENT1); 802 | 803 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.hbao_result); 804 | glUniform2f(1, 1.0f / float(width), 0); 805 | glDrawArrays(GL_TRIANGLES, 0, 3); 806 | 807 | // final output to main fbo 808 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene); 809 | glDisable(GL_DEPTH_TEST); 810 | glEnable(GL_BLEND); 811 | glBlendFunc(GL_ZERO, GL_SRC_COLOR); 812 | if(m_tweak.samples > 1) 813 | { 814 | glEnable(GL_SAMPLE_MASK); 815 | glSampleMaski(0, 1 << sampleIdx); 816 | } 817 | 818 | #if USE_AO_SPECIALBLUR 819 | glUseProgram(m_progManager.get(programs.hbao_blur2)); 820 | glUniform1f(0, m_tweak.blurSharpness / meters2viewspace); 821 | #endif 822 | 823 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.hbao_blur); 824 | glUniform2f(1, 0, 1.0f / float(height)); 825 | glDrawArrays(GL_TRIANGLES, 0, 3); 826 | } 827 | 828 | 829 | void Sample::drawHbaoClassic(const Projection& projection, int width, int height, int sampleIdx) 830 | { 831 | prepareHbaoData(projection, width, height); 832 | 833 | drawLinearDepth(projection, width, height, sampleIdx); 834 | 835 | { 836 | NV_PROFILE_GL_SECTION("ssaocalc"); 837 | 838 | if(m_tweak.blur) 839 | { 840 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao_calc); 841 | glDrawBuffer(GL_COLOR_ATTACHMENT0); 842 | } 843 | else 844 | { 845 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene); 846 | glDisable(GL_DEPTH_TEST); 847 | glEnable(GL_BLEND); 848 | glBlendFunc(GL_ZERO, GL_SRC_COLOR); 849 | if(m_tweak.samples > 1) 850 | { 851 | glEnable(GL_SAMPLE_MASK); 852 | glSampleMaski(0, 1 << sampleIdx); 853 | } 854 | } 855 | 856 | glUseProgram(m_progManager.get(USE_AO_SPECIALBLUR && m_tweak.blur ? programs.hbao_calc_blur : programs.hbao_calc)); 857 | 858 | glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffers.hbao_ubo); 859 | glNamedBufferSubData(buffers.hbao_ubo, 0, sizeof(HBAOData), &m_hbaoUbo); 860 | 861 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.scene_depthlinear); 862 | nvgl::bindMultiTexture(GL_TEXTURE1, GL_TEXTURE_2D, textures.hbao_randomview[sampleIdx]); 863 | glDrawArrays(GL_TRIANGLES, 0, 3); 864 | } 865 | 866 | if(m_tweak.blur) 867 | { 868 | drawHbaoBlur(projection, width, height, sampleIdx); 869 | } 870 | 871 | glEnable(GL_DEPTH_TEST); 872 | glDisable(GL_BLEND); 873 | glDisable(GL_SAMPLE_MASK); 874 | glSampleMaski(0, ~0); 875 | 876 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, 0); 877 | nvgl::bindMultiTexture(GL_TEXTURE1, GL_TEXTURE_2D, 0); 878 | 879 | glUseProgram(0); 880 | } 881 | 882 | void Sample::drawHbaoCacheAware(const Projection& projection, int width, int height, int sampleIdx) 883 | { 884 | int quarterWidth = ((width + 3) / 4); 885 | int quarterHeight = ((height + 3) / 4); 886 | 887 | prepareHbaoData(projection, width, height); 888 | 889 | drawLinearDepth(projection, width, height, sampleIdx); 890 | 891 | { 892 | NV_PROFILE_GL_SECTION("viewnormal"); 893 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.viewnormal); 894 | 895 | glUseProgram(m_progManager.get(programs.viewnormal)); 896 | 897 | glUniform4fv(0, 1, glm::value_ptr(m_hbaoUbo.projInfo)); 898 | glUniform1i(1, m_hbaoUbo.projOrtho); 899 | glUniform2fv(2, 1, glm::value_ptr(m_hbaoUbo.InvFullResolution)); 900 | 901 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.scene_depthlinear); 902 | glDrawArrays(GL_TRIANGLES, 0, 3); 903 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, 0); 904 | } 905 | 906 | { 907 | NV_PROFILE_GL_SECTION("deinterleave"); 908 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao2_deinterleave); 909 | glViewport(0, 0, quarterWidth, quarterHeight); 910 | 911 | glUseProgram(m_progManager.get(programs.hbao2_deinterleave)); 912 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.scene_depthlinear); 913 | 914 | for(int i = 0; i < HBAO_RANDOM_ELEMENTS; i += NUM_MRT) 915 | { 916 | glUniform4f(0, float(i % 4) + 0.5f, float(i / 4) + 0.5f, m_hbaoUbo.InvFullResolution.x, 917 | m_hbaoUbo.InvFullResolution.y); 918 | 919 | for(int layer = 0; layer < NUM_MRT; layer++) 920 | { 921 | glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + layer, textures.hbao2_depthview[i + layer], 0); 922 | } 923 | glDrawArrays(GL_TRIANGLES, 0, 3); 924 | } 925 | } 926 | 927 | { 928 | NV_PROFILE_GL_SECTION("ssaocalc"); 929 | 930 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao2_calc); 931 | glViewport(0, 0, quarterWidth, quarterHeight); 932 | 933 | glUseProgram(m_progManager.get(USE_AO_SPECIALBLUR && m_tweak.blur ? programs.hbao2_calc_blur : programs.hbao2_calc)); 934 | nvgl::bindMultiTexture(GL_TEXTURE1, GL_TEXTURE_2D, textures.scene_viewnormal); 935 | 936 | glBindBufferBase(GL_UNIFORM_BUFFER, 0, buffers.hbao_ubo); 937 | glNamedBufferSubData(buffers.hbao_ubo, 0, sizeof(HBAOData), &m_hbaoUbo); 938 | 939 | #if USE_AO_LAYERED_SINGLEPASS 940 | // instead of drawing to each layer individually 941 | // we draw all layers at once, and use image writes to update the array texture 942 | // this buys additional performance :) 943 | 944 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, textures.hbao2_deptharray); 945 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_IMAGE 946 | glBindImageTexture(0, textures.hbao2_resultarray, 0, GL_TRUE, 0, GL_WRITE_ONLY, USE_AO_SPECIALBLUR ? GL_RG16F : GL_R8); 947 | #endif 948 | glDrawArrays(GL_TRIANGLES, 0, 3 * HBAO_RANDOM_ELEMENTS); 949 | #if USE_AO_LAYERED_SINGLEPASS == AO_LAYERED_IMAGE 950 | glMemoryBarrier(GL_TEXTURE_FETCH_BARRIER_BIT | GL_SHADER_IMAGE_ACCESS_BARRIER_BIT); 951 | #endif 952 | #else 953 | for(int i = 0; i < HBAO_RANDOM_ELEMENTS; i++) 954 | { 955 | glUniform2f(0, float(i % 4) + 0.5f, float(i / 4) + 0.5f); 956 | glUniform4fv(1, 1, m_hbaoRandom[i].get_value()); 957 | 958 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, textures.hbao2_depthview[i]); 959 | glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, textures.hbao2_resultarray, 0, i); 960 | 961 | glDrawArrays(GL_TRIANGLES, 0, 3); 962 | } 963 | #endif 964 | } 965 | 966 | { 967 | NV_PROFILE_GL_SECTION("reinterleave"); 968 | 969 | if(m_tweak.blur) 970 | { 971 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.hbao_calc); 972 | glDrawBuffer(GL_COLOR_ATTACHMENT0); 973 | } 974 | else 975 | { 976 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene); 977 | glDisable(GL_DEPTH_TEST); 978 | glEnable(GL_BLEND); 979 | glBlendFunc(GL_ZERO, GL_SRC_COLOR); 980 | if(m_tweak.samples > 1) 981 | { 982 | glEnable(GL_SAMPLE_MASK); 983 | glSampleMaski(0, 1 << sampleIdx); 984 | } 985 | } 986 | glViewport(0, 0, width, height); 987 | 988 | glUseProgram(m_progManager.get(USE_AO_SPECIALBLUR && m_tweak.blur ? programs.hbao2_reinterleave_blur : programs.hbao2_reinterleave)); 989 | 990 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, textures.hbao2_resultarray); 991 | glDrawArrays(GL_TRIANGLES, 0, 3); 992 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D_ARRAY, 0); 993 | } 994 | 995 | if(m_tweak.blur) 996 | { 997 | drawHbaoBlur(projection, width, height, sampleIdx); 998 | } 999 | 1000 | glDisable(GL_BLEND); 1001 | glEnable(GL_DEPTH_TEST); 1002 | glDisable(GL_SAMPLE_MASK); 1003 | glSampleMaski(0, ~0); 1004 | 1005 | nvgl::bindMultiTexture(GL_TEXTURE0, GL_TEXTURE_2D, 0); 1006 | nvgl::bindMultiTexture(GL_TEXTURE1, GL_TEXTURE_2D, 0); 1007 | 1008 | glUseProgram(0); 1009 | } 1010 | 1011 | 1012 | void Sample::think(double time) 1013 | { 1014 | NV_PROFILE_GL_SECTION("Frame"); 1015 | 1016 | m_control.m_sceneOrtho = m_tweak.ortho; 1017 | m_control.processActions({m_windowState.m_winSize[0], m_windowState.m_winSize[1]}, 1018 | glm::vec2(m_windowState.m_mouseCurrent[0], m_windowState.m_mouseCurrent[1]), 1019 | m_windowState.m_mouseButtonFlags, m_windowState.m_mouseWheel); 1020 | 1021 | if(m_windowState.onPress(KEY_R)) 1022 | { 1023 | m_progManager.reloadPrograms(); 1024 | } 1025 | if(!m_progManager.areProgramsValid()) 1026 | { 1027 | waitEvents(); 1028 | return; 1029 | } 1030 | 1031 | processUI(time); 1032 | 1033 | int width = m_windowState.m_winSize[0]; 1034 | int height = m_windowState.m_winSize[1]; 1035 | 1036 | m_projection.ortho = m_control.m_sceneOrtho; 1037 | m_projection.orthoheight = m_control.m_sceneOrthoZoom; 1038 | m_projection.update(width, height); 1039 | 1040 | if(m_tweakLast.samples != m_tweak.samples) 1041 | { 1042 | initFramebuffers(width, height, m_tweak.samples); 1043 | } 1044 | m_tweakLast = m_tweak; 1045 | 1046 | { 1047 | NV_PROFILE_GL_SECTION("Scene"); 1048 | glViewport(0, 0, width, height); 1049 | 1050 | glBindFramebuffer(GL_FRAMEBUFFER, fbos.scene); 1051 | 1052 | glm::vec4 bgColor(0.2, 0.2, 0.2, 0.0); 1053 | glClearBufferfv(GL_COLOR, 0, &bgColor.x); 1054 | 1055 | glClearDepth(1.0); 1056 | glClear(GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); 1057 | glEnable(GL_DEPTH_TEST); 1058 | 1059 | m_sceneUbo.viewport = uvec2(width, height); 1060 | 1061 | glm::mat4 view = m_control.m_viewMatrix; 1062 | 1063 | m_sceneUbo.viewProjMatrix = m_projection.matrix * view; 1064 | m_sceneUbo.viewMatrix = view; 1065 | m_sceneUbo.viewMatrixIT = glm::transpose(glm::inverse(view)); 1066 | 1067 | glUseProgram(m_progManager.get(programs.draw_scene)); 1068 | glBindBufferBase(GL_UNIFORM_BUFFER, UBO_SCENE, buffers.scene_ubo); 1069 | glBufferSubData(GL_UNIFORM_BUFFER, 0, sizeof(SceneData), &m_sceneUbo); 1070 | 1071 | glBindVertexBuffer(0, buffers.scene_vbo, 0, sizeof(Vertex)); 1072 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers.scene_ibo); 1073 | 1074 | glEnableVertexAttribArray(VERTEX_POS); 1075 | glEnableVertexAttribArray(VERTEX_NORMAL); 1076 | glEnableVertexAttribArray(VERTEX_COLOR); 1077 | 1078 | glDrawElements(GL_TRIANGLES, m_sceneTriangleIndices, GL_UNSIGNED_INT, NV_BUFFER_OFFSET(0)); 1079 | 1080 | glDisableVertexAttribArray(VERTEX_POS); 1081 | glDisableVertexAttribArray(VERTEX_NORMAL); 1082 | glDisableVertexAttribArray(VERTEX_COLOR); 1083 | 1084 | glBindBufferBase(GL_UNIFORM_BUFFER, UBO_SCENE, 0); 1085 | glBindVertexBuffer(0, 0, 0, 0); 1086 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); 1087 | } 1088 | 1089 | { 1090 | NV_PROFILE_GL_SECTION("ssao"); 1091 | 1092 | for(int sample = 0; sample < std::max(1, m_tweak.samples); sample++) 1093 | { 1094 | switch(m_tweak.algorithm) 1095 | { 1096 | case ALGORITHM_HBAO_CLASSIC: 1097 | drawHbaoClassic(m_projection, width, height, sample); 1098 | break; 1099 | case ALGORITHM_HBAO_CACHEAWARE: 1100 | drawHbaoCacheAware(m_projection, width, height, sample); 1101 | break; 1102 | } 1103 | } 1104 | } 1105 | 1106 | { 1107 | NV_PROFILE_GL_SECTION("Blit"); 1108 | // blit to background 1109 | glBindFramebuffer(GL_READ_FRAMEBUFFER, fbos.scene); 1110 | glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0); 1111 | glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 1112 | glBindFramebuffer(GL_FRAMEBUFFER, 0); 1113 | } 1114 | 1115 | { 1116 | NV_PROFILE_GL_SECTION("GUI"); 1117 | ImGui::Render(); 1118 | ImGui::RenderDrawDataGL(ImGui::GetDrawData()); 1119 | } 1120 | 1121 | ImGui::EndFrame(); 1122 | } 1123 | 1124 | void Sample::resize(int width, int height) 1125 | { 1126 | initFramebuffers(width, height, m_tweak.samples); 1127 | } 1128 | } // namespace ssao 1129 | 1130 | using namespace ssao; 1131 | 1132 | int main(int argc, const char** argv) 1133 | { 1134 | NVPSystem system(PROJECT_NAME); 1135 | Sample sample; 1136 | return sample.run(PROJECT_NAME, argc, argv, SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT); 1137 | } 1138 | -------------------------------------------------------------------------------- /viewnormal.frag.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014-2021, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | * 16 | * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #version 430 22 | 23 | in vec2 texCoord; 24 | 25 | layout(location=0) uniform vec4 projInfo; 26 | layout(location=1) uniform int projOrtho; 27 | layout(location=2) uniform vec2 InvFullResolution; 28 | 29 | layout(binding=0) uniform sampler2D texLinearDepth; 30 | 31 | layout(location=0,index=0) out vec4 out_Color; 32 | 33 | //---------------------------------------------------------------------------------- 34 | 35 | vec3 UVToView(vec2 uv, float eye_z) 36 | { 37 | return vec3((uv * projInfo.xy + projInfo.zw) * (projOrtho != 0 ? 1. : eye_z), eye_z); 38 | } 39 | 40 | vec3 FetchViewPos(vec2 UV) 41 | { 42 | float ViewDepth = textureLod(texLinearDepth,UV,0).x; 43 | return UVToView(UV, ViewDepth); 44 | } 45 | 46 | vec3 MinDiff(vec3 P, vec3 Pr, vec3 Pl) 47 | { 48 | vec3 V1 = Pr - P; 49 | vec3 V2 = P - Pl; 50 | return (dot(V1,V1) < dot(V2,V2)) ? V1 : V2; 51 | } 52 | 53 | vec3 ReconstructNormal(vec2 UV, vec3 P) 54 | { 55 | vec3 Pr = FetchViewPos(UV + vec2(InvFullResolution.x, 0)); 56 | vec3 Pl = FetchViewPos(UV + vec2(-InvFullResolution.x, 0)); 57 | vec3 Pt = FetchViewPos(UV + vec2(0, InvFullResolution.y)); 58 | vec3 Pb = FetchViewPos(UV + vec2(0, -InvFullResolution.y)); 59 | return normalize(cross(MinDiff(P, Pr, Pl), MinDiff(P, Pt, Pb))); 60 | } 61 | 62 | //---------------------------------------------------------------------------------- 63 | 64 | void main() { 65 | vec3 P = FetchViewPos(texCoord); 66 | vec3 N = ReconstructNormal(texCoord, P); 67 | 68 | out_Color = vec4(N*0.5 + 0.5,0); 69 | } 70 | --------------------------------------------------------------------------------