├── .gitattributes ├── .gitignore ├── CMakeLists.txt ├── CONTRIBUTING ├── LICENSE ├── README.md ├── comp_depth_minmax.hpp ├── doc ├── depth.png ├── normal.png ├── nrm_depth.png ├── nrm_depth_without.png ├── obj_contour.png ├── obj_contour_fxaa.png ├── object_id.png └── vk_toon_shader.png ├── example.cpp ├── example.hpp ├── main.cpp ├── post_compositing.hpp ├── post_effect.hpp ├── post_kuwahara.hpp ├── post_kuwahara_aniso.hpp ├── post_nrmdepth.hpp ├── post_objcontour.hpp ├── post_tonemapper.hpp ├── rasterizer.cpp ├── rasterizer.hpp ├── raypick.hpp ├── raytracer.cpp ├── raytracer.hpp ├── shaders ├── binding.glsl ├── compositing.frag ├── contour_normaldepth.frag ├── contour_objects.frag ├── depthminmax.comp ├── final.frag ├── fxaa.frag ├── gltf.glsl ├── kuwa_aniso.frag ├── kuwa_gauss.frag ├── kuwa_sst.frag ├── kuwa_tfm.frag ├── kuwahara.frag ├── passthrough.vert ├── pick.rchit ├── pick.rgen ├── pick.rmiss ├── rasterizer.frag ├── rasterizer.vert ├── raytrace.rchit ├── raytrace.rgen ├── raytrace.rmiss ├── raytraceShadow.rmiss ├── share.glsl ├── tonemap.frag └── tonemapping.glsl ├── tiny_gltf.cpp └── vk_util.hpp /.gitattributes: -------------------------------------------------------------------------------- 1 | 2 | # 3 | # Git LFS (see https://git-lfs.github.com/) 4 | *.bin filter=lfs diff=lfs merge=lfs -text 5 | *.csf filter=lfs diff=lfs merge=lfs -text 6 | *.dds filter=lfs diff=lfs merge=lfs -text 7 | *.dll filter=lfs diff=lfs merge=lfs -text 8 | *.exe filter=lfs diff=lfs merge=lfs -text 9 | *.gz filter=lfs diff=lfs merge=lfs -text 10 | *.hdr filter=lfs diff=lfs merge=lfs -text 11 | *.jpg filter=lfs diff=lfs merge=lfs -text 12 | *.lib filter=lfs diff=lfs merge=lfs -text 13 | *.obj filter=lfs diff=lfs merge=lfs -text 14 | *.pdb filter=lfs diff=lfs merge=lfs -text 15 | *.png filter=lfs diff=lfs merge=lfs -text 16 | *.so filter=lfs diff=lfs merge=lfs -text 17 | *.vks filter=lfs diff=lfs merge=lfs -text 18 | *.zip filter=lfs diff=lfs merge=lfs -text 19 | * !text !filter !merge !diff 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/spv/* 2 | build 3 | cmake_build -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.5) 2 | 3 | get_filename_component(PROJNAME ${CMAKE_CURRENT_SOURCE_DIR} NAME) 4 | Project(${PROJNAME}) 5 | Message(STATUS "-------------------------------") 6 | Message(STATUS "Processing Project ${PROJNAME}:") 7 | 8 | ##################################################################################### 9 | # look for nvpro_core 1) as a sub-folder 2) at some other locations 10 | # this cannot be put anywhere else since we still didn't find setup.cmake yet 11 | # 12 | if(NOT BASE_DIRECTORY) 13 | 14 | find_path(BASE_DIRECTORY 15 | NAMES nvpro_core/cmake/setup.cmake 16 | PATHS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../.. 17 | REQUIRED 18 | DOC "Directory containing nvpro_core" 19 | ) 20 | endif() 21 | if(EXISTS ${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake) 22 | include(${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake) 23 | include(${BASE_DIRECTORY}/nvpro_core/cmake/utilities.cmake) 24 | else() 25 | message(FATAL_ERROR "could not find base directory, please set BASE_DIRECTORY to folder containing nvpro_core") 26 | endif() 27 | 28 | _add_project_definitions(${PROJNAME}) 29 | 30 | set(CMAKE_CXX_STANDARD 20) 31 | 32 | ##################################################################################### 33 | # additions from packages needed for this sample 34 | # add refs in LIBRARIES_OPTIMIZED 35 | # add refs in LIBRARIES_DEBUG 36 | # add files in PACKAGE_SOURCE_FILES 37 | 38 | set( EXENAME ${PROJNAME} ) 39 | _add_package_VulkanSDK() 40 | _add_package_IMGUI() 41 | 42 | ##################################################################################### 43 | # process the rest of some cmake code that needs to be done *after* the packages add 44 | _add_nvpro_core_lib() 45 | 46 | ##################################################################################### 47 | # Source files for this project 48 | # 49 | file(GLOB SOURCE_FILES *.cpp *.hpp *.inl *.h *.c) 50 | file(GLOB GLSL_FILES *.glsl) 51 | 52 | #-------------------------------------------------------------------------------------------------- 53 | # GLSL to SPIR-V custom build 54 | compile_glsl_directory( 55 | SRC "${CMAKE_CURRENT_SOURCE_DIR}/shaders" 56 | DST "${CMAKE_CURRENT_SOURCE_DIR}/spv" 57 | VULKAN_TARGET "vulkan1.2" 58 | ) 59 | 60 | 61 | 62 | #-------------------------------------------------------------------------------------------------- 63 | # Resources 64 | # 65 | download_files(FILENAMES robot.zip EXTRACT) 66 | 67 | 68 | source_group(Shader_Files FILES ${GLSL_SOURCES}) 69 | 70 | ##################################################################################### 71 | # Executable 72 | # 73 | if(WIN32 AND NOT GLUT_FOUND) 74 | add_definitions(/wd4996) #remove printf warning 75 | add_definitions(/wd4244) #remove double to float conversion warning 76 | add_definitions(/wd4305) #remove double to float truncation warning 77 | else() 78 | add_definitions(-fpermissive) 79 | endif() 80 | add_executable(${EXENAME} ${SOURCE_FILES} ${COMMON_SOURCE_FILES} ${PACKAGE_SOURCE_FILES} ${GLSL_SOURCES}) 81 | 82 | #_set_subsystem_console(${EXENAME}) 83 | 84 | ##################################################################################### 85 | # common source code needed for this sample 86 | # 87 | source_group(common FILES 88 | ${COMMON_SOURCE_FILES} 89 | ${PACKAGE_SOURCE_FILES} 90 | ) 91 | source_group("Source Files" FILES ${SOURCE_FILES}) 92 | 93 | if(UNIX) 94 | set(UNIXLINKLIBS dl pthread) 95 | else() 96 | set(UNIXLINKLIBS) 97 | endif() 98 | 99 | ##################################################################################### 100 | # Linkage 101 | # 102 | target_link_libraries(${EXENAME} ${PLATFORM_LIBRARIES} nvpro_core) 103 | 104 | foreach(DEBUGLIB ${LIBRARIES_DEBUG}) 105 | target_link_libraries(${EXENAME} debug ${DEBUGLIB}) 106 | endforeach(DEBUGLIB) 107 | 108 | foreach(RELEASELIB ${LIBRARIES_OPTIMIZED}) 109 | target_link_libraries(${EXENAME} optimized ${RELEASELIB}) 110 | endforeach(RELEASELIB) 111 | 112 | ##################################################################################### 113 | # copies binaries that need to be put next to the exe files (ZLib, etc.) 114 | # 115 | _finalize_target( ${EXENAME} ) 116 | 117 | 118 | ##################################################################################### 119 | # Extra elements for INSTALL 120 | # 121 | install(FILES ${SPV_OUTPUT} CONFIGURATIONS Release DESTINATION "bin_${ARCH}/${PROJNAME}/spv") 122 | install(FILES ${SPV_OUTPUT} CONFIGURATIONS Debug DESTINATION "bin_${ARCH}_debug/${PROJNAME}/spv") 123 | -------------------------------------------------------------------------------- /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 | # Silhouette and toon shading 2 | 3 | This sample is loading a glTF scene and extracting lines for object contours and details, like when there is a sharp curvature on the object. 4 | 5 | This is only post-processing, no line/edge drawing. 6 | 7 | ![](doc/vk_toon_shader.png) 8 | 9 | 10 | ## Line extractions 11 | 12 | Either we are using the raster or the ray tracer, the renderer must provide 3 data information: normal, depth, object id. 13 | 14 | ![normal](doc/normal.png) 15 | ![depth](doc/depth.png) 16 | ![objectID](doc/object_id.png) 17 | 18 | In the fragment shader, or in the closest hit shader, these data will be stored in a single RGBA32F buffer, where the first XY will have the normal data encoded, Z, the depth and W the object ID, an integer we cast to float. 19 | 20 | ## Contour Extraction 21 | 22 | To extract the contour, we are using a post-processing pipeline, mainly using a fragment shader. There is a case where we are using a compute shader to find the min and max value of the depth buffer. 23 | 24 | The extraction of the object contour is done by comparing the actual pixel with the neighbors. We compare each immediate neighbor if the value is greather, smaller or different from the current one. This has for effect to create a line on the current object, outside of it or both, which makes a thicker line. 25 | 26 | ~~~~ 27 | +---+---+---+ 28 | | A | B | C | 29 | +---+---+---+ 30 | | D | X | E | 31 | +---+---+---+ 32 | | F | G | H | 33 | +---+---+---+ 34 | ~~~~ 35 | 36 | Comparing X against any A-H, if one value is (< , >, !=) then we set 1 to the result. 37 | 38 | The GLSL shader is a fragment shader, but to use the `unsigned int` from Iray, we cast the image pixels data to a single `GL_R32F` and in the fragment shader, we reinteprete the 39 | value as an integer using `floatBitsToInt`. 40 | 41 | This will result to something like this 42 | 43 | ![obj](doc/obj_contour.png) 44 | 45 | ### FXAA 46 | 47 | The result is jaggy and one method to remove this, is to apply a FXAA pass on the image, which is resulting to smoother lines. 48 | 49 | ![obj](doc/obj_contour_fxaa.png) 50 | 51 | 52 | ## Normal and depth 53 | 54 | In some cases, the contour of individual object is not enough as we might want to see details that are part of the same object, like crease and valleys. 55 | 56 | This is without 57 | 58 | ![Without](doc/nrm_depth_without.png) 59 | 60 | 61 | With depth and normal extraction 62 | 63 | ![With](doc/nrm_depth.png) 64 | 65 | ### Normal extraction 66 | 67 | To extract the normal gradient to create and create additional contour, we are using the 68 | following function. See above for the position of the surrounding pixels. 69 | 70 | `N = (|X-B|∙|X-B|) + (|X-G|∙|X-G|) + (|X-E|∙|X-E|) + (|X-D|∙|X-D|)` 71 | 72 | ### Depth extraction 73 | 74 | For depth extraction, we are normalizing the depth value by finding the nearest depth 75 | position (d1) and the farthest value (d2). 76 | 77 | Since we want to clearly emphasis the elements closer to the camera to avoid too many 78 | contour created in the background, we will give more priority to the closest elements as you can see with the blue line. 79 | 80 | 81 | 82 | 83 | The extraction of the depth contour is done using Sobel eqn. [linear1] for the first order of differential and eqn. [linear2] 84 | See: [Comprehensible Rendering of 3-D Shapes](https://www.cs.princeton.edu/courses/archive/fall00/cs597b/papers/saito90.pdf) 85 | 86 | linear1: `g = (|A +2B +C -F-2G-H| + C|C+2E+H-A-2D-F|)/8` 87 | 88 | linear2: `l = (8X -A-B-C-D-E-F-G-H)/3` 89 | 90 | ## References 91 | 92 | * https://graphics.pixar.com/library/ToonRendering/paper.pdf 93 | * https://en.wikipedia.org/wiki/Cel_shading 94 | * https://gfx.cs.princeton.edu/gfx/pubs/DeCarlo_2003_SCF/DeCarlo2003.pdf 95 | * http://www.markmark.net/npar/npar2000_lake_et_al.pdf 96 | * https://hpi.de/fileadmin/user_upload/hpi/navigation/10_forschung/30_publikationen/15_dissertationen/Diss_Nienhaus.pdf 97 | * https://developer.nvidia.com/gpugems/GPUGems2/gpugems2_chapter15.html 98 | * https://www.ijstr.org/final-print/apr2018/Edge-Detection-In-Images-Using-Haar-Wavelets-Sobel-Gabor-And-Laplacian-Filters.pdf 99 | * https://en.wikipedia.org/wiki/Canny_edge_detector 100 | * https://www.cs.princeton.edu/courses/archive/fall00/cs597b/papers/saito90.pdf 101 | * https://adempsey.github.io/edgey/ 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /comp_depth_minmax.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 | #pragma once 21 | 22 | #include 23 | 24 | #include "vk_util.hpp" 25 | 26 | #include "nvh/fileoperations.hpp" 27 | #include "nvvk/debug_util_vk.hpp" 28 | #include "nvvk/descriptorsets_vk.hpp" 29 | #include "nvvk/shaders_vk.hpp" 30 | 31 | 32 | extern std::vector defaultSearchPaths; 33 | 34 | 35 | // Find the min/max value of the depth value stored in the output data buffer 36 | class CompDepthMinMax 37 | { 38 | public: 39 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) 40 | { 41 | m_device = device; 42 | m_queueIndex = queueIndex; 43 | m_alloc = allocator; 44 | m_debug.setup(device); 45 | 46 | // Create the buffer, no value in 47 | m_values = m_alloc->createBuffer(2 * sizeof(uint32_t), vkBU::eTransferSrc | vkBU::eTransferDst | vkBU::eStorageBuffer, 48 | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 49 | createCompDescriptors(); 50 | createCompPipelines(); 51 | } 52 | 53 | void setInput(const nvvk::Texture& dataImage) 54 | { 55 | std::vector writes; 56 | writes.emplace_back(m_descSetBind.nvvk::DescriptorSetBindings::makeWrite(m_descSet, 0, &dataImage.descriptor)); // ray tracing 57 | vk::DescriptorBufferInfo bi{m_values.buffer, 0, VK_WHOLE_SIZE}; 58 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 1, &bi)); // ray tracing 59 | m_device.updateDescriptorSets(writes, nullptr); 60 | } 61 | 62 | void execute(const vk::CommandBuffer& cmdBuf, const vk::Extent2D& size) 63 | { 64 | float big{10000.f}; 65 | m_minmax = {*reinterpret_cast(&big), 0}; // Resetting zNear and zFar values (floatBitsToInt) 66 | cmdBuf.updateBuffer(m_values.buffer, 0, m_minmax); 67 | cmdBuf.bindPipeline(vk::PipelineBindPoint::eCompute, m_pipeline); 68 | cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eCompute, m_pipelineLayout, 0, {m_descSet}, {}); 69 | cmdBuf.dispatch(size.width / 32 + 1, size.height / 32 + 1, 1); 70 | 71 | // Adding a barrier to make sure the compute is done before one of the fragment 72 | // shader picks up the values computed here. 73 | vk::BufferMemoryBarrier bmb; 74 | bmb.setSrcAccessMask(vk::AccessFlagBits::eShaderWrite); 75 | bmb.setDstAccessMask(vk::AccessFlagBits::eShaderRead); 76 | bmb.setOffset(0); 77 | bmb.setSize(VK_WHOLE_SIZE); 78 | bmb.setSize(VK_WHOLE_SIZE); 79 | bmb.setBuffer(m_values.buffer); 80 | cmdBuf.pipelineBarrier(vk::PipelineStageFlagBits::eComputeShader, vk::PipelineStageFlagBits::eFragmentShader, 81 | vk::DependencyFlagBits::eDeviceGroup, 0, nullptr, 1, &bmb, 0, nullptr); 82 | } 83 | 84 | void getNearFar(float& near_, float& far_) 85 | { 86 | { 87 | m_device.waitIdle(); // Must wait to be sure to get synchronization (use only for debug) 88 | void* mapped = m_alloc->map(m_values); 89 | memcpy(m_minmax.data(), mapped, 2 * sizeof(float)); 90 | m_alloc->unmap(m_values); 91 | } 92 | 93 | near_ = *reinterpret_cast(&m_minmax[0]); // intBitsToFloat 94 | far_ = *reinterpret_cast(&m_minmax[1]); 95 | } 96 | const nvvk::Buffer& getBuffer() { return m_values; } // zNear - zFar 97 | 98 | void destroy() 99 | { 100 | m_alloc->destroy(m_values); 101 | m_device.destroyDescriptorSetLayout(m_descSetLayout); 102 | m_device.destroyPipeline(m_pipeline); 103 | m_device.destroyPipelineLayout(m_pipelineLayout); 104 | m_device.destroyDescriptorPool(m_descPool); 105 | } 106 | 107 | 108 | private: 109 | void createCompDescriptors() 110 | { 111 | m_descSetBind.addBinding(vkDS(0, vkDT::eStorageImage, 1, vkSS::eCompute)); // Input - image 112 | m_descSetBind.addBinding(vkDS(1, vkDT::eStorageBuffer, 1, vkSS::eCompute)); // Output - values 113 | 114 | m_descSetLayout = m_descSetBind.createLayout(m_device); 115 | m_descPool = m_descSetBind.createPool(m_device, 1); 116 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 117 | } 118 | 119 | void createCompPipelines() 120 | { 121 | vk::PipelineLayoutCreateInfo layout_info{{}, 1, &m_descSetLayout}; 122 | m_pipelineLayout = m_device.createPipelineLayout(layout_info); 123 | m_debug.setObjectName(m_pipelineLayout, "minmax"); 124 | vk::ComputePipelineCreateInfo createInfo{{}, {}, m_pipelineLayout}; 125 | 126 | createInfo.stage = nvvk::createShaderStageInfo(m_device, nvh::loadFile("spv/depthminmax.comp.spv", true, defaultSearchPaths), 127 | VK_SHADER_STAGE_COMPUTE_BIT); 128 | m_pipeline = m_device.createComputePipeline({}, createInfo, nullptr).value; 129 | m_device.destroy(createInfo.stage.module); 130 | } 131 | 132 | vk::Device m_device; 133 | uint32_t m_queueIndex; 134 | nvvkpp::ResourceAllocator* m_alloc{nullptr}; 135 | nvvk::DebugUtil m_debug; 136 | 137 | std::array m_minmax; 138 | nvvk::Buffer m_values; // min/max 139 | 140 | nvvkpp::DescriptorSetBindings m_descSetBind; 141 | vk::DescriptorPool m_descPool; 142 | vk::DescriptorSetLayout m_descSetLayout; 143 | vk::DescriptorSet m_descSet; 144 | vk::Pipeline m_pipeline; 145 | vk::PipelineLayout m_pipelineLayout; 146 | }; 147 | -------------------------------------------------------------------------------- /doc/depth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/depth.png -------------------------------------------------------------------------------- /doc/normal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/normal.png -------------------------------------------------------------------------------- /doc/nrm_depth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/nrm_depth.png -------------------------------------------------------------------------------- /doc/nrm_depth_without.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/nrm_depth_without.png -------------------------------------------------------------------------------- /doc/obj_contour.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/obj_contour.png -------------------------------------------------------------------------------- /doc/obj_contour_fxaa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/obj_contour_fxaa.png -------------------------------------------------------------------------------- /doc/object_id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/object_id.png -------------------------------------------------------------------------------- /doc/vk_toon_shader.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nvpro-samples/vk_toon_shader/7b99bd8970d753180ece5d43a00f45fea0a64f6a/doc/vk_toon_shader.png -------------------------------------------------------------------------------- /example.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #include 22 | 23 | #include "vk_util.hpp" 24 | 25 | #include 26 | 27 | #include "comp_depth_minmax.hpp" 28 | #include "nvh/gltfscene.hpp" 29 | #include "nvvkhl/appbase_vkpp.hpp" 30 | #include "nvvk/gizmos_vk.hpp" 31 | #include "nvvk/images_vk.hpp" 32 | #include "post_compositing.hpp" 33 | #include "post_kuwahara.hpp" 34 | #include "post_kuwahara_aniso.hpp" 35 | #include "post_nrmdepth.hpp" 36 | #include "post_objcontour.hpp" 37 | #include "post_tonemapper.hpp" 38 | #include "rasterizer.hpp" 39 | #include "raypick.hpp" 40 | #include "raytracer.hpp" 41 | 42 | 43 | //-------------------------------------------------------------------------------------------------- 44 | // Loading a glTF scene, raytrace and tonemap result 45 | // 46 | class VkToonExample : public nvvkhl::AppBase 47 | { 48 | public: 49 | VkToonExample() = default; 50 | 51 | void loadScene(const std::string& filename); 52 | void createPostProcess(); 53 | void display(); 54 | 55 | void setup(const vk::Instance& instance, const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t graphicsQueueIndex) override; 56 | 57 | void destroy() override; 58 | void onResize(int w, int h) override; 59 | void createRenderPass() override; 60 | void createAxis(); 61 | void createDescriptorFinal(); 62 | void createFinalPipeline(); 63 | void onKeyboard(int key, int scancode, int action, int mods) override; 64 | void onFileDrop(const char* filename) override; 65 | 66 | private: 67 | struct SceneUBO 68 | { 69 | glm::mat4 projection; 70 | glm::mat4 model; 71 | glm::vec4 cameraPosition{0.f, 0.f, 0.f, 1.f}; 72 | }; 73 | 74 | void createDescriptorMaterial(); 75 | void createDescriptorRaytrace(); 76 | void createPipeline(); 77 | void createSceneBuffers(); 78 | void createSceneDescriptors(); 79 | void drawUI(); 80 | void importImages(tinygltf::Model& gltfModel); 81 | void prepareUniformBuffers(); 82 | void resetFrame(); 83 | void settingPostPipeline(); 84 | void updateCameraBuffer(const vk::CommandBuffer& cmdBuffer); 85 | void updateDescriptor(const vk::DescriptorImageInfo& descriptor); 86 | void updateFrame(); 87 | 88 | vk::GeometryNV primitiveToGeometry(const nvh::GltfPrimMesh& prim); 89 | 90 | 91 | vk::RenderPass m_renderPassUI; 92 | vk::PipelineLayout m_pipelineLayout; 93 | vk::Pipeline m_pipeline; 94 | 95 | // Descriptors 96 | enum Dset 97 | { 98 | eFinal, // For the tonemapper 99 | eScene, // All scene data 100 | Total 101 | }; 102 | std::vector m_descSetLayout{Dset::Total}; 103 | std::vector m_descPool{Dset::Total}; 104 | std::vector m_descSet{Dset::Total}; 105 | std::vector m_descSetLayoutBind{Dset::Total}; 106 | 107 | // GLTF scene model 108 | nvh::GltfScene m_gltfScene; // The scene 109 | nvh::GltfStats m_sceneStats; // The scene stats 110 | SceneUBO m_sceneUbo; // Camera, light and more 111 | 112 | int m_frameNumber{0}; 113 | 114 | nvvk::AxisVK m_axis; // To display the axis in the lower left corner 115 | Raytracer m_raytracer; // The raytracer 116 | RayPicker m_rayPicker; // Picking under mouse using raytracer 117 | Rasterizer m_rasterizer; // Rasterizer 118 | 119 | // Post effect 120 | Tonemapper m_tonemapper; // 121 | PostNrmDepth m_nrmDepth; 122 | PostObjContour m_objContour; 123 | PostCompositing m_compositing; 124 | PostKuwahara m_kuwahara; 125 | PostKuwaharaAniso m_kuwaharaAniso; 126 | CompDepthMinMax m_depthMinMax; 127 | 128 | // All buffers on the Device 129 | nvvk::Buffer m_sceneBuffer; 130 | nvvk::Buffer m_vertexBuffer; 131 | nvvk::Buffer m_normalBuffer; 132 | nvvk::Buffer m_uvBuffer; 133 | nvvk::Buffer m_indexBuffer; 134 | nvvk::Buffer m_materialBuffer; 135 | nvvk::Buffer m_matrixBuffer; 136 | 137 | // All textures 138 | std::vector m_textures; 139 | 140 | bool m_useRaytracer{true}; 141 | glm::vec3 m_backgroundColor{1.f}; // clear color and miss 142 | int m_toonNbStep{5}; // Toon shading steps 143 | glm::vec3 m_toonLightDir{-1, -1, -1}; // Toon light 144 | 145 | // Allocator for buffers and images 146 | Allocator m_alloc; 147 | 148 | nvvk::DebugUtil m_debug; 149 | }; 150 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | //-------------------------------------------------------------------------------------------------- 22 | // This example is creating a scene with many similar objects and a plane. There are a few materials 23 | // and a light direction. 24 | // More details in simple.cpp 25 | // 26 | 27 | #include 28 | #include 29 | #include 30 | #include 31 | VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE 32 | 33 | #define IMGUI_DEFINE_MATH_OPERATORS 34 | #include "backends/imgui_impl_glfw.h" 35 | #include "backends/imgui_impl_vulkan.h" 36 | #include "example.hpp" 37 | #include "GLFW/glfw3.h" 38 | #include "nvh/fileoperations.hpp" 39 | #include "nvh/inputparser.h" 40 | #include "nvpsystem.hpp" 41 | #include "nvvk/context_vk.hpp" 42 | #include "nvvk/extensions_vk.hpp" 43 | 44 | int const SAMPLE_SIZE_WIDTH = 1600; 45 | int const SAMPLE_SIZE_HEIGHT = 1000; 46 | 47 | // Default search path for shaders 48 | std::vector defaultSearchPaths; 49 | 50 | 51 | //-------------------------------------------------------------------------------------------------- 52 | // 53 | // 54 | int main(int argc, char** argv) 55 | { 56 | // setup some basic things for the sample, logging file for example 57 | NVPSystem system(PROJECT_NAME); 58 | 59 | // Default search path for shaders 60 | defaultSearchPaths = { 61 | NVPSystem::exePath() + PROJECT_NAME, 62 | NVPSystem::exePath() + R"(media)", 63 | NVPSystem::exePath() + PROJECT_RELDIRECTORY, 64 | NVPSystem::exePath() + PROJECT_DOWNLOAD_RELDIRECTORY, 65 | }; 66 | 67 | // Parsing the command line: mandatory '-f' for the filename of the scene 68 | InputParser parser(argc, argv); 69 | std::string filename = parser.getString("-f"); 70 | if(parser.exist("-f")) 71 | { 72 | filename = parser.getString("-f"); 73 | } 74 | else if(argc == 2 && nvh::endsWith(argv[1], ".gltf")) // Drag&Drop on .exe 75 | { 76 | filename = argv[1]; 77 | } 78 | else 79 | { 80 | filename = nvh::findFile("robot/robot.gltf", defaultSearchPaths, true); 81 | } 82 | 83 | // Setup GLFW window 84 | if(!glfwInit()) 85 | { 86 | return 1; 87 | } 88 | glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API); 89 | GLFWwindow* window = glfwCreateWindow(SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT, PROJECT_NAME, nullptr, nullptr); 90 | 91 | // Enabling the extension 92 | vk::PhysicalDeviceDescriptorIndexingFeaturesEXT feature; 93 | 94 | // Vulkan required extensions 95 | assert(glfwVulkanSupported() == 1); 96 | uint32_t count{0}; 97 | auto reqExtensions = glfwGetRequiredInstanceExtensions(&count); 98 | 99 | // Requesting Vulkan extensions and layers 100 | nvvk::ContextCreateInfo contextInfo; 101 | contextInfo.setVersion(1, 2); // Using Vulkan 1.2 102 | for(uint32_t ext_id = 0; ext_id < count; ext_id++) // Adding required extensions (surface, win32, linux, ..) 103 | contextInfo.addInstanceExtension(reqExtensions[ext_id]); 104 | contextInfo.addInstanceLayer("VK_LAYER_LUNARG_monitor", true); // FPS in titlebar 105 | contextInfo.addInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, true); // Allow debug names 106 | contextInfo.addDeviceExtension(VK_KHR_SWAPCHAIN_EXTENSION_NAME); // Enabling ability to present rendering 107 | 108 | // #VKRay: Activate the ray tracing extension 109 | contextInfo.addDeviceExtension(VK_NV_RAY_TRACING_EXTENSION_NAME, true /*optional*/); 110 | 111 | 112 | // Creating the Vulkan instance and device 113 | nvvk::Context vkctx; 114 | vkctx.initInstance(contextInfo); 115 | 116 | // Find all compatible devices 117 | auto compatibleDevices = vkctx.getCompatibleDevices(contextInfo); 118 | assert(!compatibleDevices.empty()); 119 | 120 | // Use first compatible device 121 | vkctx.initDevice(compatibleDevices[0], contextInfo); 122 | 123 | VkToonExample example; 124 | 125 | // Window need to be opened to get the surface on which to draw 126 | vk::SurfaceKHR surface = example.getVkSurface(vkctx.m_instance, window); 127 | vkctx.setGCTQueueWithPresent(surface); 128 | 129 | example.setup(vkctx.m_instance, vkctx.m_device, vkctx.m_physicalDevice, vkctx.m_queueGCT.familyIndex); 130 | LOGI("Using %s \n", example.getPhysicalDevice().getProperties().deviceName.data()); 131 | 132 | example.createSwapchain(surface, SAMPLE_SIZE_WIDTH, SAMPLE_SIZE_HEIGHT); 133 | example.createDepthBuffer(); 134 | example.createRenderPass(); 135 | example.createFrameBuffers(); 136 | //example.createTonemapper(); 137 | example.createAxis(); 138 | example.createDescriptorFinal(); 139 | example.createFinalPipeline(); // How the quad will be rendered 140 | 141 | example.loadScene(filename); // Now build the example 142 | example.createPostProcess(); // Adding all post-effects, line extractions, .. 143 | example.initGUI(0); // Using sub-pass 0 144 | 145 | 146 | // GLFW Callback 147 | example.setupGlfwCallbacks(window); 148 | ImGui_ImplGlfw_InitForVulkan(window, true); 149 | 150 | // Main loop 151 | while(!glfwWindowShouldClose(window)) 152 | { 153 | glfwPollEvents(); 154 | if(example.isMinimized()) 155 | continue; 156 | 157 | CameraManip.updateAnim(); 158 | example.display(); // infinitely drawing 159 | } 160 | 161 | 162 | glfwDestroyWindow(window); 163 | glfwTerminate(); 164 | 165 | example.destroy(); 166 | vkctx.deinit(); 167 | 168 | 169 | return 0; 170 | } 171 | -------------------------------------------------------------------------------- /post_compositing.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | #include "nvvk/resourceallocator_vk.hpp" 25 | 26 | // Merge the final frame and add the contour of the normal/depth and object 27 | class PostCompositing : public PostEffect 28 | { 29 | public: 30 | const std::string getShaderName() override { return R"(spv/compositing.frag.spv)"; } 31 | 32 | // Attaching the 3 input images 33 | void setInputs(const std::vector& inputs) override 34 | { 35 | std::vector writes; 36 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 0, &inputs[0].descriptor)); // ray tracing 37 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 1, &inputs[1].descriptor)); // normal depth 38 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 2, &inputs[2].descriptor)); // object 39 | m_device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr); 40 | } 41 | 42 | void execute(const vk::CommandBuffer& cmdBuf) override 43 | { 44 | if(!m_active) 45 | return; 46 | 47 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 48 | PostEffect::execute(cmdBuf); 49 | } 50 | 51 | bool uiSetup() override 52 | { 53 | bool changed{false}; 54 | // changed |= ImGui::Checkbox("Color Background", (bool*)&m_pushCnt.setBackground); 55 | // changed |= ImGui::ColorEdit3("Background Color", &m_pushCnt.backgroundColor.x); 56 | changed |= ImGui::ColorEdit3("Contour Color", &m_pushCnt.lineColor.x); 57 | return changed; 58 | } 59 | 60 | private: 61 | // One input image and push constant to control the effect 62 | void createDescriptorSet() override 63 | { 64 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 65 | 66 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // ray tracing 67 | m_descSetBind.addBinding(vkDS(1, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth 68 | m_descSetBind.addBinding(vkDS(2, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Object contour 69 | m_descSetLayout = m_descSetBind.createLayout(m_device); 70 | m_descPool = m_descSetBind.createPool(m_device); 71 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 72 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 73 | m_debug.setObjectName(m_pipelineLayout, "compositing"); 74 | } 75 | 76 | struct PushConstant 77 | { 78 | glm::vec3 backgroundColor{1.f}; 79 | int setBackground{0}; 80 | glm::vec3 lineColor{0.3f}; 81 | }; 82 | PushConstant m_pushCnt; 83 | }; 84 | -------------------------------------------------------------------------------- /post_effect.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "imgui.h" 24 | #include "nvh/fileoperations.hpp" 25 | #include "nvvk/commands_vk.hpp" 26 | #include "nvvk/debug_util_vk.hpp" 27 | #include "nvvk/descriptorsets_vk.hpp" 28 | #include "nvvk/images_vk.hpp" 29 | #include "nvvk/pipeline_vk.hpp" 30 | #include "nvvk/vulkanhppsupport.hpp" 31 | 32 | #include "vk_util.hpp" 33 | 34 | #include 35 | 36 | 37 | extern std::vector defaultSearchPaths; 38 | 39 | // Base Class to create effect on incoming images and output one image 40 | // 41 | // Usage: 42 | // - setup(...) 43 | // - initialize( size of window/image ) 44 | // - setInput( image.descriptor ) 45 | // - run 46 | // - getOutput 47 | class PostEffect 48 | { 49 | 50 | 51 | public: 52 | PostEffect() = default; 53 | 54 | virtual void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) 55 | { 56 | m_device = device; 57 | m_queueIndex = queueIndex; 58 | m_alloc = allocator; 59 | m_debug.setup(device); 60 | } 61 | 62 | virtual void initialize(const vk::Extent2D& size) 63 | { 64 | createRenderPass(); 65 | createDescriptorSet(); 66 | createPipeline(); 67 | updateRenderTarget(size); 68 | } 69 | 70 | // Attaching the input image 71 | virtual void setInputs(const std::vector& inputs) 72 | { 73 | std::vector writes; 74 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 0, &inputs[0].descriptor)); // ray tracing 75 | m_device.updateDescriptorSets(writes, nullptr); 76 | } 77 | 78 | // Internal image format 79 | virtual vk::Format getOutputFormat() { return vk::Format::eR8G8B8A8Unorm; } 80 | // Display controls for the post-process 81 | virtual bool uiSetup() { return false; } // return true when something changed 82 | virtual const std::string getShaderName() = 0; 83 | 84 | 85 | void setActive(bool active_) { m_active = active_; } 86 | bool isActive() { return m_active; } 87 | 88 | // Updating the output framebuffer when the image size is changing 89 | virtual void updateRenderTarget(const vk::Extent2D& size) 90 | { 91 | m_size = size; 92 | 93 | // Create new output image 94 | m_alloc->destroy(m_output); 95 | vk::SamplerCreateInfo samplerCreateInfo; // default values 96 | vk::ImageCreateInfo imageCreateInfo = 97 | nvvkpp::makeImage2DCreateInfo(m_size, getOutputFormat(), 98 | vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled); 99 | 100 | nvvk::Image image = m_alloc->createImage(imageCreateInfo); 101 | vk::ImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo); 102 | m_output = m_alloc->createTexture(image, ivInfo, samplerCreateInfo); 103 | 104 | { 105 | nvvk::CommandPool scb(m_device, m_queueIndex); 106 | auto cmdBuf = scb.createCommandBuffer(); 107 | nvvkpp::cmdBarrierImageLayout(cmdBuf, m_output.image, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal); 108 | m_alloc->finalizeAndReleaseStaging(); 109 | } 110 | 111 | 112 | // Create the Framebuffer and attach the texture 113 | m_device.destroy(m_framebuffer); 114 | vk::FramebufferCreateInfo info; 115 | info.setRenderPass(m_renderPass); 116 | info.setAttachmentCount(1); 117 | info.setPAttachments(reinterpret_cast(&m_output.descriptor.imageView)); 118 | info.setWidth(m_size.width); 119 | info.setHeight(m_size.height); 120 | info.setLayers(1); 121 | m_framebuffer = m_device.createFramebuffer(info); 122 | } 123 | 124 | virtual void destroy() 125 | { 126 | m_alloc->destroy(m_output); 127 | m_device.destroyFramebuffer(m_framebuffer); 128 | m_device.destroyDescriptorSetLayout(m_descSetLayout); 129 | m_device.destroyRenderPass(m_renderPass); 130 | m_device.destroyPipeline(m_pipeline); 131 | m_device.destroyPipelineLayout(m_pipelineLayout); 132 | m_device.destroyDescriptorPool(m_descPool); 133 | } 134 | 135 | virtual const nvvk::Texture getOutput() { return m_output; } 136 | 137 | 138 | // Executing the the post-process 139 | virtual void execute(const vk::CommandBuffer& cmdBuf) 140 | { 141 | vk::ClearValue clearValues; // default is 0,0,0,0 142 | //clearValues[0].setColor(std::array({0.f, 0.f, 0.f, 0.f})); 143 | 144 | vk::RenderPassBeginInfo renderPassBeginInfo = {m_renderPass, m_framebuffer, {{}, m_size}, 1, &clearValues}; 145 | cmdBuf.beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline); 146 | if(m_active) 147 | { 148 | cmdBuf.setViewport(0, {vk::Viewport(0, 0, m_size.width, m_size.height, 0, 1)}); 149 | cmdBuf.setScissor(0, {{{0, 0}, {m_size.width, m_size.height}}}); 150 | cmdBuf.bindPipeline(vk::PipelineBindPoint::eGraphics, m_pipeline); 151 | cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, m_pipelineLayout, 0, m_descSet, {}); 152 | cmdBuf.draw(3, 1, 0, 0); 153 | } 154 | cmdBuf.endRenderPass(); 155 | } 156 | 157 | protected: 158 | // Render pass, one clear, no depth 159 | void createRenderPass() 160 | { 161 | if(m_renderPass) 162 | m_device.destroyRenderPass(m_renderPass); 163 | 164 | // Color attachment 165 | vk::AttachmentDescription attachments; 166 | attachments.setFormat(getOutputFormat()); // image format of the output image 167 | attachments.setLoadOp(vk::AttachmentLoadOp::eClear); 168 | attachments.setFinalLayout(vk::ImageLayout::eShaderReadOnlyOptimal); 169 | 170 | vk::AttachmentReference colorReference{0, vk::ImageLayout::eColorAttachmentOptimal}; 171 | vk::SubpassDescription subpassDescription; 172 | subpassDescription.setColorAttachmentCount(1); 173 | subpassDescription.setPColorAttachments(&colorReference); 174 | 175 | vk::RenderPassCreateInfo renderPassInfo{{}, 1, &attachments, 1, &subpassDescription}; 176 | m_renderPass = m_device.createRenderPass(renderPassInfo); 177 | 178 | std::string rp_name = getShaderName(); 179 | rp_name = rp_name.substr(rp_name.find_first_of("/") + 1, rp_name.find_first_of(".") - rp_name.find_first_of("/") - 1); 180 | m_debug.setObjectName(m_renderPass, rp_name.c_str()); 181 | } 182 | 183 | // One input image and push constant to control the effect 184 | virtual void createDescriptorSet() 185 | { 186 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth from ray tracing 187 | m_descSetLayout = m_descSetBind.createLayout(m_device); 188 | m_descPool = m_descSetBind.createPool(m_device); 189 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 190 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 0, nullptr}); 191 | m_debug.setObjectName(m_pipelineLayout, "post_effect"); 192 | } 193 | 194 | 195 | // Creating the shading pipeline 196 | void createPipeline() 197 | { 198 | const std::string& fragProg = getShaderName(); 199 | // Pipeline: completely generic, no vertices 200 | nvvkpp::GraphicsPipelineGeneratorCombined pipelineGenerator(m_device, m_pipelineLayout, m_renderPass); 201 | pipelineGenerator.addShader(nvh::loadFile("spv/passthrough.vert.spv", true, defaultSearchPaths), vk::ShaderStageFlagBits::eVertex); 202 | pipelineGenerator.addShader(nvh::loadFile(fragProg, true, defaultSearchPaths), vk::ShaderStageFlagBits::eFragment); 203 | pipelineGenerator.rasterizationState.setCullMode(vk::CullModeFlagBits::eNone); 204 | m_pipeline = pipelineGenerator.createPipeline(); 205 | } 206 | 207 | // 208 | bool m_active{true}; 209 | vk::RenderPass m_renderPass; 210 | vk::Pipeline m_pipeline; 211 | vk::PipelineLayout m_pipelineLayout; 212 | nvvkpp::DescriptorSetBindings m_descSetBind; 213 | vk::DescriptorPool m_descPool; 214 | vk::DescriptorSetLayout m_descSetLayout; 215 | vk::DescriptorSet m_descSet; 216 | vk::Extent2D m_size{0, 0}; 217 | vk::Framebuffer m_framebuffer; 218 | nvvk::Texture m_output; 219 | vk::Device m_device; 220 | uint32_t m_queueIndex; 221 | nvvkpp::ResourceAllocator* m_alloc{nullptr}; 222 | nvvk::DebugUtil m_debug; 223 | }; 224 | -------------------------------------------------------------------------------- /post_kuwahara.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | 25 | // Create a smoothing effect on the image https://en.wikipedia.org/wiki/Kuwahara_filter 26 | class PostKuwahara : public PostEffect 27 | { 28 | public: 29 | PostKuwahara() { m_active = false; } 30 | 31 | const std::string getShaderName() override { return R"(spv/kuwahara.frag.spv)"; } 32 | 33 | void execute(const vk::CommandBuffer& cmdBuf) override 34 | { 35 | if(!m_active) 36 | return; 37 | 38 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 39 | PostEffect::execute(cmdBuf); 40 | } 41 | 42 | bool uiSetup() override { return ImGui::SliderInt("radius", &m_pushCnt.radius, 1, 8); } 43 | 44 | private: 45 | // One input image and push constant to control the effect 46 | void createDescriptorSet() override 47 | { 48 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 49 | 50 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth from ray tracing 51 | m_descSetLayout = m_descSetBind.createLayout(m_device); 52 | m_descPool = m_descSetBind.createPool(m_device); 53 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 54 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 55 | m_debug.setObjectName(m_pipelineLayout, "kuwahara"); 56 | } 57 | 58 | struct PushConstant 59 | { 60 | int radius{3}; 61 | }; 62 | PushConstant m_pushCnt; 63 | }; 64 | -------------------------------------------------------------------------------- /post_kuwahara_aniso.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | 25 | // This is the implementation of the anisotropic Kuwahara filter (https://hpi.de/en/doellner/rendering/akf.html) 26 | // Paper : http://www.kyprianidis.com/p/pg2009/jkyprian-pg2009.pdf 27 | // Preview App' : https://code.google.com/archive/p/gpuakf/downloads 28 | // Code : https://code.google.com/archive/p/gpuakf/source/default/source 29 | 30 | // Apply anisotropic Kuwahara filter (https://hpi.de/en/doellner/rendering/akf.html) 31 | class PostKuwaharaAniso : public PostEffect 32 | { 33 | public: 34 | const std::string getShaderName() override { return R"(spv/kuwa_aniso.frag.spv)"; } 35 | 36 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) override 37 | { 38 | m_sst.setup(device, physicalDevice, queueIndex, allocator); 39 | m_gauss.setup(device, physicalDevice, queueIndex, allocator); 40 | m_tfm.setup(device, physicalDevice, queueIndex, allocator); 41 | PostEffect::setup(device, physicalDevice, queueIndex, allocator); 42 | } 43 | 44 | void initialize(const vk::Extent2D& size) override 45 | { 46 | m_sst.initialize(size); 47 | m_gauss.initialize(size); 48 | m_tfm.initialize(size); 49 | PostEffect::initialize(size); 50 | updateKernel(); 51 | } 52 | 53 | void setInputs(const std::vector& inputs) override 54 | { 55 | m_sst.setInputs(inputs); 56 | m_gauss.setInputs({m_sst.getOutput()}); 57 | m_tfm.setInputs({m_gauss.getOutput()}); 58 | 59 | const nvvk::Texture& outTfm = m_tfm.getOutput(); 60 | 61 | std::vector writes; 62 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 0, &inputs[0].descriptor)); // ray tracing 63 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 1, &m_kernel.descriptor)); // kernel 64 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 2, &outTfm.descriptor)); // kuwahara info 65 | m_device.updateDescriptorSets(writes, nullptr); 66 | } 67 | 68 | void updateRenderTarget(const vk::Extent2D& size) override 69 | { 70 | m_sst.updateRenderTarget(size); 71 | m_gauss.updateRenderTarget(size); 72 | m_tfm.updateRenderTarget(size); 73 | PostEffect::updateRenderTarget(size); 74 | } 75 | 76 | // Executing the effect 77 | void execute(const vk::CommandBuffer& cmdBuf) override 78 | { 79 | if(!m_active) 80 | return; 81 | 82 | m_sst.execute(cmdBuf); 83 | m_gauss.execute(cmdBuf); 84 | m_tfm.execute(cmdBuf); 85 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 86 | PostEffect::execute(cmdBuf); 87 | } 88 | 89 | void destroy() override 90 | { 91 | m_sst.destroy(); 92 | m_gauss.destroy(); 93 | m_tfm.destroy(); 94 | m_alloc->destroy(m_kernel); 95 | PostEffect::destroy(); 96 | } 97 | 98 | // UI Control 99 | bool uiSetup() override 100 | { 101 | bool changed{false}; 102 | // changed |= ImGui::InputFloat("alpha", &m_pushCnt.alpha); 103 | changed |= ImGui::SliderFloat("radius", &m_pushCnt.radius, 1, 20); 104 | changed |= ImGui::SliderInt("N", &m_Nsectors, 1, 16); 105 | if(changed) 106 | updateKernel(); 107 | return changed; 108 | } 109 | 110 | private: 111 | // One input image and push constant to control the effect 112 | void createDescriptorSet() override 113 | { 114 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 115 | 116 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // ray tracing image 117 | m_descSetBind.addBinding(vkDS(1, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // kernel 118 | m_descSetBind.addBinding(vkDS(2, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // from tfm 119 | m_descSetLayout = m_descSetBind.createLayout(m_device); 120 | m_descPool = m_descSetBind.createPool(m_device); 121 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 122 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 123 | m_debug.setObjectName(m_pipelineLayout, "kuwahara_aniso"); 124 | } 125 | 126 | 127 | void updateKernel() 128 | { 129 | int N = m_Nsectors; 130 | float smoothing = m_smoothing / 100.0f; // in % 131 | 132 | const int krnl_size = 32; 133 | const float sigma = 0.25f * (krnl_size - 1); 134 | 135 | float* krnl[4]; 136 | for(int k = 0; k < 4; ++k) 137 | { 138 | krnl[k] = new float[krnl_size * krnl_size]; 139 | make_sector(krnl[k], k, N, krnl_size, sigma, smoothing * sigma); 140 | } 141 | 142 | m_device.waitIdle(); 143 | m_alloc->destroy(m_kernel); 144 | { 145 | nvvkpp::ScopeCommandBuffer cmdBuf(m_device, m_queueIndex); 146 | vk::SamplerCreateInfo samplerCreateInfo; // default values 147 | vk::Extent2D size(krnl_size, krnl_size); 148 | vk::ImageCreateInfo imageCreateInfo = nvvkpp::makeImage2DCreateInfo(size, vk::Format::eR32Sfloat); 149 | 150 | nvvk::Image image = m_alloc->createImage(cmdBuf, krnl_size * krnl_size * sizeof(float), krnl[0], imageCreateInfo); 151 | vk::ImageViewCreateInfo ivInfo = nvvkpp::makeImageViewCreateInfo(image.image, imageCreateInfo); 152 | m_kernel = m_alloc->createTexture(image, ivInfo, samplerCreateInfo); 153 | 154 | 155 | nvvkpp::cmdBarrierImageLayout(cmdBuf, m_kernel.image, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal); 156 | } 157 | m_alloc->finalizeAndReleaseStaging(); 158 | m_debug.setObjectName(m_kernel.image, "Kernel"); 159 | 160 | for(auto& k : krnl) 161 | { 162 | delete k; 163 | } 164 | } 165 | 166 | 167 | static void gauss_filter(float* data, int width, int height, float sigma) 168 | { 169 | float twoSigma2 = 2.0f * sigma * sigma; 170 | int halfWidth = (int)ceil(2.0 * sigma); 171 | 172 | //float* src_data = new float[width * height]; 173 | std::vector srcData(width * height); 174 | 175 | memcpy(srcData.data(), data, width * height * sizeof(float)); 176 | 177 | for(int y = 0; y < height; ++y) 178 | { 179 | for(int x = 0; x < width; ++x) 180 | { 181 | float sum = 0; 182 | float w = 0; 183 | 184 | for(int i = -halfWidth; i <= halfWidth; ++i) 185 | { 186 | for(int j = -halfWidth; j <= halfWidth; ++j) 187 | { 188 | int xi = x + i; 189 | int yj = y + j; 190 | if((xi >= 0) && (xi < width) && (yj >= 0) && (yj < height)) 191 | { 192 | float r = sqrt((float)(i * i + j * j)); 193 | float k = exp(-r * r / twoSigma2); 194 | w += k; 195 | sum += k * srcData[xi + yj * width]; 196 | } 197 | } 198 | } 199 | 200 | data[x + y * width] = sum / w; 201 | } 202 | } 203 | 204 | // delete[] src_data; 205 | } 206 | 207 | 208 | void make_sector(float* krnl, int k, int N, int size, float sigma_r, float sigma_s) 209 | { 210 | float* p = krnl; 211 | for(int j = 0; j < size; ++j) 212 | { 213 | for(int i = 0; i < size; ++i) 214 | { 215 | float x = i - 0.5f * size + 0.5f; 216 | float y = j - 0.5f * size + 0.5f; 217 | float r = sqrtf((x * x + y * y)); 218 | 219 | float a = 0.5f * atan2(y, x) / glm::pi() + k * 1.0f / N; 220 | if(a > 0.5f) 221 | a -= 1.0f; 222 | if(a < -0.5f) 223 | a += 1.0f; 224 | 225 | if((fabs(a) <= 0.5f / N) && (r < 0.5f * size)) 226 | { 227 | *p = 1; 228 | } 229 | else 230 | { 231 | *p = 0; 232 | } 233 | ++p; 234 | } 235 | } 236 | 237 | gauss_filter(krnl, size, size, sigma_s); 238 | 239 | p = krnl; 240 | float mx = 0.0f; 241 | for(int j = 0; j < size; ++j) 242 | { 243 | for(int i = 0; i < size; ++i) 244 | { 245 | float x = i - 0.5f * size + 0.5f; 246 | float y = j - 0.5f * size + 0.5f; 247 | float r = sqrtf(x * x + y * y); 248 | *p *= exp(-0.5f * r * r / sigma_r / sigma_r); 249 | if(*p > mx) 250 | mx = *p; 251 | ++p; 252 | } 253 | } 254 | 255 | p = krnl; 256 | for(int j = 0; j < size; ++j) 257 | { 258 | for(int i = 0; i < size; ++i) 259 | { 260 | *p /= mx; 261 | ++p; 262 | } 263 | } 264 | } 265 | 266 | 267 | struct PushConstant 268 | { 269 | float radius{3.f}; 270 | float q{8.f}; 271 | float alpha{1.f}; 272 | }; 273 | PushConstant m_pushCnt; 274 | 275 | nvvk::Texture m_kernel; 276 | int m_Nsectors = 8; 277 | float m_smoothing = 33.33f; 278 | float m_sigma_t{2.0f}; 279 | 280 | 281 | struct PostKSst : public PostEffect 282 | { 283 | const std::string getShaderName() override { return R"(spv/kuwa_sst.frag.spv)"; } 284 | }; 285 | 286 | struct PostKTfm : public PostEffect 287 | { 288 | const std::string getShaderName() override { return R"(spv/kuwa_tfm.frag.spv)"; } 289 | }; 290 | 291 | struct PostGauss : public PostEffect 292 | { 293 | const std::string getShaderName() override { return R"(spv/kuwa_gauss.frag.spv)"; } 294 | }; 295 | 296 | PostKSst m_sst; 297 | PostKTfm m_tfm; 298 | PostGauss m_gauss; 299 | }; 300 | -------------------------------------------------------------------------------- /post_nrmdepth.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | 25 | // Extract the contour from the normal and depth buffer 26 | // And does a second post effect (FXAA) on the contour 27 | class PostNrmDepth : public PostEffect 28 | { 29 | public: 30 | const std::string getShaderName() override { return R"(spv/contour_normaldepth.frag.spv)"; } 31 | 32 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) override 33 | { 34 | m_fxaa.setup(device, physicalDevice, queueIndex, allocator); 35 | PostEffect::setup(device, physicalDevice, queueIndex, allocator); 36 | } 37 | 38 | void initialize(const vk::Extent2D& size) override 39 | { 40 | m_fxaa.initialize(size); 41 | PostEffect::initialize(size); 42 | } 43 | 44 | void setInputs(const std::vector& inputs, const nvvk::Buffer& minMaxBuffer) 45 | { 46 | m_fxaa.setInputs({m_output}); 47 | 48 | std::vector writes; 49 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 0, &inputs[0].descriptor)); // ray tracing 50 | vk::DescriptorBufferInfo bufInfo{minMaxBuffer.buffer, 0, VK_WHOLE_SIZE}; 51 | writes.emplace_back(m_descSetBind.makeWrite(m_descSet, 1, &bufInfo)); // zNear - zFar from compute shader 52 | m_device.updateDescriptorSets(writes, nullptr); 53 | } 54 | 55 | void updateRenderTarget(const vk::Extent2D& size) override 56 | { 57 | m_fxaa.updateRenderTarget(size); 58 | PostEffect::updateRenderTarget(size); 59 | } 60 | 61 | void execute(const vk::CommandBuffer& cmdBuf) override 62 | { 63 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 64 | PostEffect::execute(cmdBuf); 65 | if(m_useFxaa) 66 | m_fxaa.execute(cmdBuf); 67 | } 68 | 69 | const nvvk::Texture getOutput() override 70 | { 71 | if(m_useFxaa) 72 | return m_fxaa.getOutput(); 73 | 74 | return m_output; 75 | } 76 | 77 | void destroy() override 78 | { 79 | m_fxaa.destroy(); 80 | PostEffect::destroy(); 81 | } 82 | 83 | bool uiSetup() override 84 | { 85 | bool changed{false}; 86 | changed |= ImGui::SliderFloat("Normal Threshold", &m_pushCnt.normalDiffCoeff, 0.0f, 1.f, "%.3f", ImGuiSliderFlags_Logarithmic); 87 | changed |= ImGui::SliderFloat("Depth Threshold", &m_pushCnt.depthDiffCoeff, 0.00f, 10.f, "%.3f", ImGuiSliderFlags_Logarithmic); 88 | changed |= ImGui::Checkbox("FXAA on Inside Details", &m_useFxaa); 89 | 90 | return changed; 91 | } 92 | 93 | private: 94 | // One input image and push constant to control the effect 95 | void createDescriptorSet() override 96 | { 97 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 98 | 99 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth from ray tracing 100 | m_descSetBind.addBinding(vkDS(1, vkDT::eStorageBuffer, 1, vkSS::eFragment)); // Min/Max 101 | 102 | m_descSetLayout = m_descSetBind.createLayout(m_device); 103 | m_descPool = m_descSetBind.createPool(m_device); 104 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 105 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 106 | m_debug.setObjectName(m_pipelineLayout, "nrmdepth"); 107 | } 108 | 109 | struct PushConstant 110 | { 111 | float normalDiffCoeff{0.5f}; 112 | float depthDiffCoeff{1.f}; 113 | }; 114 | PushConstant m_pushCnt; 115 | 116 | // Second post effect to anti-alias lines 117 | struct PostFxaa : public PostEffect 118 | { 119 | const std::string getShaderName() override { return R"(spv/fxaa.frag.spv)"; } 120 | }; 121 | 122 | PostFxaa m_fxaa; 123 | bool m_useFxaa{true}; 124 | }; 125 | -------------------------------------------------------------------------------- /post_objcontour.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | 25 | // Extract the contour of the different objects and can apply a FXAA to this contour 26 | class PostObjContour : public PostEffect 27 | { 28 | public: 29 | const std::string getShaderName() override { return R"(spv/contour_objects.frag.spv)"; } 30 | 31 | 32 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) override 33 | { 34 | m_fxaa.setup(device, physicalDevice, queueIndex, allocator); 35 | PostEffect::setup(device, physicalDevice, queueIndex, allocator); 36 | } 37 | 38 | void initialize(const vk::Extent2D& size) override 39 | { 40 | m_fxaa.initialize(size); 41 | PostEffect::initialize(size); 42 | } 43 | 44 | void setInputs(const std::vector& inputs) override 45 | { 46 | m_fxaa.setInputs({m_output}); 47 | PostEffect::setInputs(inputs); 48 | } 49 | 50 | void updateRenderTarget(const vk::Extent2D& size) override 51 | { 52 | m_fxaa.updateRenderTarget(size); 53 | PostEffect::updateRenderTarget(size); 54 | } 55 | 56 | void execute(const vk::CommandBuffer& cmdBuf) override 57 | { 58 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 59 | PostEffect::execute(cmdBuf); 60 | if(m_useFxaa) 61 | m_fxaa.execute(cmdBuf); 62 | } 63 | 64 | const nvvk::Texture getOutput() override 65 | { 66 | if(m_useFxaa) 67 | return m_fxaa.getOutput(); 68 | 69 | return m_output; 70 | } 71 | 72 | void destroy() override 73 | { 74 | m_fxaa.destroy(); 75 | PostEffect::destroy(); 76 | } 77 | 78 | bool uiSetup() override 79 | { 80 | static const std::vector dbgItem1 = {"greater", "smaller", "thicker", "different"}; 81 | bool changed{false}; 82 | changed |= ImGui::Combo("Line Type", &m_pushCnt.method, dbgItem1.data(), int(dbgItem1.size())); 83 | changed |= ImGui::Checkbox("FXAA on Object Contour", &m_useFxaa); 84 | return changed; 85 | } 86 | 87 | private: 88 | // One input image and push constant to control the effect 89 | void createDescriptorSet() override 90 | { 91 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 92 | 93 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth from ray tracing 94 | m_descSetLayout = m_descSetBind.createLayout(m_device); 95 | m_descPool = m_descSetBind.createPool(m_device); 96 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 97 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 98 | m_debug.setObjectName(m_pipelineLayout, "objcontour"); 99 | } 100 | 101 | struct PushConstant 102 | { 103 | int method{2}; 104 | }; 105 | PushConstant m_pushCnt; 106 | 107 | // Second post effect to anti-alias lines 108 | struct PostFxaa : public PostEffect 109 | { 110 | const std::string getShaderName() override { return R"(spv/fxaa.frag.spv)"; } 111 | }; 112 | 113 | PostFxaa m_fxaa; 114 | bool m_useFxaa{true}; 115 | }; 116 | -------------------------------------------------------------------------------- /post_tonemapper.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | #include "post_effect.hpp" 24 | 25 | // Take as an input an image (RGB32F) and apply a tonemapper 26 | class Tonemapper : public PostEffect 27 | { 28 | public: 29 | const std::string getShaderName() override { return "spv/tonemap.frag.spv"; } 30 | 31 | // Executing the the tonemapper 32 | void execute(const vk::CommandBuffer& cmdBuf) override 33 | { 34 | if(!m_active) 35 | return; 36 | 37 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eFragment, 0, m_pushCnt); 38 | PostEffect::execute(cmdBuf); 39 | } 40 | 41 | // Controlling the tonemapper 42 | bool uiSetup() override 43 | { 44 | static const std::vector tmItem = {"Linear", "Uncharted 2", "Hejl Richard", "ACES"}; 45 | bool changed{false}; 46 | changed |= ImGui::Combo("Tonemapper", &m_pushCnt.tonemapper, tmItem.data(), static_cast(tmItem.size())); 47 | changed |= ImGui::InputFloat("Exposure", &m_pushCnt.exposure, 0.1f, 1.f); 48 | changed |= ImGui::InputFloat("Gamma", &m_pushCnt.gamma, .1f, 1.f); 49 | m_pushCnt.exposure = std::max(0.1f, std::min(m_pushCnt.exposure, 100.0f)); 50 | m_pushCnt.gamma = std::max(0.1f, std::min(m_pushCnt.gamma, 3.0f)); 51 | return changed; 52 | } 53 | 54 | private: 55 | // One input image and push constant to control the effect 56 | void createDescriptorSet() override 57 | { 58 | vk::PushConstantRange push_constants = {vk::ShaderStageFlagBits::eFragment, 0, sizeof(PushConstant)}; 59 | m_descSetBind.clear(); 60 | m_descSetBind.addBinding(vkDS(0, vkDT::eCombinedImageSampler, 1, vkSS::eFragment)); // Normal/depth from ray tracing 61 | m_descSetLayout = m_descSetBind.createLayout(m_device); 62 | m_descPool = m_descSetBind.createPool(m_device); 63 | m_descSet = nvvk::allocateDescriptorSet(m_device, m_descPool, m_descSetLayout); 64 | m_pipelineLayout = m_device.createPipelineLayout({{}, 1, &m_descSetLayout, 1, &push_constants}); 65 | m_debug.setObjectName(m_pipelineLayout, "tonemap"); 66 | } 67 | 68 | struct PushConstant 69 | { 70 | int tonemapper{1}; 71 | float gamma{2.2f}; 72 | float exposure{3.0f}; 73 | }; 74 | PushConstant m_pushCnt; 75 | }; 76 | -------------------------------------------------------------------------------- /rasterizer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | //-------------------------------------------------------------------------------------------------- 22 | // This example is loading a glTF scene and renders it with a very simple material 23 | // 24 | 25 | #include 26 | #include 27 | 28 | #include "nvh/fileoperations.hpp" 29 | #include "nvvk/images_vk.hpp" 30 | #include "nvvk/pipeline_vk.hpp" 31 | #include "nvvk/renderpasses_vk.hpp" 32 | #include "nvvk/shaders_vk.hpp" 33 | #include "rasterizer.hpp" 34 | 35 | 36 | extern std::vector defaultSearchPaths; 37 | 38 | void Rasterizer::setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) 39 | { 40 | m_device = device; 41 | m_queueIndex = queueIndex; 42 | m_debug.setup(device); 43 | m_alloc = allocator; 44 | } 45 | 46 | //-------------------------------------------------------------------------------------------------- 47 | // Overridden function called on shutdown 48 | // 49 | void Rasterizer::destroy() 50 | { 51 | m_device.waitIdle(); 52 | 53 | m_alloc->destroy(m_depthImage); 54 | for(auto& t : m_rasterizerOutput) 55 | m_alloc->destroy(t); 56 | 57 | m_device.destroy(m_renderPass); 58 | m_device.destroy(m_framebuffer); 59 | m_device.destroy(m_drawPipeline); 60 | m_device.destroy(m_pipelineLayout); 61 | m_device.destroy(m_depthImageView); 62 | 63 | m_framebuffer = vk::Framebuffer(); 64 | m_depthImageView = vk::ImageView(); 65 | } 66 | 67 | 68 | //-------------------------------------------------------------------------------- 69 | // Called at each frame, as fast as possible 70 | // 71 | void Rasterizer::run(const vk::CommandBuffer& cmdBuf, const vk::DescriptorSet& dsetScene, int frame /*= 0*/) 72 | { 73 | auto dbgLabel = m_debug.scopeLabel(cmdBuf, "Start rendering"); 74 | 75 | vk::ClearValue clearValues[3]; 76 | clearValues[0].setColor(makeClearColor(m_clearColor)); // Color buffer 77 | clearValues[1].setColor(std::array({0.0f, 0.0f, -1.0f, 0.f})); // Data buffer 78 | clearValues[2].setDepthStencil({1.0f, 0}); 79 | 80 | 81 | // Pre-recorded scene 82 | { 83 | auto dbgLabel = m_debug.scopeLabel(cmdBuf, "Recorded Scene"); 84 | 85 | vk::RenderPassBeginInfo renderPassBeginInfo{m_renderPass, m_framebuffer, {{}, m_outputSize}, 3, clearValues}; 86 | // Recorded 87 | //cmdBuf.beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eSecondaryCommandBuffers); 88 | //cmdBuf.executeCommands(m_recordedCmdBuffer); 89 | // Immediate 90 | cmdBuf.beginRenderPass(renderPassBeginInfo, vk::SubpassContents::eInline); 91 | setViewport(cmdBuf); 92 | render(cmdBuf, dsetScene); 93 | cmdBuf.endRenderPass(); 94 | } 95 | } 96 | 97 | //-------------------------------------------------------------------------------------------------- 98 | // When the pipeline is set for using dynamic, this becomes useful 99 | // 100 | void Rasterizer::setViewport(const vk::CommandBuffer& cmdBuf) 101 | { 102 | cmdBuf.setViewport(0, {vk::Viewport(0.0f, 0.0f, static_cast(m_outputSize.width), 103 | static_cast(m_outputSize.height), 0.0f, 1.0f)}); 104 | cmdBuf.setScissor(0, {{{0, 0}, {m_outputSize.width, m_outputSize.height}}}); 105 | } 106 | 107 | 108 | //-------------------------------------------------------------------------------------------------- 109 | // Building the command buffer, is in fact, recording all the calls needed to draw the frame in a 110 | // command buffer.This need to be call only if the number of objects in the scene is changing or 111 | // if the viewport is changing 112 | // 113 | void Rasterizer::recordCommandBuffer(const vk::CommandPool& cmdPool, const vk::DescriptorSet& dsetScene) 114 | { 115 | m_device.freeCommandBuffers(cmdPool, {m_recordedCmdBuffer}); 116 | m_recordedCmdBuffer = m_device.allocateCommandBuffers({cmdPool, vk::CommandBufferLevel::eSecondary, 1})[0]; 117 | 118 | vk::CommandBufferInheritanceInfo inheritance_info{m_renderPass}; 119 | vk::CommandBufferBeginInfo begin_info{vkCB::eSimultaneousUse | vkCB::eRenderPassContinue, &inheritance_info}; 120 | 121 | m_recordedCmdBuffer.begin(begin_info); 122 | { 123 | setViewport(m_recordedCmdBuffer); 124 | render(m_recordedCmdBuffer, dsetScene); 125 | } 126 | m_recordedCmdBuffer.end(); 127 | } 128 | 129 | 130 | //-------------------------------------------------------------------------------------------------- 131 | // The pipeline is how things are rendered, which shaders, type of primitives, depth test and more 132 | // 133 | void Rasterizer::createPipeline(const vk::DescriptorSetLayout& sceneDescSetLayout) 134 | { 135 | vk::PushConstantRange pushConstantRanges = {vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, 136 | sizeof(PushC)}; 137 | 138 | // Creating the pipeline layout 139 | vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo; 140 | pipelineLayoutCreateInfo.setSetLayoutCount(1); 141 | pipelineLayoutCreateInfo.setPSetLayouts(&sceneDescSetLayout); 142 | pipelineLayoutCreateInfo.setPushConstantRangeCount(1); 143 | pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstantRanges); 144 | m_pipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo); 145 | 146 | // Pipeline 147 | std::vector paths = defaultSearchPaths; 148 | nvvkpp::GraphicsPipelineGeneratorCombined gpb(m_device, m_pipelineLayout, m_renderPass); 149 | gpb.depthStencilState.depthTestEnable = true; 150 | 151 | gpb.addBlendAttachmentState(nvvk::GraphicsPipelineState::makePipelineColorBlendAttachmentState()); 152 | gpb.addShader(nvh::loadFile("spv/rasterizer.vert.spv", true, paths), vk::ShaderStageFlagBits::eVertex); 153 | gpb.addShader(nvh::loadFile("spv/rasterizer.frag.spv", true, paths), vk::ShaderStageFlagBits::eFragment); 154 | gpb.addBindingDescriptions({{0, sizeof(glm::vec3)}, {1, sizeof(glm::vec3)}, {2, sizeof(glm::vec2)}}); 155 | gpb.addAttributeDescriptions({ 156 | {0, 0, vk::Format::eR32G32B32Sfloat, 0}, // Position 157 | {1, 1, vk::Format::eR32G32B32Sfloat, 0}, // Normal 158 | {2, 2, vk::Format::eR32G32Sfloat, 0}, // Texcoord0 159 | }); 160 | gpb.rasterizationState.setCullMode(vk::CullModeFlagBits::eNone); 161 | m_drawPipeline = gpb.createPipeline(); 162 | 163 | m_debug.setObjectName(m_drawPipeline, "ShadingPipeline"); 164 | m_debug.setObjectName(gpb.getShaderModule(0), "VertexShader"); 165 | m_debug.setObjectName(gpb.getShaderModule(1), "FragmentShader"); 166 | } 167 | 168 | 169 | //-------------------------------------------------------------------------------------------------- 170 | // Rendering all glTF nodes 171 | // 172 | void Rasterizer::render(const vk::CommandBuffer& cmdBuff, const vk::DescriptorSet& dsetScene) 173 | { 174 | if(!m_drawPipeline) 175 | { 176 | return; 177 | } 178 | 179 | m_debug.setObjectName(cmdBuff, "Recored"); 180 | auto dgbLabel = m_debug.scopeLabel(cmdBuff, "Recording Scene"); 181 | 182 | // Pipeline to use for rendering the current scene 183 | cmdBuff.bindPipeline(vk::PipelineBindPoint::eGraphics, m_drawPipeline); 184 | 185 | // Offsets for the descriptor set and vertex buffer 186 | std::vector offsets = {0, 0, 0}; 187 | 188 | // Keeping track of the last material to avoid binding them again 189 | uint32_t lastMaterial = -1; 190 | 191 | std::vector vertexBuffers = {m_vertexBuffer->buffer, m_normalBuffer->buffer, m_uvBuffer->buffer}; 192 | cmdBuff.bindVertexBuffers(0, static_cast(vertexBuffers.size()), vertexBuffers.data(), offsets.data()); 193 | cmdBuff.bindIndexBuffer(m_indexBuffer->buffer, 0, vk::IndexType::eUint32); 194 | 195 | std::vector descriptorSets = {dsetScene}; 196 | 197 | // The pipeline uses four descriptor set, one for the scene information, one for the matrix of the instance, one for the textures and for the environment 198 | cmdBuff.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, m_pipelineLayout, 0, descriptorSets, {}); 199 | 200 | uint32_t idxNode = 0; 201 | for(auto& node : m_gltfScene->m_nodes) 202 | { 203 | auto dgbLabel = m_debug.scopeLabel(cmdBuff, std::string("Draw Mesh: " + std::to_string(node.primMesh))); 204 | auto& primitive = m_gltfScene->m_primMeshes[node.primMesh]; 205 | 206 | m_pushC.instID = idxNode++; 207 | m_pushC.matID = primitive.materialIndex; 208 | cmdBuff.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eFragment, 0, m_pushC); 209 | cmdBuff.drawIndexed(primitive.indexCount, 1, primitive.firstIndex, primitive.vertexOffset, 0); 210 | } 211 | } 212 | 213 | 214 | void Rasterizer::setToonSteps(int nbStep) 215 | { 216 | m_pushC.nbSteps = nbStep; 217 | } 218 | 219 | void Rasterizer::setToonLightDir(glm::vec3 lightDir) 220 | { 221 | m_pushC.lightDir = lightDir; 222 | } 223 | 224 | // Return all outputs 225 | const std::vector& Rasterizer::outputImages() const 226 | { 227 | return m_rasterizerOutput; 228 | } 229 | 230 | //-------------------------------------------------------------------------------------------------- 231 | // The display will render the recorded command buffer, then in a sub-pass, render the UI 232 | // 233 | void Rasterizer::createRenderPass() 234 | { 235 | m_renderPass = nvvkpp::createRenderPass(m_device, {vk::Format::eR32G32B32A32Sfloat, vk::Format::eR32G32B32A32Sfloat}, // color attachment 236 | m_depthFormat, // depth attachment 237 | 1, // Nb sub-passes 238 | true, // clearColor 239 | true, // clearDepth 240 | vk::ImageLayout::eUndefined, // initialLayout 241 | vk::ImageLayout::eGeneral); // finalLayout 242 | 243 | m_debug.setObjectName(m_renderPass, "General Render Pass"); 244 | } 245 | 246 | 247 | //-------------------------------------------------------------------------------------------------- 248 | // Making the two output images: color, data(normal, depth, ID) 249 | // 250 | void Rasterizer::createOutputImages(vk::Extent2D size) 251 | { 252 | for(auto& t : m_rasterizerOutput) 253 | m_alloc->destroy(t); 254 | m_rasterizerOutput.clear(); 255 | 256 | m_outputSize = size; 257 | auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage 258 | | vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eColorAttachment; 259 | vk::DeviceSize imgSize = size.width * size.height * 4 * sizeof(float); 260 | vk::Format format = vk::Format::eR32G32B32A32Sfloat; 261 | 262 | // Create two output image, the color and the data 263 | for(int i = 0; i < 2; i++) 264 | { 265 | nvvkpp::ScopeCommandBuffer cmdBuf(m_device, m_queueIndex); 266 | vk::SamplerCreateInfo samplerCreateInfo; // default values 267 | vk::ImageCreateInfo imageCreateInfo = nvvkpp::makeImage2DCreateInfo(size, format, usage); 268 | 269 | nvvk::Image image = m_alloc->createImage(cmdBuf, imgSize, nullptr, imageCreateInfo, vk::ImageLayout::eGeneral); 270 | vk::ImageViewCreateInfo ivInfo = nvvk::makeImageViewCreateInfo(image.image, imageCreateInfo); 271 | nvvk::Texture txt = m_alloc->createTexture(image, ivInfo, samplerCreateInfo); 272 | txt.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; 273 | 274 | m_rasterizerOutput.push_back(txt); 275 | } 276 | { 277 | nvvkpp::ScopeCommandBuffer cmdBuf(m_device, m_queueIndex); 278 | createDepthBuffer(cmdBuf, size); 279 | } 280 | createFrameBuffer(); 281 | } 282 | 283 | //-------------------------------------------------------------------------------------------------- 284 | // Create the framebuffers in which the images will be rendered 285 | // - Swapchain need to be created before calling this 286 | // 287 | void Rasterizer::createFrameBuffer() 288 | { 289 | // Recreate the frame buffers 290 | m_device.destroy(m_framebuffer); 291 | 292 | // Array of attachment (color, depth) 293 | std::array attachments; 294 | 295 | // Create frame buffers for every swap chain image 296 | vk::FramebufferCreateInfo framebufferCreateInfo; 297 | framebufferCreateInfo.renderPass = m_renderPass; 298 | framebufferCreateInfo.attachmentCount = 3; 299 | framebufferCreateInfo.width = m_outputSize.width; 300 | framebufferCreateInfo.height = m_outputSize.height; 301 | framebufferCreateInfo.layers = 1; 302 | framebufferCreateInfo.pAttachments = attachments.data(); 303 | 304 | // Create frame buffers for every swap chain image 305 | attachments[0] = m_rasterizerOutput[0].descriptor.imageView; // Color 306 | attachments[1] = m_rasterizerOutput[1].descriptor.imageView; // Data 307 | attachments[2] = m_depthImageView; // Depth 308 | m_framebuffer = m_device.createFramebuffer(framebufferCreateInfo); 309 | 310 | std::string name = std::string("Rasterizer_Framebuffer"); 311 | #if DEBUG 312 | m_device.setDebugUtilsObjectNameEXT( 313 | {vk::ObjectType::eFramebuffer, reinterpret_cast(m_framebuffer), name.c_str()}); 314 | #endif 315 | } 316 | 317 | 318 | //-------------------------------------------------------------------------------------------------- 319 | // Creating an image to be used as depth buffer 320 | // 321 | void Rasterizer::createDepthBuffer(vk::CommandBuffer commandBuffer, vk::Extent2D imageSize) 322 | { 323 | m_alloc->destroy(m_depthImage); 324 | m_device.destroy(m_depthImageView); 325 | 326 | vk::ImageCreateInfo imageInfo = 327 | nvvkpp::makeImage2DCreateInfo(imageSize, m_depthFormat, vk::ImageUsageFlagBits::eDepthStencilAttachment); 328 | m_depthImage = m_alloc->createImage(imageInfo); 329 | m_debug.setObjectName(m_depthImage.image, "m_depthImage"); 330 | 331 | vk::ImageViewCreateInfo viewInfo = 332 | nvvkpp::makeImage2DViewCreateInfo(m_depthImage.image, m_depthFormat, vk::ImageAspectFlagBits::eDepth); 333 | m_depthImageView = m_device.createImageView(viewInfo); 334 | m_debug.setObjectName(m_depthImageView, "m_depthImageView"); 335 | 336 | // Set layout to VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL 337 | nvvkpp::cmdBarrierImageLayout(commandBuffer, // Command buffer 338 | m_depthImage.image, // Image 339 | vk::ImageLayout::eUndefined, // Old layout 340 | vk::ImageLayout::eDepthStencilAttachmentOptimal, // New layout 341 | vk::ImageAspectFlagBits::eDepth | vk::ImageAspectFlagBits::eStencil); 342 | } 343 | 344 | 345 | void Rasterizer::setObjectPointers(nvh::GltfScene* gltfScene, 346 | nvvk::Buffer* vertexBuffer, 347 | nvvk::Buffer* normalBuffer, 348 | nvvk::Buffer* uvBuffer, 349 | nvvk::Buffer* indexBuffer) 350 | { 351 | m_gltfScene = gltfScene; 352 | m_vertexBuffer = vertexBuffer; 353 | m_normalBuffer = normalBuffer; 354 | m_uvBuffer = uvBuffer; 355 | m_indexBuffer = indexBuffer; 356 | } 357 | -------------------------------------------------------------------------------- /rasterizer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #include 22 | 23 | #include "nvvk/vulkanhppsupport.hpp" 24 | #include "nvh/gltfscene.hpp" 25 | #include "nvvk/commands_vk.hpp" 26 | #include "nvvk/debug_util_vk.hpp" 27 | #include "nvvk/descriptorsets_vk.hpp" 28 | 29 | #include "vk_util.hpp" 30 | 31 | //-------------------------------------------------------------------------------------------------- 32 | // Simple example showing a cube, camera movement and post-process 33 | // 34 | class Rasterizer 35 | { 36 | public: 37 | Rasterizer() = default; 38 | 39 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator); 40 | void setObjectPointers(nvh::GltfScene* gltfScene, 41 | nvvk::Buffer* vertexBuffer, 42 | nvvk::Buffer* normalBuffer, 43 | nvvk::Buffer* uvBuffer, 44 | nvvk::Buffer* indexBuffer); 45 | 46 | // Executing the rasterizer 47 | void run(const vk::CommandBuffer& cmdBuf, const vk::DescriptorSet& dsetScene, int frame = 0); 48 | 49 | // Return the rendered image 50 | const std::vector& outputImages() const; 51 | 52 | void destroy(); 53 | void recordCommandBuffer(const vk::CommandPool& cmdPool, const vk::DescriptorSet& dsetScene); 54 | void createOutputImages(vk::Extent2D size); 55 | void createFrameBuffer(); 56 | void createRenderPass(); 57 | void createPipeline(const vk::DescriptorSetLayout& sceneDescSetLayout); 58 | 59 | void setClearColor(glm::vec3& _color) { m_clearColor = _color; } 60 | void setToonSteps(int nbStep); 61 | void setToonLightDir(glm::vec3 lightDir); 62 | 63 | private: 64 | void createDepthBuffer(vk::CommandBuffer commandBuffer, vk::Extent2D imageSize); 65 | void setViewport(const vk::CommandBuffer& cmdBuf); 66 | void render(const vk::CommandBuffer& cmdBuff, const vk::DescriptorSet& dsetScene); 67 | 68 | 69 | struct PushC 70 | { 71 | glm::vec3 lightDir{-1, -1, -1}; 72 | int nbSteps{5}; 73 | int instID{0}; 74 | int matID{0}; 75 | } m_pushC; 76 | 77 | glm::vec3 m_clearColor{0, 0, 0}; 78 | 79 | // Rasterizer 80 | vk::PipelineLayout m_pipelineLayout; 81 | vk::Pipeline m_drawPipeline; 82 | vk::CommandBuffer m_recordedCmdBuffer; 83 | std::vector m_rasterizerOutput; // many outputs (2) 84 | nvvk::Image m_depthImage; 85 | vk::ImageView m_depthImageView; 86 | vk::Extent2D m_outputSize; 87 | vk::RenderPass m_renderPass; 88 | vk::Framebuffer m_framebuffer; 89 | vk::Format m_depthFormat{vk::Format::eD24UnormS8Uint}; 90 | 91 | // Scene data 92 | nvh::GltfScene* m_gltfScene{nullptr}; // The scene 93 | nvvk::Buffer* m_vertexBuffer{nullptr}; 94 | nvvk::Buffer* m_normalBuffer{nullptr}; 95 | nvvk::Buffer* m_uvBuffer{nullptr}; 96 | nvvk::Buffer* m_indexBuffer{nullptr}; 97 | 98 | // Vulkan core 99 | vk::Device m_device; 100 | nvvk::DebugUtil m_debug; 101 | uint32_t m_queueIndex; 102 | nvvkpp::ResourceAllocator* m_alloc{nullptr}; 103 | }; 104 | -------------------------------------------------------------------------------- /raypick.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | ////////////////////////////////////////////////////////////////////////// 24 | // Raytracing implementation for the Vulkan Interop (G-Buffers) 25 | ////////////////////////////////////////////////////////////////////////// 26 | 27 | 28 | #include "nvh/fileoperations.hpp" 29 | #include "nvvk/debug_util_vk.hpp" 30 | #include "nvvk/descriptorsets_vk.hpp" 31 | #include "nvvk/shaders_vk.hpp" 32 | #include "nvvk/vulkanhppsupport.hpp" 33 | #include "vk_util.hpp" 34 | 35 | 36 | extern std::vector defaultSearchPaths; 37 | 38 | 39 | struct RayPicker 40 | { 41 | private: 42 | std::vector m_groups; 43 | 44 | public: 45 | struct PushConstant 46 | { 47 | float pickX{0}; 48 | float pickY{0}; 49 | } m_pushC; 50 | 51 | struct PickResult 52 | { 53 | glm::vec4 worldPos{0, 0, 0, 0}; 54 | glm::vec4 barycentrics{0, 0, 0, 0}; 55 | uint32_t intanceID{0}; 56 | uint32_t intanceCustomID{0}; 57 | uint32_t primitiveID{0}; 58 | }; 59 | 60 | nvvk::Buffer m_pickResult; 61 | nvvk::Buffer m_sbtBuffer; 62 | 63 | nvvkpp::DescriptorSetBindings m_binding; 64 | 65 | vk::DescriptorPool m_descPool; 66 | vk::DescriptorSetLayout m_descSetLayout; 67 | vk::DescriptorSet m_descSet; 68 | vk::PipelineLayout m_pipelineLayout; 69 | vk::Pipeline m_pipeline; 70 | vk::PhysicalDeviceRayTracingPropertiesNV m_raytracingProperties; 71 | vk::AccelerationStructureNV m_tlas; 72 | vk::PhysicalDevice m_physicalDevice; 73 | vk::Device m_device; 74 | uint32_t m_queueIndex; 75 | nvvkpp::ResourceAllocator* m_alloc{nullptr}; 76 | nvvk::DebugUtil m_debug; 77 | 78 | RayPicker() = default; 79 | 80 | 81 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) 82 | { 83 | m_physicalDevice = physicalDevice; 84 | m_device = device; 85 | m_queueIndex = queueIndex; 86 | m_debug.setup(device); 87 | m_alloc = allocator; 88 | } 89 | 90 | 91 | VkBuffer outputResult() const { return m_pickResult.buffer; } 92 | 93 | void destroy() 94 | { 95 | m_alloc->destroy(m_pickResult); 96 | m_alloc->destroy(m_sbtBuffer); 97 | m_device.destroyDescriptorSetLayout(m_descSetLayout); 98 | m_device.destroyPipelineLayout(m_pipelineLayout); 99 | m_device.destroyPipeline(m_pipeline); 100 | m_device.destroyDescriptorPool(m_descPool); 101 | } 102 | 103 | void initialize(const vk::AccelerationStructureNV& tlas, const vk::DescriptorBufferInfo& sceneUbo) 104 | { 105 | 106 | m_tlas = tlas; 107 | 108 | // Query the values of shaderHeaderSize and maxRecursionDepth in current implementation 109 | m_raytracingProperties = 110 | m_physicalDevice.getProperties2() 111 | .get(); 112 | 113 | 114 | createOutputResult(); 115 | createDescriptorSet(sceneUbo); 116 | createPipeline(); 117 | createShadingBindingTable(); 118 | } 119 | 120 | void createOutputResult() 121 | { 122 | m_alloc->destroy(m_pickResult); 123 | m_pickResult = 124 | m_alloc->createBuffer(sizeof(PickResult), vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eStorageBuffer, 125 | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 126 | m_debug.setObjectName(m_pickResult.buffer, "PickResult"); 127 | } 128 | 129 | void createDescriptorSet(const vk::DescriptorBufferInfo& sceneUbo) 130 | { 131 | m_binding.clear(); 132 | m_binding.addBinding(vkDS(0, vkDT::eAccelerationStructureNV, 1, vkSS::eRaygenNV | vkSS::eClosestHitNV)); 133 | m_binding.addBinding(vkDS(1, vkDT::eStorageBuffer, 1, vkSS::eRaygenNV)); 134 | m_binding.addBinding(vkDS(2, vkDT::eUniformBuffer, 1, vkSS::eRaygenNV | vkSS::eClosestHitNV)); 135 | 136 | m_descPool = m_binding.createPool(m_device); 137 | m_descSetLayout = m_binding.createLayout(m_device); 138 | m_descSet = m_device.allocateDescriptorSets({m_descPool, 1, &m_descSetLayout})[0]; 139 | 140 | vk::WriteDescriptorSetAccelerationStructureNV descAsInfo{1, &m_tlas}; 141 | 142 | vk::DescriptorBufferInfo pickDesc{m_pickResult.buffer, 0, VK_WHOLE_SIZE}; 143 | std::vector writes; 144 | writes.emplace_back(m_binding.makeWrite(m_descSet, 0, &descAsInfo)); 145 | writes.emplace_back(m_binding.makeWrite(m_descSet, 1, &pickDesc)); 146 | writes.emplace_back(m_binding.makeWrite(m_descSet, 2, &sceneUbo)); 147 | m_device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr); 148 | } 149 | 150 | 151 | void createPipeline() 152 | { 153 | vk::ShaderModule raygenSM = nvvk::createShaderModule(m_device, nvh::loadFile("spv/pick.rgen.spv", true, defaultSearchPaths)); 154 | vk::ShaderModule missSM = nvvk::createShaderModule(m_device, nvh::loadFile("spv/pick.rmiss.spv", true, defaultSearchPaths)); 155 | vk::ShaderModule chitSM = nvvk::createShaderModule(m_device, nvh::loadFile("spv/pick.rchit.spv", true, defaultSearchPaths)); 156 | 157 | std::vector stages; 158 | 159 | // Raygen 160 | stages.push_back({{}, vk::ShaderStageFlagBits::eRaygenNV, raygenSM, "main"}); 161 | vk::RayTracingShaderGroupCreateInfoNV rg{vk::RayTracingShaderGroupTypeNV::eGeneral, VK_SHADER_UNUSED_NV, 162 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 163 | rg.setGeneralShader(static_cast(stages.size() - 1)); 164 | m_groups.push_back(rg); 165 | // Miss - TODO remove 166 | stages.push_back({{}, vk::ShaderStageFlagBits::eMissNV, missSM, "main"}); 167 | vk::RayTracingShaderGroupCreateInfoNV mg{vk::RayTracingShaderGroupTypeNV::eGeneral, VK_SHADER_UNUSED_NV, 168 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 169 | mg.setGeneralShader(static_cast(stages.size() - 1)); 170 | m_groups.push_back(mg); 171 | // Hit 172 | stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitNV, chitSM, "main"}); 173 | vk::RayTracingShaderGroupCreateInfoNV hg{vk::RayTracingShaderGroupTypeNV::eTrianglesHitGroup, VK_SHADER_UNUSED_NV, 174 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 175 | hg.setClosestHitShader(static_cast(stages.size() - 1)); 176 | m_groups.push_back(hg); 177 | 178 | vk::PushConstantRange pushConstant{vk::ShaderStageFlagBits::eRaygenNV, 0, sizeof(PushConstant)}; 179 | vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo; 180 | pipelineLayoutCreateInfo.setSetLayoutCount(1); 181 | pipelineLayoutCreateInfo.setPSetLayouts(&m_descSetLayout); 182 | pipelineLayoutCreateInfo.setPushConstantRangeCount(1); 183 | pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstant); 184 | m_pipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo); 185 | m_debug.setObjectName(m_pipelineLayout, "raypick"); 186 | 187 | // Assemble the shader stages and recursion depth info into the raytracing pipeline 188 | vk::RayTracingPipelineCreateInfoNV rayPipelineInfo; 189 | rayPipelineInfo.setStageCount(static_cast(stages.size())); 190 | rayPipelineInfo.setPStages(stages.data()); 191 | rayPipelineInfo.setGroupCount(static_cast(m_groups.size())); 192 | rayPipelineInfo.setPGroups(m_groups.data()); 193 | rayPipelineInfo.setMaxRecursionDepth(2); 194 | rayPipelineInfo.setLayout(m_pipelineLayout); 195 | m_pipeline = m_device.createRayTracingPipelineNV({}, rayPipelineInfo).value; 196 | 197 | m_device.destroyShaderModule(raygenSM); 198 | m_device.destroyShaderModule(missSM); 199 | m_device.destroyShaderModule(chitSM); 200 | } 201 | 202 | 203 | void createShadingBindingTable() 204 | { 205 | auto groupCount = static_cast(m_groups.size()); // 3 shaders: raygen, miss, chit 206 | uint32_t groupHandleSize = m_raytracingProperties.shaderGroupHandleSize; // Size of a program identifier 207 | uint32_t alignSize = m_raytracingProperties.shaderGroupBaseAlignment; // Size of a program identifier 208 | 209 | 210 | // Fetch all the shader handles used in the pipeline, so that they can be written in the SBT 211 | uint32_t sbtSize = groupCount * alignSize; 212 | std::vector shaderHandleStorage(sbtSize); 213 | auto res = m_device.getRayTracingShaderGroupHandlesNV(m_pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data()); 214 | assert(res == vk::Result::eSuccess); 215 | 216 | m_sbtBuffer = m_alloc->createBuffer(sbtSize, vk::BufferUsageFlagBits::eTransferSrc, 217 | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 218 | m_debug.setObjectName(m_sbtBuffer.buffer, std::string("PickSBT").c_str()); 219 | 220 | // Write the handles in the SBT 221 | void* mapped = m_alloc->map(m_sbtBuffer); 222 | auto* pData = reinterpret_cast(mapped); 223 | for(uint32_t g = 0; g < groupCount; g++) 224 | { 225 | memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize); // raygen 226 | pData += alignSize; 227 | } 228 | m_alloc->unmap(m_sbtBuffer); 229 | } 230 | 231 | void run(const vk::CommandBuffer& cmdBuf, float x, float y) 232 | { 233 | m_pushC.pickX = x; 234 | m_pushC.pickY = y; 235 | 236 | uint32_t progSize = m_raytracingProperties.shaderGroupBaseAlignment; // Size of a program identifier 237 | cmdBuf.bindPipeline(vk::PipelineBindPoint::eRayTracingNV, m_pipeline); 238 | cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingNV, m_pipelineLayout, 0, {m_descSet}, {}); 239 | cmdBuf.pushConstants(m_pipelineLayout, vk::ShaderStageFlagBits::eRaygenNV, 0, m_pushC); 240 | 241 | vk::DeviceSize rayGenOffset = 0 * progSize; 242 | vk::DeviceSize missOffset = 1 * progSize; 243 | vk::DeviceSize missStride = progSize; 244 | vk::DeviceSize hitGroupOffset = 2 * progSize; // Jump over the miss 245 | vk::DeviceSize hitGroupStride = progSize; 246 | 247 | cmdBuf.traceRaysNV(m_sbtBuffer.buffer, rayGenOffset, // 248 | m_sbtBuffer.buffer, missOffset, missStride, // 249 | m_sbtBuffer.buffer, hitGroupOffset, hitGroupStride, // 250 | m_sbtBuffer.buffer, 0, 0, // 251 | 1, 1, // 252 | 1 /*, NVVKPP_DISPATCHER*/); 253 | 254 | vk::BufferMemoryBarrier bmb{vk::AccessFlagBits::eMemoryWrite, 255 | vk::AccessFlagBits::eMemoryRead, 256 | VK_QUEUE_FAMILY_IGNORED, 257 | VK_QUEUE_FAMILY_IGNORED, 258 | m_pickResult.buffer, 259 | 0, 260 | VK_WHOLE_SIZE}; 261 | cmdBuf.pipelineBarrier(vk::PipelineStageFlagBits::eTopOfPipe, vk::PipelineStageFlagBits::eTopOfPipe, 262 | vk::DependencyFlagBits::eDeviceGroup, 0, nullptr, 1, &bmb, 0, nullptr); 263 | } 264 | 265 | PickResult getResult() 266 | { 267 | PickResult pr; 268 | void* mapped = m_alloc->map(m_pickResult); 269 | memcpy(&pr, mapped, sizeof(PickResult)); 270 | m_alloc->unmap(m_pickResult); 271 | return pr; 272 | } 273 | }; 274 | -------------------------------------------------------------------------------- /raytracer.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #include "raytracer.hpp" 22 | #include "imgui.h" 23 | #include "nvh/fileoperations.hpp" 24 | #include "nvvk/images_vk.hpp" 25 | #include "nvvk/shaders_vk.hpp" 26 | 27 | extern std::vector defaultSearchPaths; 28 | 29 | Raytracer::Raytracer() = default; 30 | 31 | //-------------------------------------------------------------------------------------------------- 32 | // Initializing the allocator and querying the raytracing properties 33 | // 34 | void Raytracer::setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator) 35 | { 36 | m_device = device; 37 | m_queueIndex = queueIndex; 38 | m_debug.setup(device); 39 | m_alloc = allocator; 40 | 41 | // Requesting raytracing properties 42 | auto properties = physicalDevice.getProperties2(); 43 | m_rtProperties = properties.get(); 44 | 45 | if(m_rtProperties.shaderGroupHandleSize != 0) 46 | m_bValid = true; 47 | else 48 | { 49 | m_bValid = false; 50 | return; 51 | } 52 | m_rtBuilder.setup(device, allocator, queueIndex); 53 | } 54 | 55 | const std::vector& Raytracer::outputImages() const 56 | { 57 | return m_raytracingOutput; 58 | } 59 | 60 | int Raytracer::maxFrames() const 61 | { 62 | return m_maxFrames; 63 | } 64 | 65 | void Raytracer::destroy() 66 | { 67 | for(auto& t : m_raytracingOutput) 68 | m_alloc->destroy(t); 69 | m_rtBuilder.destroy(); 70 | m_device.destroy(m_descPool); 71 | m_device.destroy(m_descSetLayout); 72 | m_device.destroy(m_pipeline); 73 | m_device.destroy(m_pipelineLayout); 74 | m_alloc->destroy(m_sbtBuffer); 75 | m_alloc->destroy(m_rtPrimLookup); 76 | m_binding.clear(); 77 | } 78 | 79 | //-------------------------------------------------------------------------------------------------- 80 | // Making all output images: color, normal, ... 81 | // 82 | void Raytracer::createOutputImages(vk::Extent2D size) 83 | { 84 | for(auto& t : m_raytracingOutput) 85 | m_alloc->destroy(t); 86 | m_raytracingOutput.clear(); 87 | 88 | m_outputSize = size; 89 | auto usage = vk::ImageUsageFlagBits::eSampled | vk::ImageUsageFlagBits::eStorage | vk::ImageUsageFlagBits::eTransferSrc; 90 | vk::DeviceSize imgSize = size.width * size.height * 4 * sizeof(float); 91 | vk::Format format = vk::Format::eR32G32B32A32Sfloat; 92 | 93 | // Create two output image, the color and the data 94 | for(int i = 0; i < 2; i++) 95 | { 96 | nvvkpp::ScopeCommandBuffer cmdBuf(m_device, m_queueIndex); 97 | vk::SamplerCreateInfo samplerCreateInfo; // default values 98 | vk::ImageCreateInfo imageCreateInfo = nvvkpp::makeImage2DCreateInfo(size, format, usage); 99 | 100 | nvvk::Image image = m_alloc->createImage(cmdBuf, imgSize, nullptr, imageCreateInfo, vk::ImageLayout::eGeneral); 101 | vk::ImageViewCreateInfo ivInfo = nvvkpp::makeImageViewCreateInfo(image.image, imageCreateInfo); 102 | nvvk::Texture txt = m_alloc->createTexture(image, ivInfo, samplerCreateInfo); 103 | txt.descriptor.imageLayout = VK_IMAGE_LAYOUT_GENERAL; 104 | 105 | m_raytracingOutput.push_back(txt); 106 | } 107 | m_alloc->finalizeAndReleaseStaging(); 108 | } 109 | 110 | void Raytracer::createDescriptorSet() 111 | { 112 | using vkDS = vk::DescriptorSetLayoutBinding; 113 | using vkDT = vk::DescriptorType; 114 | using vkSS = vk::ShaderStageFlagBits; 115 | 116 | uint32_t nbOutput = static_cast(m_raytracingOutput.size()); 117 | 118 | m_binding.addBinding(vkDS(0, vkDT::eAccelerationStructureNV, 1, vkSS::eRaygenNV | vkSS::eClosestHitNV)); 119 | m_binding.addBinding(vkDS(1, vkDT::eStorageImage, nbOutput, vkSS::eRaygenNV)); // Output image 120 | m_binding.addBinding(vkDS(2, vkDT::eStorageBuffer, 1, vkSS::eClosestHitNV | vkSS::eAnyHitNV)); // Primitive info 121 | 122 | m_descPool = m_binding.createPool(m_device); 123 | m_descSetLayout = m_binding.createLayout(m_device); 124 | m_descSet = m_device.allocateDescriptorSets({m_descPool, 1, &m_descSetLayout})[0]; 125 | 126 | std::vector writes; 127 | 128 | vk::AccelerationStructureNV tlas = m_rtBuilder.getAccelerationStructure(); 129 | vk::WriteDescriptorSetAccelerationStructureNV descAsInfo{1, &tlas}; 130 | vk::DescriptorBufferInfo primitiveInfoDesc{m_rtPrimLookup.buffer, 0, VK_WHOLE_SIZE}; 131 | writes.emplace_back(m_binding.makeWrite(m_descSet, 0, &descAsInfo)); 132 | 133 | std::vector descImgInfo; 134 | for(auto& i : m_raytracingOutput) 135 | { 136 | descImgInfo.push_back(i.descriptor); 137 | } 138 | writes.emplace_back(m_binding.makeWriteArray(m_descSet, 1, descImgInfo.data())); 139 | 140 | writes.emplace_back(m_binding.makeWrite(m_descSet, 2, &primitiveInfoDesc)); 141 | m_device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr); 142 | 143 | updateDescriptorSet(); 144 | } 145 | 146 | void Raytracer::updateDescriptorSet() 147 | { 148 | // (1) Output buffer 149 | { 150 | std::vector descImgInfo; 151 | for(auto& i : m_raytracingOutput) 152 | { 153 | descImgInfo.push_back(i.descriptor); 154 | } 155 | vk::WriteDescriptorSet wds = m_binding.makeWriteArray(m_descSet, 1, descImgInfo.data()); 156 | 157 | //vk::DescriptorImageInfo imageInfo{{}, m_raytracingOutput.descriptor.imageView, vk::ImageLayout::eGeneral}; 158 | //vk::WriteDescriptorSet wds{m_rtDescSet, 1, 0, 1, vkDT::eStorageImage, &imageInfo}; 159 | m_device.updateDescriptorSets(wds, nullptr); 160 | } 161 | } 162 | 163 | void Raytracer::createPipeline(const vk::DescriptorSetLayout& sceneDescSetLayout) 164 | { 165 | vk::ShaderModule raygenSM = 166 | nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rgen.spv", true, defaultSearchPaths)); 167 | vk::ShaderModule missSM = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rmiss.spv", true, defaultSearchPaths)); 168 | vk::ShaderModule shadowmissSM = 169 | nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytraceShadow.rmiss.spv", true, defaultSearchPaths)); 170 | vk::ShaderModule chitSM = nvvk::createShaderModule(m_device, nvh::loadFile("spv/raytrace.rchit.spv", true, defaultSearchPaths)); 171 | 172 | std::vector stages; 173 | 174 | // Raygen 175 | vk::RayTracingShaderGroupCreateInfoNV rg{vk::RayTracingShaderGroupTypeNV::eGeneral, VK_SHADER_UNUSED_NV, 176 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 177 | stages.push_back({{}, vk::ShaderStageFlagBits::eRaygenNV, raygenSM, "main"}); 178 | rg.setGeneralShader(static_cast(stages.size() - 1)); 179 | m_groups.push_back(rg); 180 | // Miss 181 | vk::RayTracingShaderGroupCreateInfoNV mg{vk::RayTracingShaderGroupTypeNV::eGeneral, VK_SHADER_UNUSED_NV, 182 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 183 | stages.push_back({{}, vk::ShaderStageFlagBits::eMissNV, missSM, "main"}); 184 | mg.setGeneralShader(static_cast(stages.size() - 1)); 185 | m_groups.push_back(mg); 186 | // Shadow Miss 187 | stages.push_back({{}, vk::ShaderStageFlagBits::eMissNV, shadowmissSM, "main"}); 188 | mg.setGeneralShader(static_cast(stages.size() - 1)); 189 | m_groups.push_back(mg); 190 | // Hit Group - Closest Hit + AnyHit 191 | vk::RayTracingShaderGroupCreateInfoNV hg{vk::RayTracingShaderGroupTypeNV::eTrianglesHitGroup, VK_SHADER_UNUSED_NV, 192 | VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV, VK_SHADER_UNUSED_NV}; 193 | stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitNV, chitSM, "main"}); 194 | hg.setClosestHitShader(static_cast(stages.size() - 1)); 195 | m_groups.push_back(hg); 196 | 197 | // Push constant: ray depth, ... 198 | vk::PushConstantRange pushConstant{vk::ShaderStageFlagBits::eRaygenNV | vk::ShaderStageFlagBits::eClosestHitNV 199 | | vk::ShaderStageFlagBits::eMissNV, 200 | 0, sizeof(PushConstant)}; 201 | 202 | // All 3 descriptors 203 | std::vector allLayouts = {m_descSetLayout, sceneDescSetLayout}; 204 | vk::PipelineLayoutCreateInfo pipelineLayoutCreateInfo; 205 | pipelineLayoutCreateInfo.setSetLayoutCount(static_cast(allLayouts.size())); 206 | pipelineLayoutCreateInfo.setPSetLayouts(allLayouts.data()); 207 | pipelineLayoutCreateInfo.setPushConstantRangeCount(1); 208 | pipelineLayoutCreateInfo.setPPushConstantRanges(&pushConstant); 209 | m_pipelineLayout = m_device.createPipelineLayout(pipelineLayoutCreateInfo); 210 | m_debug.setObjectName(m_pipelineLayout, "raytracer"); 211 | 212 | // Assemble the shader stages and recursion depth info into the raytracing pipeline 213 | vk::RayTracingPipelineCreateInfoNV rayPipelineInfo; 214 | rayPipelineInfo.setStageCount(static_cast(stages.size())); 215 | rayPipelineInfo.setPStages(stages.data()); 216 | rayPipelineInfo.setGroupCount(static_cast(m_groups.size())); 217 | rayPipelineInfo.setPGroups(m_groups.data()); 218 | rayPipelineInfo.setMaxRecursionDepth(10); 219 | rayPipelineInfo.setLayout(m_pipelineLayout); 220 | m_pipeline = m_device.createRayTracingPipelineNV({}, rayPipelineInfo).value; 221 | 222 | m_device.destroyShaderModule(raygenSM); 223 | m_device.destroyShaderModule(missSM); 224 | m_device.destroyShaderModule(shadowmissSM); 225 | m_device.destroyShaderModule(chitSM); 226 | } 227 | 228 | //-------------------------------------------------------------------------------------------------- 229 | // 230 | // 231 | void Raytracer::createShadingBindingTable() 232 | { 233 | auto groupCount = static_cast(m_groups.size()); // 3 shaders: raygen, miss, chit 234 | uint32_t groupHandleSize = m_rtProperties.shaderGroupHandleSize; // Size of a program identifier 235 | uint32_t baseAlignment = m_rtProperties.shaderGroupBaseAlignment; // Size of a program identifier 236 | 237 | 238 | // Fetch all the shader handles used in the pipeline, so that they can be written in the SBT 239 | uint32_t sbtSize = groupCount * baseAlignment; 240 | std::vector shaderHandleStorage(sbtSize); 241 | auto result = m_device.getRayTracingShaderGroupHandlesNV(m_pipeline, 0, groupCount, sbtSize, shaderHandleStorage.data()); 242 | assert(result == vk::Result::eSuccess); 243 | 244 | m_sbtBuffer = m_alloc->createBuffer(sbtSize, vk::BufferUsageFlagBits::eTransferSrc, 245 | vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent); 246 | m_debug.setObjectName(m_sbtBuffer.buffer, std::string("SBT").c_str()); 247 | 248 | // Write the handles in the SBT 249 | void* mapped = m_alloc->map(m_sbtBuffer); 250 | auto* pData = reinterpret_cast(mapped); 251 | for(uint32_t g = 0; g < groupCount; g++) 252 | { 253 | memcpy(pData, shaderHandleStorage.data() + g * groupHandleSize, groupHandleSize); // raygen 254 | pData += baseAlignment; 255 | } 256 | m_alloc->unmap(m_sbtBuffer); 257 | } 258 | 259 | //-------------------------------------------------------------------------------------------------- 260 | // 261 | // 262 | void Raytracer::run(const vk::CommandBuffer& cmdBuf, const vk::DescriptorSet& sceneDescSet, int frame /*= 0*/) 263 | { 264 | m_pushC.frame = frame; 265 | 266 | uint32_t progSize = m_rtProperties.shaderGroupBaseAlignment; // Size of a program identifier 267 | cmdBuf.bindPipeline(vk::PipelineBindPoint::eRayTracingNV, m_pipeline); 268 | cmdBuf.bindDescriptorSets(vk::PipelineBindPoint::eRayTracingNV, m_pipelineLayout, 0, {m_descSet, sceneDescSet}, {}); 269 | cmdBuf.pushConstants(m_pipelineLayout, 270 | vk::ShaderStageFlagBits::eRaygenNV | vk::ShaderStageFlagBits::eClosestHitNV 271 | | vk::ShaderStageFlagBits::eMissNV, 272 | 0, m_pushC); 273 | 274 | vk::DeviceSize rayGenOffset = 0 * progSize; 275 | vk::DeviceSize missOffset = 1 * progSize; 276 | vk::DeviceSize missStride = progSize; 277 | vk::DeviceSize hitGroupOffset = 3 * progSize; // Jump over the 2 miss 278 | vk::DeviceSize hitGroupStride = progSize; 279 | 280 | cmdBuf.traceRaysNV(m_sbtBuffer.buffer, rayGenOffset, // 281 | m_sbtBuffer.buffer, missOffset, missStride, // 282 | m_sbtBuffer.buffer, hitGroupOffset, hitGroupStride, // 283 | m_sbtBuffer.buffer, 0, 0, // 284 | m_outputSize.width, m_outputSize.height, // 285 | 1 /*, NVVKPP_DISPATCHER*/); 286 | } 287 | 288 | bool Raytracer::uiSetup() 289 | { 290 | bool modified = false; 291 | if(ImGui::CollapsingHeader("Ray Tracing")) 292 | { 293 | modified = false; 294 | modified |= ImGui::SliderFloat("Max Ray Length", &m_pushC.maxRayLenght, 1, 1000000, "%.1f"); 295 | modified |= ImGui::SliderInt("Samples Per Frame", &m_pushC.samples, 1, 100); 296 | modified |= ImGui::SliderInt("Max Iteration ", &m_maxFrames, 1, 1000); 297 | } 298 | return modified; 299 | } 300 | 301 | void Raytracer::setClearColor(glm::vec3& _color) 302 | { 303 | m_pushC.backgroundColor = _color; 304 | } 305 | 306 | void Raytracer::setToonSteps(int nbStep) 307 | { 308 | m_pushC.nbSteps = nbStep; 309 | } 310 | 311 | void Raytracer::setToonLightDir(glm::vec3 lightDir) 312 | { 313 | m_pushC.lightDir = lightDir; 314 | } 315 | -------------------------------------------------------------------------------- /raytracer.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | 21 | #pragma once 22 | 23 | ////////////////////////////////////////////////////////////////////////// 24 | // Raytracing implementation 25 | // 26 | // There are 2 descriptor sets 27 | // (0) - Acceleration structure and result image 28 | // (1) - Various buffers: vertices, indices, matrices, Material and Textures 29 | // 30 | ////////////////////////////////////////////////////////////////////////// 31 | 32 | #include 33 | 34 | #include "vk_util.hpp" 35 | 36 | #include "nvvk/commands_vk.hpp" 37 | #include "nvvk/descriptorsets_vk.hpp" 38 | #include "nvvk/raytraceNV_vk.hpp" 39 | 40 | // Structure used for retrieving the primitive information in the closest hit 41 | // The gl_InstanceCustomIndexNV 42 | struct RtPrimitiveLookup 43 | { 44 | uint32_t indexOffset; 45 | uint32_t vertexOffset; 46 | int materialIndex; 47 | }; 48 | 49 | class Raytracer 50 | { 51 | public: 52 | Raytracer(); 53 | 54 | // Initializing the allocator and querying the raytracing properties 55 | void setup(const vk::Device& device, const vk::PhysicalDevice& physicalDevice, uint32_t queueIndex, nvvkpp::ResourceAllocator* allocator); 56 | 57 | bool isValid() { return m_bValid; } 58 | 59 | // Return the rendered image 60 | const std::vector& outputImages() const; 61 | int maxFrames() const; 62 | 63 | void destroy(); 64 | 65 | // Creating the two images where the result is stored 66 | void createOutputImages(vk::Extent2D size); 67 | 68 | // Create a descriptor set holding the acceleration structure and the output image 69 | void createDescriptorSet(); 70 | 71 | // Will be called when resizing the window 72 | void updateDescriptorSet(); 73 | 74 | // Pipeline with all shaders, including the 3 descriptor layouts. 75 | void createPipeline(const vk::DescriptorSetLayout& sceneDescSetLayout); 76 | 77 | // The SBT, storing in a buffer the calling handles of each shader group 78 | void createShadingBindingTable(); 79 | 80 | // Executing the raytracing 81 | void run(const vk::CommandBuffer& cmdBuf, const vk::DescriptorSet& sceneDescSet, int frame = 0); 82 | 83 | // To control the raytracer 84 | bool uiSetup(); 85 | 86 | nvvkpp::RaytracingBuilderNV& builder() { return m_rtBuilder; } 87 | 88 | void setPrimitiveLookup(const std::vector& primitiveLookup) 89 | { 90 | nvvkpp::ScopeCommandBuffer cmdBuf(m_device, m_queueIndex); 91 | m_rtPrimLookup = m_alloc->createBuffer(cmdBuf, primitiveLookup, vk::BufferUsageFlagBits::eStorageBuffer); 92 | m_debug.setObjectName(m_rtPrimLookup.buffer, "PrimitiveInfo"); 93 | } 94 | 95 | void setClearColor(glm::vec3& _color); 96 | void setToonSteps(int nbStep); 97 | void setToonLightDir(glm::vec3 lightDir); 98 | 99 | private: 100 | struct PushConstant 101 | { 102 | glm::vec3 backgroundColor{1, 1, 1}; 103 | int frame{0}; // Current frame number 104 | glm::vec3 lightDir{-1, -1, -1}; 105 | float maxRayLenght{100000}; 106 | int samples{1}; // samples per frame 107 | int nbSteps{3}; // Dither 108 | } m_pushC; 109 | 110 | int m_maxFrames{50}; // Max iterations 111 | 112 | vk::PhysicalDeviceRayTracingPropertiesNV m_rtProperties; 113 | std::vector m_raytracingOutput; // many outputs 114 | 115 | // Raytracer 116 | nvvk::Buffer m_sbtBuffer; 117 | nvvkpp::RaytracingBuilderNV m_rtBuilder; 118 | nvvkpp::DescriptorSetBindings m_descSetLayoutBind; 119 | vk::DescriptorPool m_descPool; 120 | vk::DescriptorSetLayout m_descSetLayout; 121 | vk::DescriptorSet m_descSet; 122 | vk::PipelineLayout m_pipelineLayout; 123 | vk::Pipeline m_pipeline; 124 | vk::Extent2D m_outputSize; 125 | nvvkpp::DescriptorSetBindings m_binding; 126 | nvvk::Buffer m_rtPrimLookup; 127 | std::vector m_groups; 128 | 129 | 130 | // Vulkan 131 | bool m_bValid{false}; 132 | vk::Device m_device; 133 | nvvk::DebugUtil m_debug; 134 | uint32_t m_queueIndex; 135 | nvvkpp::ResourceAllocator* m_alloc{nullptr}; 136 | }; 137 | -------------------------------------------------------------------------------- /shaders/binding.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 | #define B_SCENE 0 21 | #define B_MATRIX 1 22 | #define B_VERTICES 2 23 | #define B_INDICES 3 24 | #define B_NORMALS 4 25 | #define B_TEXCOORDS 5 26 | #define B_TANGENTS 6 27 | #define B_MATERIAL 8 28 | #define B_TEXTURES 9 29 | 30 | #define B_HDR 10 31 | #define B_FILTER_DIFFUSE 11 32 | #define B_LUT_BRDF 12 33 | #define B_FILTER_GLOSSY 13 34 | #define B_IMPORT_SMPL 14 35 | -------------------------------------------------------------------------------- /shaders/compositing.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | //layout(set = 0, binding = 0) uniform sampler2D inTxt; 22 | 23 | layout(set = 0, binding = 0) uniform sampler2D iChannel0; // Ray tracer out 24 | layout(set = 0, binding = 1) uniform sampler2D iChannel1; // Normal & depth Contour 25 | layout(set = 0, binding = 2) uniform sampler2D iChannel2; // Object Contour 26 | 27 | layout(push_constant) uniform params_ 28 | { 29 | vec3 backgroundColor; 30 | int setBackground; 31 | vec3 lineColor; 32 | }; 33 | 34 | layout(location = 0) in vec2 fragCoord; 35 | layout(location = 0) out vec4 fragColor; 36 | 37 | void main() 38 | { 39 | vec4 color = texture(iChannel0, fragCoord.st); 40 | 41 | // White backgound 42 | if(setBackground > 0) 43 | color.xyz = mix(color.xyz, backgroundColor, 1.0 - texture(iChannel0, fragCoord.st).a); 44 | // 45 | vec4 ct1 = texture(iChannel1, fragCoord.st); 46 | vec4 ct2 = texture(iChannel2, fragCoord.st); 47 | 48 | color.xyz = mix(color.xyz, lineColor, ct1.r); 49 | color.xyz = mix(color.xyz, lineColor, ct2.r); // outline contour over inline 50 | 51 | fragColor = color; 52 | } 53 | -------------------------------------------------------------------------------- /shaders/contour_normaldepth.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | 22 | // https://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection 23 | // https://aras-p.info/texts/CompactNormalStorage.html 24 | vec2 encode(vec3 n) 25 | { 26 | float p = sqrt(n.z * 8 + 8); 27 | return vec2(n.xy / p + 0.5); 28 | } 29 | 30 | vec3 decode(vec2 enc) 31 | { 32 | vec2 fenc = enc * 4 - 2; 33 | float f = dot(fenc, fenc); 34 | float g = sqrt(1 - f / 4); 35 | vec3 n; 36 | n.xy = fenc * g; 37 | n.z = 1 - f / 2; 38 | return n; 39 | } 40 | 41 | 42 | layout(set = 0, binding = 0) uniform sampler2D iChannel0; // Normal + depth 43 | 44 | layout(set = 0, binding = 1) buffer zValues 45 | { 46 | int minmax[2]; // zNear and zFar, from the compute shader deapthminmax.comp 47 | }; 48 | 49 | layout(push_constant) uniform params_ 50 | { 51 | float NormalDiffCoeff; 52 | float DepthDiffCoeff; 53 | }; 54 | 55 | 56 | layout(location = 0) in vec2 fragCoord; 57 | layout(location = 0) out float fragColor; 58 | 59 | 60 | float Fdepth(in float Z, in float zNear, in float zFar) 61 | { 62 | return abs((1. / Z - 1. / zNear) / ((1. / zFar) - (1. / zNear))); 63 | } 64 | 65 | float FNdepth(in float Z, in float zNear, in float zFar) 66 | { 67 | return (Z - zNear) / (zFar - zNear); 68 | } 69 | 70 | float Gradient(ivec2 texelCoord, float zNear, float zFar) 71 | { 72 | vec4 A = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, +1.0)); // +---+---+---+ 73 | vec4 B = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, +1.0)); // | A | B | C | 74 | vec4 C = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, +1.0)); // +---+---+---+ 75 | vec4 D = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, +0.0)); // | D | X | E | 76 | vec4 X = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, +0.0)); // +---+---+---+ 77 | vec4 E = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, +0.0)); // | F | G | H | 78 | vec4 F = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, -1.0)); // +---+---+---+ 79 | vec4 G = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, -1.0)); 80 | vec4 H = texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, -1.0)); 81 | 82 | // Don't sample background 83 | int objId = floatBitsToInt(X.w); 84 | if(X.z < 0.0001 || objId == 0) 85 | return 0; 86 | 87 | vec3 An = decode(A.xy); 88 | vec3 Bn = decode(B.xy); 89 | vec3 Cn = decode(C.xy); 90 | vec3 Dn = decode(D.xy); 91 | vec3 Xn = decode(X.xy); 92 | vec3 En = decode(E.xy); 93 | vec3 Fn = decode(F.xy); 94 | vec3 Gn = decode(G.xy); 95 | vec3 Hn = decode(H.xy); 96 | 97 | // Normal Gradient 98 | float Ngrad = 0; 99 | { 100 | // compute length of gradient using Sobel/Kroon operator 101 | const float k0 = 17. / 23.75; 102 | const float k1 = 61. / 23.75; 103 | const vec3 grad_y = k0 * An + k1 * Bn + k0 * Cn - k0 * Fn - k1 * Gn - k0 * Hn; 104 | const vec3 grad_x = k0 * Cn + k1 * En + k0 * Hn - k0 * An - k1 * Dn - k0 * Fn; 105 | const float g = length(grad_x) + length(grad_y); 106 | 107 | Ngrad = smoothstep(2.f, 3.f, g * NormalDiffCoeff); //!! magic 108 | } 109 | 110 | // Depth Gradient 111 | float Dgrad = 0; 112 | { 113 | // https://www.cs.princeton.edu/courses/archive/fall00/cs597b/papers/saito90.pdf 114 | A.z = Fdepth(A.z, zNear, zFar); 115 | B.z = Fdepth(B.z, zNear, zFar); 116 | C.z = Fdepth(C.z, zNear, zFar); 117 | D.z = Fdepth(D.z, zNear, zFar); 118 | E.z = Fdepth(E.z, zNear, zFar); 119 | F.z = Fdepth(F.z, zNear, zFar); 120 | G.z = Fdepth(G.z, zNear, zFar); 121 | H.z = Fdepth(H.z, zNear, zFar); 122 | X.z = Fdepth(X.z, zNear, zFar); 123 | 124 | float g = (abs(A.z + 2 * B.z + C.z - F.z - 2 * G.z - H.z) + abs(C.z + 2 * E.z + H.z - A.z - 2 * D.z - F.z)) / 8.0; 125 | float l = (8 * X.z - A.z - B.z - C.z - D.z - E.z - F.z - G.z - H.z) / 3.0; 126 | 127 | Dgrad = (l + g) * DepthDiffCoeff; 128 | Dgrad = smoothstep(0.03f, 0.1f, Dgrad); // !magic values 129 | } 130 | 131 | 132 | return Ngrad + Dgrad; 133 | } 134 | 135 | void main() 136 | { 137 | ivec2 size = textureSize(iChannel0, 0); 138 | ivec2 texelCoord = ivec2(vec2(size) * fragCoord.st); 139 | 140 | float zNear = intBitsToFloat(minmax[0]); 141 | float zFar = intBitsToFloat(minmax[1]); 142 | 143 | fragColor = Gradient(texelCoord, zNear, zFar); 144 | } 145 | -------------------------------------------------------------------------------- /shaders/contour_objects.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | 22 | // clang-format off 23 | layout(set = 0, binding = 0) uniform sampler2D iChannel0; // Object ID in Z 24 | // clang-format on 25 | 26 | layout(push_constant) uniform params_ 27 | { 28 | int contourMethod; 29 | }; 30 | 31 | 32 | layout(location = 0) in vec2 fragCoord; 33 | layout(location = 0) out vec4 fragColor; 34 | 35 | 36 | vec4 objectContour() 37 | { 38 | ivec2 size = textureSize(iChannel0, 0); 39 | ivec2 texelCoord = ivec2(vec2(size) * fragCoord.st); 40 | 41 | int A = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, +1.0)).w); // +---+---+---+ 42 | int B = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, +1.0)).w); // | A | B | C | 43 | int C = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, +1.0)).w); // +---+---+---+ 44 | int D = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, +0.0)).w); // | D | X | E | 45 | int X = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, +0.0)).w); // +---+---+---+ 46 | int E = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, +0.0)).w); // | F | G | H | 47 | int F = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(-1.0, -1.0)).w); // +---+---+---+ 48 | int G = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+0.0, -1.0)).w); 49 | int H = floatBitsToInt(texelFetchOffset(iChannel0, texelCoord, 0, ivec2(+1.0, -1.0)).w); 50 | 51 | 52 | switch(contourMethod) 53 | { 54 | case 0: // smaller 55 | if(X < A || X < B || X < C || X < D || X < E || X < F || X < G || X < H) 56 | { 57 | return vec4(1); 58 | } 59 | break; 60 | case 1: // bigger 61 | if(X > A || X > B || X > C || X > D || X > E || X > F || X > G || X > H) 62 | { 63 | return vec4(1); 64 | } 65 | break; 66 | case 2: // thicker 67 | if(X != A || X != B || X != C || X != D || X != E || X != F || X != G || X != H) 68 | { 69 | return vec4(1); 70 | } 71 | case 3: // different 72 | return vec4((int(X != A) + int(X != C) + int(X != F) + int(X != H)) * (1. / 6.) 73 | + (int(X != B) + int(X != D) + int(X != E) + int(X != G)) * (1. / 3.)); 74 | 75 | break; 76 | } 77 | 78 | return vec4(0); 79 | } 80 | 81 | void main() 82 | { 83 | fragColor = objectContour(); 84 | } 85 | -------------------------------------------------------------------------------- /shaders/depthminmax.comp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | 22 | 23 | layout(local_size_x = 32, local_size_y = 32) in; 24 | layout(binding = 0, rgba8) uniform image2D inImage; 25 | layout(binding = 1) buffer outValue 26 | { 27 | uint minmax[2]; 28 | }; 29 | 30 | // Extracting the zNear and zFar of the image, storing the value in outValue. 31 | 32 | void main() 33 | { 34 | ivec2 size = imageSize(inImage); 35 | if(gl_GlobalInvocationID.x >= size.x || gl_GlobalInvocationID.y >= size.y) 36 | return; 37 | 38 | vec4 fragColor = imageLoad(inImage, ivec2(gl_GlobalInvocationID.xy)); 39 | 40 | if(fragColor.z > 0) 41 | { 42 | atomicMin(minmax[0], floatBitsToInt(fragColor.z)); 43 | atomicMax(minmax[1], floatBitsToInt(fragColor.z)); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /shaders/final.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | layout(location = 0) in vec2 inUV; 22 | layout(location = 0) out vec4 outFragColor; 23 | 24 | layout(set = 0, binding = 0) uniform sampler2D inTxt; 25 | 26 | void main() 27 | { 28 | outFragColor = texture(inTxt, inUV).rgba; 29 | } 30 | -------------------------------------------------------------------------------- /shaders/fxaa.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | 22 | /** 23 | Basic FXAA implementation based on the code on geeks3d.com with the 24 | modification that the texture2DLod stuff was removed since it's 25 | unsupported by WebGL. 26 | 27 | -- 28 | 29 | From: 30 | https://github.com/mitsuhiko/webgl-meincraft 31 | 32 | Copyright (c) 2011 by Armin Ronacher. 33 | 34 | Some rights reserved. 35 | 36 | Redistribution and use in source and binary forms, with or without 37 | modification, are permitted provided that the following conditions are 38 | met: 39 | 40 | * Redistributions of source code must retain the above copyright 41 | notice, this list of conditions and the following disclaimer. 42 | 43 | * Redistributions in binary form must reproduce the above 44 | copyright notice, this list of conditions and the following 45 | disclaimer in the documentation and/or other materials provided 46 | with the distribution. 47 | 48 | * The names of the contributors may not be used to endorse or 49 | promote products derived from this software without specific 50 | prior written permission. 51 | 52 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 53 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 54 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 55 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 56 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 57 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 58 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 59 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 60 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 61 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 62 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 63 | */ 64 | 65 | 66 | // clang-format off 67 | layout(set = 0, binding = 0) uniform sampler2D iChannel0; // Normal + depth 68 | // clang-format on 69 | 70 | layout(location = 0) in vec2 fragCoord; 71 | layout(location = 0) out vec4 fragColor; 72 | 73 | 74 | #ifndef FXAA_REDUCE_MIN 75 | #define FXAA_REDUCE_MIN (1.0 / 128.0) 76 | #endif 77 | #ifndef FXAA_REDUCE_MUL 78 | #define FXAA_REDUCE_MUL (1.0 / 8.0) 79 | #endif 80 | #ifndef FXAA_SPAN_MAX 81 | #define FXAA_SPAN_MAX 8.0 82 | #endif 83 | 84 | // optimized version for mobile, where dependent 85 | // texture reads can be a bottleneck 86 | vec4 fxaa(sampler2D tex, 87 | vec2 fragCoord, 88 | vec2 resolution, // 89 | vec2 v_rgbNW, 90 | vec2 v_rgbNE, 91 | vec2 v_rgbSW, 92 | vec2 v_rgbSE, 93 | vec2 v_rgbM) 94 | { 95 | vec4 color; 96 | vec2 inverseVP = vec2(1.0 / resolution.x, 1.0 / resolution.y); 97 | vec3 rgbNW = texture(tex, v_rgbNW).xyz; 98 | vec3 rgbNE = texture(tex, v_rgbNE).xyz; 99 | vec3 rgbSW = texture(tex, v_rgbSW).xyz; 100 | vec3 rgbSE = texture(tex, v_rgbSE).xyz; 101 | vec4 texColor = texture(tex, v_rgbM); 102 | vec3 rgbM = texColor.xyz; 103 | vec3 luma = vec3(0.299, 0.587, 0.114); 104 | float lumaNW = dot(rgbNW, luma); 105 | float lumaNE = dot(rgbNE, luma); 106 | float lumaSW = dot(rgbSW, luma); 107 | float lumaSE = dot(rgbSE, luma); 108 | float lumaM = dot(rgbM, luma); 109 | float lumaMin = min(lumaM, min(min(lumaNW, lumaNE), min(lumaSW, lumaSE))); 110 | float lumaMax = max(lumaM, max(max(lumaNW, lumaNE), max(lumaSW, lumaSE))); 111 | 112 | vec2 dir; 113 | dir.x = -((lumaNW + lumaNE) - (lumaSW + lumaSE)); 114 | dir.y = ((lumaNW + lumaSW) - (lumaNE + lumaSE)); 115 | 116 | float dirReduce = max((lumaNW + lumaNE + lumaSW + lumaSE) * (0.25 * FXAA_REDUCE_MUL), FXAA_REDUCE_MIN); 117 | 118 | float rcpDirMin = 1.0 / (min(abs(dir.x), abs(dir.y)) + dirReduce); 119 | dir = min(vec2(FXAA_SPAN_MAX, FXAA_SPAN_MAX), max(vec2(-FXAA_SPAN_MAX, -FXAA_SPAN_MAX), dir * rcpDirMin)) * inverseVP; 120 | 121 | vec3 rgbA = 0.5 122 | * (texture(tex, fragCoord * inverseVP + dir * (1.0 / 3.0 - 0.5)).xyz 123 | + texture(tex, fragCoord * inverseVP + dir * (2.0 / 3.0 - 0.5)).xyz); 124 | vec3 rgbB = 125 | rgbA * 0.5 126 | + 0.25 * (texture(tex, fragCoord * inverseVP + dir * -0.5).xyz + texture(tex, fragCoord * inverseVP + dir * 0.5).xyz); 127 | 128 | float lumaB = dot(rgbB, luma); 129 | if((lumaB < lumaMin) || (lumaB > lumaMax)) 130 | color = vec4(rgbA, texColor.a); 131 | else 132 | color = vec4(rgbB, texColor.a); 133 | return color; 134 | } 135 | 136 | void main() 137 | { 138 | vec2 resolution = vec2(textureSize(iChannel0, 0)); 139 | vec2 fragCoord = gl_FragCoord.xy; 140 | 141 | vec2 inverseVP = 1.0 / resolution.xy; 142 | vec2 v_rgbNW = (fragCoord + vec2(-1.0, -1.0)) * inverseVP; 143 | vec2 v_rgbNE = (fragCoord + vec2(1.0, -1.0)) * inverseVP; 144 | vec2 v_rgbSW = (fragCoord + vec2(-1.0, 1.0)) * inverseVP; 145 | vec2 v_rgbSE = (fragCoord + vec2(1.0, 1.0)) * inverseVP; 146 | vec2 v_rgbM = vec2(fragCoord * inverseVP); 147 | 148 | 149 | vec4 result = fxaa(iChannel0, fragCoord, resolution, // 150 | v_rgbNW, v_rgbNE, v_rgbSW, v_rgbSE, v_rgbM); 151 | 152 | fragColor = result; 153 | } 154 | -------------------------------------------------------------------------------- /shaders/gltf.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 | // glTF material representation 22 | 23 | #ifdef __cplusplus 24 | // GLSL Type 25 | using vec4 = glm::vec4; 26 | #endif 27 | 28 | struct GltfShadeMaterial 29 | { 30 | vec4 pbrBaseColorFactor; 31 | int pbrBaseColorTexture; 32 | }; 33 | -------------------------------------------------------------------------------- /shaders/kuwa_aniso.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // by Jan Eric Kyprianidis 21 | // https://github.com/MzHub/gpuakf 22 | 23 | #version 450 24 | 25 | // clang-format off 26 | layout(set = 0, binding = 0) uniform sampler2D src; 27 | layout(set = 0, binding = 1) uniform sampler2D K0; 28 | layout(set = 0, binding = 2) uniform sampler2D tfm; 29 | // clang-format on 30 | 31 | layout(push_constant) uniform params_ 32 | { 33 | float radius; 34 | float q; 35 | float alpha; 36 | }; 37 | 38 | 39 | layout(location = 0) in vec2 fragCoord; 40 | layout(location = 0) out vec4 fragColor; 41 | 42 | 43 | const float PI = 3.14159265358979323846; 44 | const int N = 8; 45 | 46 | void main(void) 47 | { 48 | vec2 src_size = vec2(textureSize(src, 0)); 49 | vec2 uv = gl_FragCoord.xy / src_size; 50 | 51 | vec4 m[8]; 52 | vec3 s[8]; 53 | for(int k = 0; k < N; ++k) 54 | { 55 | m[k] = vec4(0.0); 56 | s[k] = vec3(0.0); 57 | } 58 | 59 | float piN = 2.0 * PI / float(N); 60 | mat2 X = mat2(cos(piN), sin(piN), -sin(piN), cos(piN)); 61 | 62 | vec4 t = texture(tfm, uv); 63 | float a = radius * clamp((alpha + t.w) / alpha, 0.1, 2.0); 64 | float b = radius * clamp(alpha / (alpha + t.w), 0.1, 2.0); 65 | 66 | float cos_phi = cos(t.z); 67 | float sin_phi = sin(t.z); 68 | 69 | mat2 R = mat2(cos_phi, -sin_phi, sin_phi, cos_phi); 70 | mat2 S = mat2(0.5 / a, 0.0, 0.0, 0.5 / b); 71 | mat2 SR = S * R; 72 | 73 | int max_x = int(sqrt(a * a * cos_phi * cos_phi + b * b * sin_phi * sin_phi)); 74 | int max_y = int(sqrt(a * a * sin_phi * sin_phi + b * b * cos_phi * cos_phi)); 75 | 76 | for(int j = -max_y; j <= max_y; ++j) 77 | { 78 | for(int i = -max_x; i <= max_x; ++i) 79 | { 80 | vec2 v = SR * vec2(i, j); 81 | if(dot(v, v) <= 0.25) 82 | { 83 | vec4 c_fix = texture(src, uv + vec2(i, j) / src_size); 84 | vec3 c = c_fix.rgb; 85 | for(int k = 0; k < N; ++k) 86 | { 87 | float w = texture(K0, vec2(0.5, 0.5) + v).x; 88 | 89 | m[k] += vec4(c * w, w); 90 | s[k] += c * c * w; 91 | 92 | v *= X; 93 | } 94 | } 95 | } 96 | } 97 | 98 | vec4 o = vec4(0.0); 99 | for(int k = 0; k < N; ++k) 100 | { 101 | m[k].rgb /= m[k].w; 102 | s[k] = abs(s[k] / m[k].w - m[k].rgb * m[k].rgb); 103 | 104 | float sigma2 = s[k].r + s[k].g + s[k].b; 105 | float w = 1.0 / (1.0 + pow(255.0 * sigma2, 0.5 * q)); 106 | 107 | o += vec4(m[k].rgb * w, w); 108 | } 109 | 110 | float alpha = texture(src, uv).a; 111 | fragColor = vec4(o.rgb / o.w, alpha); 112 | } 113 | -------------------------------------------------------------------------------- /shaders/kuwa_gauss.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // by Jan Eric Kyprianidis 21 | 22 | #version 450 23 | 24 | layout(set = 0, binding = 0) uniform sampler2D src; 25 | //layout(push_constant) uniform params_ 26 | //{ 27 | // float sigma; 28 | //}; 29 | 30 | layout(location = 0) out vec4 dst; 31 | layout(location = 0) in vec2 fragCoord; 32 | 33 | const float sigma = 2.; 34 | 35 | void main(void) 36 | { 37 | vec2 src_size = vec2(textureSize(src, 0)); 38 | vec2 uv = gl_FragCoord.xy / src_size; 39 | float twoSigma2 = 2.0 * sigma * sigma; 40 | int halfWidth = int(ceil(2.0 * sigma)); 41 | 42 | vec3 sum = vec3(0.0); 43 | float norm = 0.0; 44 | if(halfWidth > 0) 45 | { 46 | for(int i = -halfWidth; i <= halfWidth; ++i) 47 | { 48 | for(int j = -halfWidth; j <= halfWidth; ++j) 49 | { 50 | float d = length(vec2(i, j)); 51 | float kernel = exp(-d * d / twoSigma2); 52 | vec3 c = texture(src, uv + vec2(i, j) / src_size).rgb; 53 | sum += kernel * c; 54 | norm += kernel; 55 | } 56 | } 57 | } 58 | else 59 | { 60 | sum = texture(src, uv).rgb; 61 | norm = 1.0; 62 | } 63 | dst = vec4(sum / norm, 0); 64 | } 65 | -------------------------------------------------------------------------------- /shaders/kuwa_sst.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // by Jan Eric Kyprianidis 21 | #version 450 22 | 23 | layout(set = 0, binding = 0) uniform sampler2D src; 24 | layout(location = 0) out vec4 fragColor; 25 | layout(location = 0) in vec2 fragCoord; 26 | 27 | 28 | void main(void) 29 | { 30 | vec2 src_size = vec2(textureSize(src, 0)); 31 | vec2 uv = gl_FragCoord.xy / src_size; 32 | vec2 d = 1.0 / src_size; 33 | 34 | vec3 c = texture(src, uv).xyz; 35 | vec3 u = (-1.0 * texture(src, uv + vec2(-d.x, -d.y)).xyz + -2.0 * texture(src, uv + vec2(-d.x, 0.0)).xyz 36 | + -1.0 * texture(src, uv + vec2(-d.x, d.y)).xyz + +1.0 * texture(src, uv + vec2(d.x, -d.y)).xyz 37 | + +2.0 * texture(src, uv + vec2(d.x, 0.0)).xyz + +1.0 * texture(src, uv + vec2(d.x, d.y)).xyz) 38 | / 4.0; 39 | 40 | vec3 v = (-1.0 * texture(src, uv + vec2(-d.x, -d.y)).xyz + -2.0 * texture(src, uv + vec2(0.0, -d.y)).xyz 41 | + -1.0 * texture(src, uv + vec2(d.x, -d.y)).xyz + +1.0 * texture(src, uv + vec2(-d.x, d.y)).xyz 42 | + +2.0 * texture(src, uv + vec2(0.0, d.y)).xyz + +1.0 * texture(src, uv + vec2(d.x, d.y)).xyz) 43 | / 4.0; 44 | 45 | fragColor = vec4(dot(u, u), dot(v, v), dot(u, v), 1.0); 46 | } 47 | -------------------------------------------------------------------------------- /shaders/kuwa_tfm.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // by Jan Eric Kyprianidis 21 | #version 450 22 | 23 | layout(set = 0, binding = 0) uniform sampler2D src; 24 | layout(location = 0) out vec4 fragColor; 25 | layout(location = 0) in vec2 fragCoord; 26 | 27 | void main(void) 28 | { 29 | vec2 uv = gl_FragCoord.xy / vec2(textureSize(src, 0)); 30 | vec3 g = texture(src, uv).xyz; 31 | 32 | float lambda1 = 0.5 * (g.y + g.x + sqrt(g.y * g.y - 2.0 * g.x * g.y + g.x * g.x + 4.0 * g.z * g.z)); 33 | float lambda2 = 0.5 * (g.y + g.x - sqrt(g.y * g.y - 2.0 * g.x * g.y + g.x * g.x + 4.0 * g.z * g.z)); 34 | 35 | vec2 v = vec2(lambda1 - g.x, -g.z); 36 | vec2 t; 37 | if(length(v) > 0.0) 38 | { 39 | t = normalize(v); 40 | } 41 | else 42 | { 43 | t = vec2(0.0, 1.0); 44 | } 45 | 46 | float phi = atan(t.y, t.x); 47 | 48 | float A = (lambda1 + lambda2 > 0.0) ? (lambda1 - lambda2) / (lambda1 + lambda2) : 0.0; 49 | 50 | fragColor = vec4(t, phi, A); 51 | } 52 | -------------------------------------------------------------------------------- /shaders/kuwahara.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // by Jan Eric Kyprianidis 21 | 22 | #version 450 23 | 24 | // clang-format off 25 | layout(set = 0, binding = 0) uniform sampler2D iChannel0; // Normal + depth 26 | layout(push_constant) uniform params_ {int radius;}; 27 | // clang-format on 28 | 29 | layout(location = 0) in vec2 fragCoord; 30 | layout(location = 0) out vec4 fragColor; 31 | 32 | 33 | void main(void) 34 | { 35 | vec2 src_size = vec2(textureSize(iChannel0, 0)); 36 | vec2 uv = gl_FragCoord.xy / src_size; 37 | float n = float((radius + 1) * (radius + 1)); 38 | 39 | float alpha = texture(iChannel0, uv).a; 40 | 41 | vec3 m[4]; 42 | vec3 s[4]; 43 | for(int k = 0; k < 4; ++k) 44 | { 45 | m[k] = vec3(0.0); 46 | s[k] = vec3(0.0); 47 | } 48 | 49 | for(int j = -radius; j <= 0; ++j) 50 | { 51 | for(int i = -radius; i <= 0; ++i) 52 | { 53 | vec3 c = texture(iChannel0, uv + vec2(i, j) / src_size).rgb; 54 | m[0] += c; 55 | s[0] += c * c; 56 | } 57 | } 58 | 59 | for(int j = -radius; j <= 0; ++j) 60 | { 61 | for(int i = 0; i <= radius; ++i) 62 | { 63 | vec3 c = texture(iChannel0, uv + vec2(i, j) / src_size).rgb; 64 | m[1] += c; 65 | s[1] += c * c; 66 | } 67 | } 68 | 69 | for(int j = 0; j <= radius; ++j) 70 | { 71 | for(int i = 0; i <= radius; ++i) 72 | { 73 | vec3 c = texture(iChannel0, uv + vec2(i, j) / src_size).rgb; 74 | m[2] += c; 75 | s[2] += c * c; 76 | } 77 | } 78 | 79 | for(int j = 0; j <= radius; ++j) 80 | { 81 | for(int i = -radius; i <= 0; ++i) 82 | { 83 | vec3 c = texture(iChannel0, uv + vec2(i, j) / src_size).rgb; 84 | m[3] += c; 85 | s[3] += c * c; 86 | } 87 | } 88 | 89 | 90 | float min_sigma2 = 1e+2; 91 | for(int k = 0; k < 4; ++k) 92 | { 93 | m[k] /= n; 94 | s[k] = abs(s[k] / n - m[k] * m[k]); 95 | 96 | float sigma2 = s[k].r + s[k].g + s[k].b; 97 | if(sigma2 < min_sigma2) 98 | { 99 | min_sigma2 = sigma2; 100 | fragColor = vec4(m[k], alpha); 101 | } 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /shaders/passthrough.vert: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | layout(location = 0) out vec2 outUV; 22 | 23 | 24 | out gl_PerVertex 25 | { 26 | vec4 gl_Position; 27 | }; 28 | 29 | 30 | void main() 31 | { 32 | outUV = vec2((gl_VertexIndex << 1) & 2, gl_VertexIndex & 2); 33 | gl_Position = vec4(outUV * 2.0f - 1.0f, 1.0f, 1.0f); 34 | } 35 | -------------------------------------------------------------------------------- /shaders/pick.rchit: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | 24 | #include "share.glsl" 25 | 26 | // Payload information of the ray returning: 0 hit, 2 shadow 27 | layout(location = 0) rayPayloadInNV PerRayData_pick prd; 28 | 29 | // Raytracing hit attributes: barycentrics 30 | hitAttributeNV vec2 attribs; 31 | 32 | void main() 33 | { 34 | prd.worldPos = vec4(gl_WorldRayOriginNV + gl_WorldRayDirectionNV * gl_HitTNV, 0); 35 | prd.barycentrics = vec4(1.0 - attribs.x - attribs.y, attribs.x, attribs.y, 0); 36 | prd.instanceID = gl_InstanceID; 37 | prd.instanceCustomID = gl_InstanceCustomIndexNV; 38 | prd.primitiveID = gl_PrimitiveID; 39 | } 40 | -------------------------------------------------------------------------------- /shaders/pick.rgen: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | // Default raygen for picking objects in the scene 26 | // - 27 | //------------------------------------------------------------------------------------------------- 28 | 29 | 30 | #include "share.glsl" 31 | 32 | layout(binding = 0, set = 0) uniform accelerationStructureNV topLevelAS; 33 | layout(binding = 1, set = 0) buffer _resultPick 34 | { 35 | PerRayData_pick resultPick; 36 | }; 37 | layout(binding = 2, set = 0) uniform UBOscene 38 | { 39 | Scene s; 40 | } 41 | ubo; 42 | 43 | layout(push_constant) uniform Constants 44 | { 45 | float pickX; 46 | float pickY; 47 | }; 48 | 49 | layout(location = 0) rayPayloadNV PerRayData_pick prd; 50 | 51 | 52 | void main() 53 | { 54 | const vec2 pixelCenter = vec2(pickX, pickY); 55 | const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeNV.xy); 56 | vec2 d = inUV * 2.0 - 1.0; 57 | 58 | mat4 viewInverse = inverse(ubo.s.modelView); 59 | mat4 projInverse = inverse(ubo.s.projection); 60 | vec4 origin = viewInverse * vec4(0, 0, 0, 1); 61 | vec4 target = projInverse * vec4(d.x, d.y, 1, 1); 62 | vec4 direction = viewInverse * vec4(normalize(target.xyz), 0); 63 | uint rayFlags = gl_RayFlagsOpaqueNV; 64 | uint cullMask = 0xff; 65 | float tmin = 0.001; 66 | float tmax = 10000.0; 67 | 68 | prd.worldPos = vec4(0.f); 69 | prd.barycentrics = vec4(0.f); 70 | prd.instanceID = 0; 71 | prd.primitiveID = 0; 72 | 73 | traceNV(topLevelAS, rayFlags, cullMask, 0 /*sbtRecordOffset*/, 0 /*sbtRecordStride*/, 0 /*missIndex*/, origin.xyz, 74 | tmin, direction.xyz, tmax, 0 /*payload*/); 75 | 76 | resultPick.worldPos = prd.worldPos; 77 | resultPick.barycentrics = prd.barycentrics; 78 | resultPick.instanceID = prd.instanceID; 79 | resultPick.primitiveID = prd.primitiveID; 80 | } 81 | -------------------------------------------------------------------------------- /shaders/pick.rmiss: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | // Default miss for picking. Return non-valid object ID 26 | //------------------------------------------------------------------------------------------------- 27 | 28 | #include "share.glsl" 29 | layout(location = 0) rayPayloadInNV PerRayData_pick prd; 30 | 31 | void main() 32 | { 33 | prd.instanceID = ~0; 34 | } 35 | -------------------------------------------------------------------------------- /shaders/rasterizer.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | #extension GL_ARB_separate_shader_objects : enable 22 | #extension GL_GOOGLE_include_directive : enable 23 | #extension GL_EXT_nonuniform_qualifier : enable 24 | #extension GL_EXT_scalar_block_layout : enable 25 | 26 | #include "binding.glsl" 27 | #include "share.glsl" 28 | 29 | // clang-format off 30 | layout(set = 0, binding = B_SCENE) uniform _Scene {Scene sceneInfo; }; 31 | layout(set = 0, binding = B_MATERIAL, scalar) readonly buffer _Material {GltfShadeMaterial materials[];}; 32 | layout(set = 0, binding = B_TEXTURES) uniform sampler2D texturesMap[]; // all textures 33 | // clang-format on 34 | 35 | layout(push_constant) uniform _Push 36 | { 37 | vec3 c_lightDir; 38 | int c_nbSteps; 39 | int c_instID; 40 | int c_matID; 41 | }; 42 | 43 | // Incoming 44 | layout(location = 0) in vec3 in_worldPos; 45 | layout(location = 1) in vec3 in_normal; 46 | layout(location = 2) in vec2 in_texcoord0; 47 | 48 | // Outgoing 49 | layout(location = 0) out vec4 out_color; 50 | layout(location = 1) out vec4 out_bufferEncoded; // xy: normal, z, object ID 51 | 52 | 53 | // Debug utility 54 | vec3 integerToColor(int val) 55 | { 56 | const vec3 freq = vec3(1.33333f, 2.33333f, 3.33333f); 57 | return vec3(sin(freq * val) * .5 + .5); 58 | } 59 | 60 | void main() 61 | { 62 | // Retrieve the material on this hit 63 | GltfShadeMaterial material = materials[c_matID]; 64 | 65 | // Albedo 66 | vec3 baseColor = material.pbrBaseColorFactor.rgb; 67 | if(material.pbrBaseColorTexture > -1) 68 | baseColor *= texture(texturesMap[nonuniformEXT(material.pbrBaseColorTexture)], in_texcoord0).rgb; 69 | 70 | // Color Result 71 | vec3 toLight = normalize(-c_lightDir); 72 | float intensity = toonShading(toLight, in_normal, c_nbSteps); 73 | 74 | out_color.xyz = max(0.1f, intensity) * baseColor.xyz; // keeping ambient 75 | out_color.a = 1; 76 | 77 | // Encoding data buffer 78 | vec2 encodedNormal = encode(normalize(in_normal)); 79 | float depth = length(in_worldPos - sceneInfo.camPos.xyz); 80 | float fInstanceID = intBitsToFloat(c_instID + 1); 81 | out_bufferEncoded = vec4(encodedNormal, depth, fInstanceID); 82 | } 83 | -------------------------------------------------------------------------------- /shaders/rasterizer.vert: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | #extension GL_ARB_separate_shader_objects : enable 22 | #extension GL_GOOGLE_include_directive : enable 23 | #extension GL_EXT_nonuniform_qualifier : enable 24 | #extension GL_EXT_scalar_block_layout : enable 25 | 26 | #include "binding.glsl" 27 | #include "share.glsl" 28 | 29 | // clang-format off 30 | layout(set = 0, binding = B_SCENE) uniform _Scene {Scene sceneInfo; } ; 31 | layout(set = 0, binding = B_MATRIX) readonly buffer _Matrix {InstancesMatrices matrices[]; }; 32 | // clang-format on 33 | 34 | layout(push_constant) uniform _Push 35 | { 36 | vec3 c_lightDir; 37 | int c_nbSteps; 38 | int c_instID; 39 | int c_matID; 40 | }; 41 | 42 | 43 | // Input 44 | layout(location = 0) in vec3 in_pos; 45 | layout(location = 1) in vec3 in_normal; 46 | layout(location = 2) in vec2 in_texcoord0; 47 | 48 | 49 | // Output 50 | layout(location = 0) out vec3 out_worldPos; 51 | layout(location = 1) out vec3 out_normal; 52 | layout(location = 2) out vec2 out_texcoord0; 53 | 54 | 55 | out gl_PerVertex 56 | { 57 | vec4 gl_Position; 58 | }; 59 | 60 | 61 | void main() 62 | { 63 | vec4 worldPos = matrices[c_instID].world * vec4(in_pos, 1.0); 64 | 65 | out_normal = vec3(matrices[c_instID].worldIT * vec4(in_normal, 0.0)); 66 | out_worldPos = worldPos.xyz; 67 | out_texcoord0 = in_texcoord0; 68 | 69 | gl_Position = sceneInfo.projection * sceneInfo.modelView * worldPos; 70 | } 71 | -------------------------------------------------------------------------------- /shaders/raytrace.rchit: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | #extension GL_EXT_nonuniform_qualifier : enable 24 | #extension GL_EXT_scalar_block_layout : enable 25 | 26 | #include "binding.glsl" 27 | #include "share.glsl" 28 | 29 | 30 | layout(location = 0) rayPayloadInNV PerRayData_raytrace prd; // incoming from raygen 31 | layout(location = 1) rayPayloadNV bool payload_isHit; // shadow 32 | 33 | layout(push_constant) uniform _Push 34 | { 35 | vec3 c_backgroundColor; 36 | int c_frame; // Current frame 37 | vec3 c_lightDir; 38 | float c_maxRayLenght; // Trace depth 39 | int c_samples; // Number of samples per pixel 40 | int c_nbSteps; 41 | }; 42 | 43 | 44 | // Raytracing hit attributes: barycentrics 45 | hitAttributeNV vec2 attribs; 46 | 47 | // clang-format off 48 | layout(set = 0, binding = 0) uniform accelerationStructureNV topLevelAS; 49 | layout(set = 0, binding = 2) readonly buffer _InstanceInfo {PrimMeshInfo primInfo[];}; 50 | 51 | layout(set = 1, binding = B_VERTICES) readonly buffer _VertexBuf {float vertices[];}; 52 | layout(set = 1, binding = B_INDICES) readonly buffer _Indices {uint indices[];}; 53 | layout(set = 1, binding = B_NORMALS) readonly buffer _NormalBuf {float normals[];}; 54 | layout(set = 1, binding = B_TEXCOORDS) readonly buffer _TexCoordBuf {float texcoord0[];}; 55 | layout(set = 1, binding = B_MATERIAL, scalar) readonly buffer _MaterialBuffer {GltfShadeMaterial materials[];}; 56 | layout(set = 1, binding = B_TEXTURES) uniform sampler2D texturesMap[]; // all textures 57 | // clang-format on 58 | 59 | 60 | // Return the vertex position 61 | vec3 getVertex(uint index) 62 | { 63 | vec3 vp; 64 | vp.x = vertices[3 * index + 0]; 65 | vp.y = vertices[3 * index + 1]; 66 | vp.z = vertices[3 * index + 2]; 67 | return vp; 68 | } 69 | 70 | vec3 getNormal(uint index) 71 | { 72 | vec3 vp; 73 | vp.x = normals[3 * index + 0]; 74 | vp.y = normals[3 * index + 1]; 75 | vp.z = normals[3 * index + 2]; 76 | return vp; 77 | } 78 | 79 | vec2 getTexCoord(uint index) 80 | { 81 | vec2 vp; 82 | vp.x = texcoord0[2 * index + 0]; 83 | vp.y = texcoord0[2 * index + 1]; 84 | return vp; 85 | } 86 | 87 | // Structure of what a vertex is 88 | struct ShadingState 89 | { 90 | vec3 pos; 91 | vec3 normal; 92 | vec3 geom_normal; 93 | vec2 texcoord0; 94 | uint matIndex; 95 | }; 96 | 97 | //-------------------------------------------------------------- 98 | // Getting the interpolated vertex 99 | // gl_InstanceID gives the Instance Info 100 | // gl_PrimitiveID gives the triangle for this instance 101 | // 102 | ShadingState getShadingState() 103 | { 104 | // Retrieve the Primitive mesh buffer information 105 | PrimMeshInfo pinfo = primInfo[gl_InstanceCustomIndexNV]; 106 | 107 | // Getting the 'first index' for this mesh (offset of the mesh + offset of the triangle) 108 | uint indexOffset = pinfo.indexOffset + (3 * gl_PrimitiveID); 109 | uint vertexOffset = pinfo.vertexOffset; // Vertex offset as defined in glTF 110 | uint matIndex = max(0, pinfo.materialIndex); // material of primitive mesh 111 | 112 | // Getting the 3 indices of the triangle (local) 113 | ivec3 triangleIndex = ivec3(indices[indexOffset + 0], indices[indexOffset + 1], indices[indexOffset + 2]); 114 | triangleIndex += ivec3(vertexOffset); // (global) 115 | 116 | const vec3 barycentric = vec3(1.0 - attribs.x - attribs.y, attribs.x, attribs.y); 117 | 118 | // Position 119 | const vec3 pos0 = getVertex(triangleIndex.x); 120 | const vec3 pos1 = getVertex(triangleIndex.y); 121 | const vec3 pos2 = getVertex(triangleIndex.z); 122 | const vec3 position = pos0 * barycentric.x + pos1 * barycentric.y + pos2 * barycentric.z; 123 | const vec3 world_position = vec3(gl_ObjectToWorldNV * vec4(position, 1.0)); 124 | 125 | // Normal 126 | const vec3 nrm0 = getNormal(triangleIndex.x); 127 | const vec3 nrm1 = getNormal(triangleIndex.y); 128 | const vec3 nrm2 = getNormal(triangleIndex.z); 129 | vec3 normal = normalize(nrm0 * barycentric.x + nrm1 * barycentric.y + nrm2 * barycentric.z); 130 | const vec3 world_normal = normalize(vec3(normal * gl_WorldToObjectNV)); 131 | const vec3 geom_normal = normalize(cross(pos1 - pos0, pos2 - pos0)); 132 | 133 | // Move normal to same side as geometric normal 134 | if(dot(normal, geom_normal) <= 0) 135 | { 136 | normal *= -1.0f; 137 | } 138 | 139 | // Texture coord 140 | const vec2 uv0 = getTexCoord(triangleIndex.x); 141 | const vec2 uv1 = getTexCoord(triangleIndex.y); 142 | const vec2 uv2 = getTexCoord(triangleIndex.z); 143 | const vec2 texcoord0 = uv0 * barycentric.x + uv1 * barycentric.y + uv2 * barycentric.z; 144 | 145 | // Final shading state 146 | ShadingState state; 147 | state.pos = world_position; 148 | state.normal = world_normal; 149 | state.geom_normal = geom_normal; 150 | state.texcoord0 = texcoord0; 151 | state.matIndex = matIndex; 152 | 153 | return state; 154 | } 155 | 156 | 157 | void main() 158 | { 159 | // Get the shading information 160 | ShadingState state = getShadingState(); //ind, vertexOffset, barycentrics); 161 | 162 | // cast a shadow ray; assuming light is always outside 163 | vec3 origin = state.pos; 164 | vec3 toLight = normalize(-c_lightDir); 165 | 166 | payload_isHit = true; // Assuming the ray has hit something 167 | uint rayFlags = gl_RayFlagsTerminateOnFirstHitNV | gl_RayFlagsSkipClosestHitShaderNV; 168 | traceNV(topLevelAS, // acceleration structure 169 | rayFlags, // rayFlags 170 | 0xFF, // cullMask 171 | 0, // sbtRecordOffset 172 | 0, // sbtRecordStride 173 | 1, // missIndex 174 | origin, // ray origin 175 | 0.01, // ray min range 176 | toLight, // ray direction 177 | c_maxRayLenght, // ray max range 178 | 1 // payload layout(location = 1) 179 | ); 180 | 181 | // Retrieve the material on this hit 182 | GltfShadeMaterial material = materials[state.matIndex]; 183 | 184 | // The albedo may be defined from a base texture or a flat color 185 | vec3 baseColor = material.pbrBaseColorFactor.rgb; 186 | if(material.pbrBaseColorTexture > -1) 187 | baseColor *= texture(texturesMap[nonuniformEXT(material.pbrBaseColorTexture)], state.texcoord0).rgb; 188 | 189 | // If isHit is ture, means the ray hit an object, therefore cannot see the light and it is under shadow 190 | float intensity; 191 | if(payload_isHit) 192 | intensity = 0.0f; 193 | else 194 | intensity = toonShading(toLight, state.normal, c_nbSteps); 195 | 196 | // Result color 197 | prd.result.xyz = max(0.1f, intensity) * baseColor.xyz; // keeping ambient 198 | prd.result.a = 1; 199 | 200 | // Result data 201 | prd.normal = state.normal; 202 | prd.depth = gl_HitTNV; 203 | prd.objId = gl_InstanceID + 1; 204 | } 205 | -------------------------------------------------------------------------------- /shaders/raytrace.rgen: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | // Default ray generation 26 | // - Will sample the pixel n-number of time: samples 27 | // - Will jitter the camera in sub-pixel to do anitaliasing 28 | //------------------------------------------------------------------------------------------------- 29 | 30 | 31 | #include "binding.glsl" 32 | #include "share.glsl" 33 | 34 | // clang-format off 35 | layout(set = 0, binding = 0) uniform accelerationStructureNV topLevelAS; // Scene BVH 36 | layout(set = 0, binding = 1, rgba32f) uniform image2D image[]; // In/Out rendering images 0: color, 1: data 37 | 38 | layout(set = 1, binding = B_SCENE) uniform _Scene {Scene sceneInfo;}; 39 | // clang-format on 40 | 41 | //------------------------------------ 42 | // Push constant from the application 43 | //------------------------------------ 44 | layout(push_constant) uniform Constants 45 | { 46 | vec3 c_backgroundColor; 47 | int c_frame; // Current frame 48 | vec3 c_lightDir; 49 | float c_maxRayLenght; // Trace depth 50 | int c_samples; // Number of samples per pixel 51 | int c_nbSteps; 52 | }; 53 | 54 | // Payload 55 | layout(location = 0) rayPayloadNV PerRayData_raytrace prd; 56 | 57 | 58 | //------------------------------- 59 | // Ray generation 60 | //------------------------------- 61 | void main() 62 | { 63 | vec4 hitValue = vec4(0); 64 | vec4 bufferEncoded = vec4(0); // xy: normal, z, object ID, w, depth 65 | 66 | for(int smpl = 0; smpl < c_samples; ++smpl) 67 | { 68 | // Initialize the random number 69 | uint seed = tea(gl_LaunchIDNV.y * gl_LaunchSizeNV.x + gl_LaunchIDNV.x, c_frame * c_samples + smpl); 70 | float r1 = rnd(seed); 71 | float r2 = rnd(seed); 72 | 73 | bool first_frame = c_frame == 0 && smpl == 0; 74 | 75 | // Subpixel jitter: send the ray through a different position inside the pixel each time, to provide antialiasing. 76 | vec2 subpixel_jitter = first_frame ? vec2(0.5f, 0.5f) : vec2(r1, r2); 77 | 78 | const vec2 pixelCenter = vec2(gl_LaunchIDNV.xy) + subpixel_jitter; 79 | 80 | const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeNV.xy); 81 | vec2 d = inUV * 2.0 - 1.0; 82 | 83 | // Initialize ray information 84 | mat4 viewInverse = inverse(sceneInfo.modelView); 85 | mat4 projInverse = inverse(sceneInfo.projection); 86 | vec4 origin = viewInverse * vec4(0, 0, 0, 1); 87 | vec4 target = projInverse * vec4(d.x, d.y, 1, 1); 88 | vec4 direction = viewInverse * vec4(normalize(target.xyz), 0); 89 | float Tmin = 0.001; 90 | float Tmax = 100000.0; 91 | 92 | // Payload 93 | prd.result = vec4(0.f); 94 | prd.importance = vec3(1.f); 95 | prd.seed = seed; 96 | prd.rayDepth = 0; 97 | prd.roughness = 0; 98 | prd.normal = vec3(0.f); 99 | prd.depth = -1; 100 | prd.objId = 0; 101 | 102 | traceNV(topLevelAS, // acceleration structure 103 | gl_RayFlagsNoneNV, // rayFlags 104 | 0xFF, // cullMask 105 | 0, // sbtRecordOffset 106 | 0, // sbtRecordStride 107 | 0, // missIndex 108 | origin.xyz, // ray origin 109 | Tmin, // ray min range 110 | direction.xyz, // ray direction 111 | Tmax, // ray max range 112 | 0 // payload layout(location = 0) 113 | ); 114 | 115 | // Accumulating the result for this sample 116 | hitValue += prd.result; 117 | 118 | vec2 n = encode(prd.normal); 119 | bufferEncoded = vec4(n, prd.depth, intBitsToFloat(prd.objId)); 120 | } 121 | 122 | // Average result 123 | hitValue = hitValue / c_samples; 124 | 125 | // Do accumulation over time 126 | if(c_frame > 0) 127 | { 128 | float a = 1.0f / float(c_frame); 129 | vec3 old_color = imageLoad(image[0], ivec2(gl_LaunchIDNV.xy)).xyz; 130 | imageStore(image[0], ivec2(gl_LaunchIDNV.xy), vec4(mix(old_color, hitValue.xyz, a), hitValue.a)); 131 | } 132 | else 133 | { 134 | // First frame, replace value in the buffer 135 | imageStore(image[0], ivec2(gl_LaunchIDNV.xy), hitValue); 136 | imageStore(image[1], ivec2(gl_LaunchIDNV.xy), bufferEncoded); 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /shaders/raytrace.rmiss: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | #extension GL_GOOGLE_include_directive : enable 23 | 24 | //------------------------------------------------------------------------------------------------- 25 | // Default miss shader for the raytracer 26 | // - Return the background color 27 | //------------------------------------------------------------------------------------------------- 28 | 29 | #include "binding.glsl" 30 | #include "share.glsl" 31 | 32 | layout(location = 0) rayPayloadInNV PerRayData_raytrace payload; 33 | 34 | layout(push_constant) uniform _Push 35 | { 36 | vec3 c_backgroundColor; // Miss color 37 | int c_frame; // Current frame 38 | vec3 c_lightDir; // 39 | float c_maxRayLenght; // Trace depth 40 | int c_samples; // Number of samples per pixel 41 | int c_nbSteps; // Toon shading steps 42 | }; 43 | 44 | void main() 45 | { 46 | payload.result = vec4(c_backgroundColor, 0); 47 | payload.depth = -1; // Will stop rendering 48 | } 49 | -------------------------------------------------------------------------------- /shaders/raytraceShadow.rmiss: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 460 21 | #extension GL_NV_ray_tracing : require 22 | 23 | layout(location = 1) rayPayloadInNV bool payload_isHit; 24 | 25 | //------------------------------------------------------------------------------------------------- 26 | // This will be executed when sending shadow rays and missing all geometries 27 | // - There are no hit shader for the shadow ray, therefore 28 | // - Before calling Trace, set payload_isHit=true 29 | // - The default anyhit, closesthit won't change isShadowed, but if nothing is hit, it will be 30 | // set to false. 31 | //------------------------------------------------------------------------------------------------- 32 | 33 | void main() 34 | { 35 | payload_isHit = false; 36 | } 37 | -------------------------------------------------------------------------------- /shaders/share.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 | #include "gltf.glsl" 21 | 22 | const highp float M_PI = 3.14159265358979323846f; // pi 23 | const highp float M_PI_2 = 1.57079632679489661923f; // pi/2 24 | const highp float M_PI_4 = 0.785398163397448309616; // pi/4 25 | const highp float M_1_PI = 0.318309886183790671538; // 1/pi 26 | const highp float M_2_PI = 0.636619772367581343076; // 2/pi 27 | 28 | //------------------------------------------------------------------------------------------------- 29 | // random number generator based on the Optix SDK 30 | //------------------------------------------------------------------------------------------------- 31 | 32 | uint tea(uint val0, uint val1) 33 | { 34 | uint v0 = val0; 35 | uint v1 = val1; 36 | uint s0 = 0u; 37 | 38 | for(uint n = 0u; n < 16u; n++) 39 | { 40 | s0 += 0x9e3779b9u; 41 | v0 += ((v1 << 4u) + 0xa341316cu) ^ (v1 + s0) ^ ((v1 >> 5u) + 0xc8013ea4u); 42 | v1 += ((v0 << 4u) + 0xad90777du) ^ (v0 + s0) ^ ((v0 >> 5u) + 0x7e95761eu); 43 | } 44 | 45 | return v0; 46 | } 47 | 48 | // Generate random unsigned int in [0, 2^24) 49 | uint lcg(inout uint prev) 50 | { 51 | uint LCG_A = 1664525u; 52 | uint LCG_C = 1013904223u; 53 | prev = (LCG_A * prev + LCG_C); 54 | return prev & 0x00FFFFFF; 55 | } 56 | 57 | uint lcg2(inout uint prev) 58 | { 59 | prev = (prev * 8121 + 28411) % 134456; 60 | return prev; 61 | } 62 | 63 | // Generate random float in [0, 1) 64 | float rnd(inout uint prev) 65 | { 66 | return (float(lcg(prev)) / float(0x01000000)); 67 | } 68 | 69 | 70 | vec2 rnd2(inout uint prev) 71 | { 72 | return vec2(rnd(prev), rnd(prev)); 73 | } 74 | 75 | 76 | //------------------------------------------------------------------------------------------------- 77 | // Avoiding self intersections (see Ray Tracing Gems, Ch. 6) 78 | //------------------------------------------------------------------------------------------------- 79 | 80 | vec3 offsetRay(in vec3 p, in vec3 n) 81 | { 82 | const float intScale = 256.0f; 83 | const float floatScale = 1.0f / 65536.0f; 84 | const float origin = 1.0f / 32.0f; 85 | 86 | ivec3 of_i = ivec3(intScale * n.x, intScale * n.y, intScale * n.z); 87 | 88 | vec3 p_i = vec3(intBitsToFloat(floatBitsToInt(p.x) + ((p.x < 0) ? -of_i.x : of_i.x)), 89 | intBitsToFloat(floatBitsToInt(p.y) + ((p.y < 0) ? -of_i.y : of_i.y)), 90 | intBitsToFloat(floatBitsToInt(p.z) + ((p.z < 0) ? -of_i.z : of_i.z))); 91 | 92 | return vec3(abs(p.x) < origin ? p.x + floatScale * n.x : p_i.x, // 93 | abs(p.y) < origin ? p.y + floatScale * n.y : p_i.y, // 94 | abs(p.z) < origin ? p.z + floatScale * n.z : p_i.z); 95 | } 96 | 97 | 98 | //------------------------------------------------------------------------------------------------- 99 | // Toon Shading : apply quatization on lambertian when nbSteps > 0 100 | //------------------------------------------------------------------------------------------------- 101 | 102 | float toonShading(in vec3 L, // Vector to light 103 | in vec3 N, // Normal 104 | in float nbSteps // Quatization steps 105 | ) 106 | { 107 | float NdotL = dot(N, L); 108 | if(nbSteps > 0.f) 109 | { 110 | const float inv = 1.f / nbSteps; 111 | NdotL = floor(NdotL * (1.f + inv * 0.5f) * nbSteps) * inv; 112 | } 113 | return NdotL; 114 | } 115 | 116 | //------------------------------------------------------------------------------------------------- 117 | // Ray tracer Payloads 118 | //------------------------------------------------------------------------------------------------- 119 | 120 | // Payload for raytracing 121 | struct PerRayData_raytrace 122 | { 123 | vec4 result; 124 | vec3 importance; 125 | float roughness; 126 | uint seed; 127 | int rayDepth; 128 | vec3 normal; 129 | float depth; 130 | int objId; 131 | }; 132 | 133 | // Payload for ray picking 134 | struct PerRayData_pick 135 | { 136 | vec4 worldPos; 137 | vec4 barycentrics; 138 | uint instanceID; 139 | uint instanceCustomID; 140 | uint primitiveID; 141 | }; 142 | 143 | 144 | //------------------------------------------------------------------------------------------------- 145 | // Structures in buffers 146 | //------------------------------------------------------------------------------------------------- 147 | 148 | // Primitive Mesh information, use for finding geometry data 149 | struct PrimMeshInfo 150 | { 151 | uint indexOffset; 152 | uint vertexOffset; 153 | int materialIndex; 154 | }; 155 | 156 | // Matrices buffer for all instances 157 | struct InstancesMatrices 158 | { 159 | mat4 world; 160 | mat4 worldIT; 161 | }; 162 | 163 | // Scene information 164 | struct Scene 165 | { 166 | mat4 projection; 167 | mat4 modelView; 168 | vec4 camPos; 169 | }; 170 | 171 | 172 | //------------------------------------------------------------------------------------------------- 173 | // sRGB to linear approximation 174 | // see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html 175 | //------------------------------------------------------------------------------------------------- 176 | vec4 SRGBtoLINEAR(vec4 srgbIn, float gamma) 177 | { 178 | return vec4(pow(srgbIn.xyz, vec3(gamma)), srgbIn.w); 179 | } 180 | 181 | //------------------------------------------------------------------------------------------------- 182 | // Normal Encoding: Lambert azimuthal equal-area projection 183 | // see Method #4 https://aras-p.info/texts/CompactNormalStorage.html 184 | //------------------------------------------------------------------------------------------------- 185 | vec2 encode(vec3 n) 186 | { 187 | float p = sqrt(n.z * 8 + 8); 188 | return vec2(n.xy / p + 0.5); 189 | } 190 | // Normal Decoding 191 | vec3 decode(vec2 enc) 192 | { 193 | vec2 fenc = enc * 4 - 2; 194 | float f = dot(fenc, fenc); 195 | float g = sqrt(1 - f / 4); 196 | vec3 n; 197 | n.xy = fenc * g; 198 | n.z = 1 - f / 2; 199 | return n; 200 | } 201 | -------------------------------------------------------------------------------- /shaders/tonemap.frag: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #version 450 21 | #extension GL_GOOGLE_include_directive : enable 22 | 23 | //---------------------------------------------------------------------------- 24 | // Use for tonemapping the incoming image (full screen quad) 25 | // 26 | 27 | 28 | // Tonemapping functions 29 | #include "tonemapping.glsl" 30 | 31 | layout(location = 0) in vec2 outUV; 32 | layout(location = 0) out vec4 fragColor; 33 | 34 | layout(set = 0, binding = 0) uniform sampler2D inImage; 35 | 36 | layout(push_constant) uniform shaderInformation 37 | { 38 | int tonemapper; // tonemapper to use 39 | float gamma; // Default 2.2 40 | float exposure; // Overal exposure 41 | } 42 | pushc; 43 | 44 | void main() 45 | { 46 | vec2 uv = outUV; 47 | vec4 color = texture(inImage, uv); 48 | 49 | fragColor = vec4(toneMap(color.rgb, pushc.tonemapper, pushc.gamma, pushc.exposure), color.a); 50 | } 51 | -------------------------------------------------------------------------------- /shaders/tonemapping.glsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 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 | #define NO_DEBUG_OUTPUT 0 21 | #define DEBUG_METALLIC 1 22 | #define DEBUG_NORMAL 2 23 | #define DEBUG_BASECOLOR 3 24 | #define DEBUG_OCCLUSION 4 25 | #define DEBUG_EMISSIVE 5 26 | #define DEBUG_UV 6 27 | #define DEBUG_ALPHA 7 28 | #define DEBUG_ROUGHNESS 8 29 | 30 | // Gamma Correction in Computer Graphics 31 | // see https://www.teamten.com/lawrence/graphics/gamma/ 32 | vec3 gammaCorrection(vec3 color, float gamma) 33 | { 34 | return pow(color, vec3(1.0 / gamma)); 35 | } 36 | 37 | // sRGB to linear approximation 38 | // see http://chilliant.blogspot.com/2012/08/srgb-approximations-for-hlsl.html 39 | vec4 SRGBtoLINEAR(vec4 srgbIn, float gamma) 40 | { 41 | return vec4(pow(srgbIn.xyz, vec3(gamma)), srgbIn.w); 42 | } 43 | 44 | // Uncharted 2 tone map 45 | // see: http://filmicworlds.com/blog/filmic-tonemapping-operators/ 46 | vec3 toneMapUncharted2Impl(vec3 color) 47 | { 48 | const float A = 0.15; 49 | const float B = 0.50; 50 | const float C = 0.10; 51 | const float D = 0.20; 52 | const float E = 0.02; 53 | const float F = 0.30; 54 | return ((color * (A * color + C * B) + D * E) / (color * (A * color + B) + D * F)) - E / F; 55 | } 56 | 57 | vec3 toneMapUncharted(vec3 color, float gamma) 58 | { 59 | const float W = 11.2; 60 | const float ExposureBias = 2.0f; 61 | color = toneMapUncharted2Impl(color * ExposureBias); 62 | vec3 whiteScale = 1.0 / toneMapUncharted2Impl(vec3(W)); 63 | return gammaCorrection(color * whiteScale, gamma); 64 | } 65 | 66 | // Hejl Richard tone map 67 | // see: http://filmicworlds.com/blog/filmic-tonemapping-operators/ 68 | vec3 toneMapHejlRichard(vec3 color) 69 | { 70 | color = max(vec3(0.0), color - vec3(0.004)); 71 | return (color * (6.2 * color + .5)) / (color * (6.2 * color + 1.7) + 0.06); 72 | } 73 | 74 | // ACES tone map 75 | // see: https://knarkowicz.wordpress.com/2016/01/06/aces-filmic-tone-mapping-curve/ 76 | vec3 toneMapACES(vec3 color, float gamma) 77 | { 78 | const float A = 2.51; 79 | const float B = 0.03; 80 | const float C = 2.43; 81 | const float D = 0.59; 82 | const float E = 0.14; 83 | return gammaCorrection(clamp((color * (A * color + B)) / (color * (C * color + D) + E), 0.0, 1.0), gamma); 84 | } 85 | 86 | #define TONEMAP_DEFAULT 0 87 | #define TONEMAP_UNCHARTED 1 88 | #define TONEMAP_HEJLRICHARD 2 89 | #define TONEMAP_ACES 3 90 | 91 | vec3 toneMap(vec3 color, int tonemap, float gamma, float exposure) 92 | { 93 | color *= exposure; 94 | 95 | switch(tonemap) 96 | { 97 | case TONEMAP_UNCHARTED: 98 | return toneMapUncharted(color, gamma); 99 | case TONEMAP_HEJLRICHARD: 100 | return toneMapHejlRichard(color); 101 | case TONEMAP_ACES: 102 | return toneMapACES(color, gamma); 103 | default: 104 | return gammaCorrection(color, gamma); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /tiny_gltf.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | // Visual Studio warnings 21 | #ifdef _MSC_VER 22 | #pragma warning(disable : 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen 23 | #endif 24 | 25 | #define TINYGLTF_IMPLEMENTATION 26 | #define STB_IMAGE_IMPLEMENTATION 27 | #define STB_IMAGE_WRITE_IMPLEMENTATION 28 | #include "tiny_gltf.h" 29 | -------------------------------------------------------------------------------- /vk_util.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2019-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) 2019-2021 NVIDIA CORPORATION 17 | * SPDX-License-Identifier: Apache-2.0 18 | */ 19 | 20 | #pragma once 21 | 22 | #include "nvvk/vulkanhppsupport.hpp" 23 | 24 | // Choosing the allocator to use 25 | // #define ALLOC_DMA 26 | #define ALLOC_DEDICATED 27 | // #define ALLOC_VMA 28 | #include 29 | 30 | #if defined(ALLOC_DMA) 31 | #include 32 | typedef nvvkpp::ResourceAllocatorDma Allocator; 33 | #elif defined(ALLOC_VMA) 34 | #include 35 | typedef nvvkpp::ResourceAllocatorVma Allocator; 36 | #else 37 | typedef nvvkpp::ResourceAllocatorDedicated Allocator; 38 | #endif 39 | 40 | using vkDT = vk::DescriptorType; 41 | using vkDS = vk::DescriptorSetLayoutBinding; 42 | using vkSS = vk::ShaderStageFlagBits; 43 | using vkCB = vk::CommandBufferUsageFlagBits; 44 | using vkBU = vk::BufferUsageFlagBits; 45 | using vkIU = vk::ImageUsageFlagBits; 46 | using vkMP = vk::MemoryPropertyFlagBits; 47 | 48 | 49 | // Utility to time the execution of something resetting the timer 50 | // on each elapse call 51 | // Usage: 52 | // { 53 | // MilliTimer timer; 54 | // ... stuff ... 55 | // double time_elapse = timer.elapse(); 56 | // } 57 | #include 58 | 59 | struct MilliTimer 60 | { 61 | MilliTimer() { reset(); } 62 | void reset() { startTime = std::chrono::high_resolution_clock::now(); } 63 | double elapse() 64 | { 65 | auto now = std::chrono::high_resolution_clock::now(); 66 | auto t = std::chrono::duration_cast(now - startTime).count() / 1000.0; 67 | startTime = now; 68 | return t; 69 | } 70 | std::chrono::high_resolution_clock::time_point startTime; 71 | }; 72 | 73 | inline vk::ClearColorValue makeClearColor(glm::vec3& _c) 74 | { 75 | vk::ClearColorValue cc; 76 | cc.float32[0] = _c.x; 77 | cc.float32[1] = _c.y; 78 | cc.float32[2] = _c.z; 79 | cc.float32[3] = 0; 80 | return cc; 81 | } 82 | --------------------------------------------------------------------------------