diff --git a/CMakeLists.txt b/CMakeLists.txt
index 9e73945..fe42ad0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -2,24 +2,24 @@ cmake_minimum_required(VERSION 3.9.6 FATAL_ERROR)
project(vk_raytracing_tutorial)
#--------------------------------------------------------------------------------------------------
-# look for shared_sources 1) as a sub-folder 2) at some other locations
+# look for nvpro_core 1) as a sub-folder 2) at some other locations
# this cannot be put anywhere else since we still didn't find setup.cmake yet
if(NOT BASE_DIRECTORY)
find_path(BASE_DIRECTORY
- NAMES shared_sources/cmake/setup.cmake
+ NAMES nvpro_core/cmake/setup.cmake
PATHS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/.. ${CMAKE_CURRENT_SOURCE_DIR}/../..
REQUIRED
- DOC "Directory containing shared_sources"
+ DOC "Directory containing nvpro_core"
)
endif()
## Various functions and macros REQUIRED
-if(EXISTS ${BASE_DIRECTORY}/shared_sources/cmake/setup.cmake)
- include(${BASE_DIRECTORY}/shared_sources/cmake/setup.cmake)
- include(${BASE_DIRECTORY}/shared_sources/cmake/utilities.cmake)
+if(EXISTS ${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake)
+ include(${BASE_DIRECTORY}/nvpro_core/cmake/setup.cmake)
+ include(${BASE_DIRECTORY}/nvpro_core/cmake/utilities.cmake)
else()
- message(FATAL_ERROR "could not find base directory, please set BASE_DIRECTORY to folder containing shared_sources")
+ message(FATAL_ERROR "could not find base directory, please set BASE_DIRECTORY to folder containing nvpro_core")
endif()
set(TUTO_KHR_DIR ${CMAKE_CURRENT_SOURCE_DIR})
@@ -29,7 +29,7 @@ set(TUTO_KHR_DIR ${CMAKE_CURRENT_SOURCE_DIR})
# Package shared by all projects
_add_package_VulkanSDK()
_add_package_ImGUI()
-_add_shared_sources_lib()
+_add_nvpro_core_lib()
message(STATUS "COPY ${CMAKE_CURRENT_SOURCE_DIR}/media to ${OUTPUT_PATH}")
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/media DESTINATION ${OUTPUT_PATH})
@@ -53,6 +53,7 @@ add_subdirectory(ray_tracing_rayquery)
add_subdirectory(ray_tracing_reflections)
add_subdirectory(ray_tracing_ao)
add_subdirectory(ray_tracing_indirect_scissor)
+add_subdirectory(ray_tracing_specialization)
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4947287
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,177 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
\ No newline at end of file
diff --git a/LICENSE.md b/LICENSE.md
deleted file mode 100644
index f73c6b4..0000000
--- a/LICENSE.md
+++ /dev/null
@@ -1,31 +0,0 @@
-*Copyright 2016 NVIDIA Corporation*
-
-BY DOWNLOADING THE SOFTWARE AND OTHER AVAILABLE MATERIALS, YOU ("DEVELOPER") AGREE TO BE BOUND BY THE FOLLOWING TERMS AND CONDITIONS
-
-----------
-
-The materials available for download to Developers may include software in both sample source ("*Source Code*") and object code ("*Object Code*") versions, documentation ("*Documentation*"), certain art work ("*Art Assets*") and other materials (collectively, these materials referred to herein as "*Materials*"). Except as expressly indicated herein, all terms and conditions of this Agreement apply to all of the Materials.
-
-Except as expressly set forth herein, NVIDIA owns all of the Materials and makes them available to Developer only under the terms and conditions set forth in this Agreement.
-
-
-----------
-
-
-**License**: Subject to the terms of this Agreement, NVIDIA hereby grants to Developer a royalty-free, non-exclusive license to possess and to use the Materials. The following terms apply to the specified type of Material:
-
-**Source Code**: Developer shall have the right to modify and create derivative works with the Source Code. Developer shall own any derivative works ("*Derivatives*") it creates to the Source Code, provided that Developer uses the Materials in accordance with the terms of this Agreement. Developer may distribute the Derivatives, provided that all NVIDIA copyright notices and trademarks are used properly and the Derivatives include the following statement: "This software contains source code provided by NVIDIA Corporation."
-
-**Object Code**: Developer agrees not to disassemble, decompile or reverse engineer the Object Code versions of any of the Materials. Developer acknowledges that certain of the Materials provided in Object Code version may contain third party components that may be subject to restrictions, and expressly agrees not to attempt to modify or distribute such Materials without first receiving consent from NVIDIA.
-
-**Art Assets**: Developer shall have the right to modify and create Derivatives of the Art Assets, but may not distribute any of the Art Assets or Derivatives created therefrom without NVIDIA’s prior written consent.
-
-**Government End Users**: If you are acquiring the Software on behalf of any unit or agency of the United States Government, the following provisions apply. The Government agrees the Software and documentation were developed at private expense and are provided with “RESTRICTED RIGHTS”. Use, duplication, or disclosure by the Government is subject to restrictions as set forth in DFARS 227.7202-1(a) and 227.7202-3(a) (1995), DFARS 252.227-7013(c)(1)(ii) (Oct 1988), FAR 12.212(a)(1995), FAR 52.227-19, (June 1987) or FAR 52.227-14(ALT III) (June 1987),as amended from time to time. In the event that this License, or any part thereof, is deemed inconsistent with the minimum rights identified in the Restricted Rights provisions, the minimum rights shall prevail.
-No Other License. No rights or licenses are granted by NVIDIA under this License, expressly or by implication, with respect to any proprietary information or patent, copyright, trade secret or other intellectual property right owned or controlled by NVIDIA, except as expressly provided in this License.
-Term: This License is effective until terminated. NVIDIA may terminate this Agreement (and with it, all of Developer’s right to the Materials) immediately upon written notice (which may include email) to Developer, with or without cause.
-
-**Support**: NVIDIA has no obligation to support or to continue providing or updating any of the Materials.
-
-**No Warranty**: THE SOFTWARE AND ANY OTHER MATERIALS PROVIDED BY NVIDIA TO DEVELOPER HEREUNDER ARE PROVIDED "AS IS." NVIDIA DISCLAIMS ALL WARRANTIES, EXPRESS, IMPLIED OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-
-**LIMITATION OF LIABILITY**: NVIDIA SHALL NOT BE LIABLE TO DEVELOPER, DEVELOPER’S CUSTOMERS, OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR UNDER DEVELOPER FOR ANY LOSS OF PROFITS, INCOME, SAVINGS, OR ANY OTHER CONSEQUENTIAL, INCIDENTAL, SPECIAL, PUNITIVE, DIRECT OR INDIRECT DAMAGES (WHETHER IN AN ACTION IN CONTRACT, TORT OR BASED ON A WARRANTY), EVEN IF NVIDIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. THESE LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF THE ESSENTIAL PURPOSE OF ANY LIMITED REMEDY. IN NO EVENT SHALL NVIDIA’S AGGREGATE LIABILITY TO DEVELOPER OR ANY OTHER PERSON OR ENTITY CLAIMING THROUGH OR UNDER DEVELOPER EXCEED THE AMOUNT OF MONEY ACTUALLY PAID BY DEVELOPER TO NVIDIA FOR THE SOFTWARE OR ANY OTHER MATERIALS.
diff --git a/README.md b/README.md
index 93587e9..25a20f2 100644
--- a/README.md
+++ b/README.md
@@ -43,4 +43,5 @@ Tutorial | Details
 | [glTF Scene](ray_tracing_gltf) Instead of loading separate OBJ objects, the example was modified to load glTF scene files containing multiple objects. This example is not about shading, but using more complex data than OBJ. However, it also shows a basic path tracer implementation.
 | [Advance](ray_tracing__advance) An example combining most of the above samples in a single application.
 | [Trace Rays Indirect](ray_tracing_indirect_scissor) Teaches the use of `vkCmdTraceRaysIndirectKHR`, which sources width/height/depth from a buffer. As a use case, we add lanterns to the scene and use a compute shader to calculate scissor rectangles for each of them.
- | [AO Raytracing](ray_tracing_ao) This extension to the tutorial is showing how G-Buffers from the fragment shader, can be used in a compute shader to cast ambient occlusion rays using ray queries ([GLSL_EXT_ray_query](https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GLSL_EXT_ray_query.txt)).
\ No newline at end of file
+ | [AO Raytracing](ray_tracing_ao) This extension to the tutorial is showing how G-Buffers from the fragment shader, can be used in a compute shader to cast ambient occlusion rays using ray queries ([GLSL_EXT_ray_query](https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GLSL_EXT_ray_query.txt)).
+ | [Specialization Constants](ray_tracing_specialization) Showing how to use specialization constant and using interactively different specialization.
\ No newline at end of file
diff --git a/common/obj_loader.cpp b/common/obj_loader.cpp
index 31fa34e..56b8a4d 100644
--- a/common/obj_loader.cpp
+++ b/common/obj_loader.cpp
@@ -1,6 +1,21 @@
-/******************************************************************************
- * Copyright 1998-2018 NVIDIA Corp. All Rights Reserved.
- *****************************************************************************/
+/*
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
// This file exist only to do the implementation of tiny obj loader
#define TINYOBJLOADER_IMPLEMENTATION
diff --git a/common/obj_loader.h b/common/obj_loader.h
index 97493c1..15031ee 100644
--- a/common/obj_loader.h
+++ b/common/obj_loader.h
@@ -1,10 +1,25 @@
-/******************************************************************************
- * Copyright 1998-2018 NVIDIA Corp. All Rights Reserved.
- *****************************************************************************/
+/*
+ * Copyright (c) 2021, NVIDIA CORPORATION. All rights reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ * SPDX-FileCopyrightText: Copyright (c) 2014-2021 NVIDIA CORPORATION
+ * SPDX-License-Identifier: Apache-2.0
+ */
#pragma once
-#include "fileformats/tiny_obj_loader.h"
#include "nvmath/nvmath.h"
+#include "tiny_obj_loader.h"
#include
#include
#include
diff --git a/docs/NV_to_KHR.md.html b/docs/NV_to_KHR.md.html
index bc763cb..e138c66 100644
--- a/docs/NV_to_KHR.md.html
+++ b/docs/NV_to_KHR.md.html
@@ -70,7 +70,7 @@ of the above structures.
//--------------------------------------------------------------------------------------------------
// Converting a OBJ primitive to the ray tracing geometry used for the BLAS
//
-nvvkpp::RaytracingBuilderKHR::Blas HelloVulkan::objectToVkGeometryKHR(const ObjModel& model)
+auto HelloVulkan::objectToVkGeometryKHR(const ObjModel& model)
{
// Setting up the creation info of acceleration structure
vk::AccelerationStructureCreateGeometryTypeInfoKHR asCreate;
diff --git a/docs/index.html b/docs/index.html
deleted file mode 100644
index 5cfd81c..0000000
--- a/docs/index.html
+++ /dev/null
@@ -1,12 +0,0 @@
-
-(insert vkrt_tutorial.md.html here)
-
-
-
-
-
-
-
-
diff --git a/docs/setup.md b/docs/setup.md
index 5a4136f..ae9ccae 100644
--- a/docs/setup.md
+++ b/docs/setup.md
@@ -6,14 +6,12 @@
Besides the current repository, you will also need to clone or download the following repositories:
-* [shared_sources](https://github.com/nvpro-samples/shared_sources): The primary framework that all samples depend on.
-* [shared_external](https://github.com/nvpro-samples/shared_external): Third party libraries that are provided pre-compiled, mostly for Windows x64 / MSVC.
+* [nvpro_core](https://github.com/nvpro-samples/nvpro_core): The primary framework that all samples depend on.
Cloning all repositories
~~~~~
-git clone https://github.com/nvpro-samples/shared_sources.git
-git clone https://github.com/nvpro-samples/shared_external.git
+git clone --recursive --shallow-submodules https://github.com/nvpro-samples/nvpro_core.git
git clone https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR.git
~~~~~
@@ -22,8 +20,7 @@ The directory structure should be looking like this:
~~~~
C:\Vulkan\nvpro-samples
|
- +---shared_external
- +---shared_sources
+ +---nvpro_core
+---vk_raytracing_tutorial_KHR
| +---ray_tracing__simple
| +---ray_tracing_...
@@ -42,7 +39,7 @@ Version 1.2.162.0 and up has ray tracing extensions support.
## Driver
-NVIDIA driver 160.0 and up support Vulkan ray tracing.
+NVIDIA driver 450.0 and up support Vulkan ray tracing.
* Standard driver: https://www.nvidia.com/Download/index.aspx
* Vulkan beta driver: https://developer.nvidia.com/vulkan-driver
@@ -50,9 +47,11 @@ NVIDIA driver 160.0 and up support Vulkan ray tracing.
## CMake
-The CMakefile will use other makefiles from `shared_sources` and look for Vulkan environment variables for the installation of the SDK. Therefore, it is important to have all the above installed before running Cmake in the
+The CMakefile will use other makefiles from `nvpro_core` and look for Vulkan environment variables for the installation of the SDK. Therefore, it is important to have all the above installed before running Cmake in the
`vk_raytracing_tutorial_KHR` directory.
+**Note:** Ray tracing only works with 64 bit environment. Therefore, make sure to choose the right build environment.
+
**Note:** If you are using your own Vulkan header files, it is possible to overide the default search path.
Modify `VULKAN > VULKAN_HEADERS_OVERRIDE_INCLUDE_DIR` to the path to beta vulkan headers.
diff --git a/docs/setup.md.html b/docs/setup.md.html
index 5081c25..2edb65f 100644
--- a/docs/setup.md.html
+++ b/docs/setup.md.html
@@ -22,17 +22,14 @@ All following instructions are based on the modification of this project.
Besides the current repository, you will also need to clone or download the following repositories:
-* [shared_sources](https://github.com/nvpro-samples/shared_sources): The primary framework that all samples depend on.
-* [shared_external](https://github.com/nvpro-samples/shared_external): Third party libraries that are provided pre-compiled, mostly for Windows x64 / MSVC.
+* [nvpro_core](https://github.com/nvpro-samples/nvpro_core): The primary framework that all samples depend on.
The directory structure should be looking like:
**********************************************************
* \
* |
-* +-- 📂 shared_external
-* |
-* +-- 📂 shared_sources
+* +-- 📂 nvpro_core
* |
* +-- 📂 vk_raytracing_tutorial_KHR
* | |
@@ -47,11 +44,13 @@ The directory structure should be looking like:
!!! Warning
- **Run CMake** in vk_raytracing_tutorial_KHR.
+ **Run CMake** in vk_raytracing_tutorial_KHR.
-!!! Warning Beta
+!!! Warning Beta Vulkan SDK
Modify `VULKAN > VULKAN_HEADERS_OVERRIDE_INCLUDE_DIR` to the path to beta vulkan headers.
+!!! Error
+ **32 bit is not supported**
diff --git a/docs/vkrt_tuto_animation.md.html b/docs/vkrt_tuto_animation.md.html
deleted file mode 100644
index 57abba9..0000000
--- a/docs/vkrt_tuto_animation.md.html
+++ /dev/null
@@ -1,614 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Animation**
-
-Authors: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/), Neil Bickford
-
-
-# Animation
-
-
-
-This is an extension of the [Vulkan ray tracing tutorial](vkrt_tutorial.md.html).
-
-We will discuss two animation methods: animating only the transformation matrices, and animating the geometry itself.
-
-(insert setup.md.html here)
-
-# Animating the Matrices
-This first example shows how we can update the matrices used for instances in the TLAS.
-
-## Creating a Scene
-
-In main.cpp we can create a new scene with a ground plane and 21 instances of the Wuson model, by replacing the
-`helloVk.loadModel` calls in `main()`. The code below creates all of the instances
-at the same position, but we will displace them later in the animation function. If you run the example,
-you will find that the rendering is considerably slow, because the geometries are exactly at the same position
-and the acceleration structure does not deal with this well.
-
-~~~~ C++
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths),
- nvmath::scale_mat4(nvmath::vec3f(2.f, 1.f, 2.f)));
- helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths));
- HelloVulkan::ObjInstance inst = helloVk.m_objInstance.back();
- for(int i = 0; i < 20; i++)
- helloVk.m_objInstance.push_back(inst);
-~~~~
-
-## Animation Function
-We want to have all of the Wuson models running in a circle, and we will first modify the rasterizer to handle this.
-Animating the transformation matrices will be done entirely on the CPU, and we will copy the computed transformation to the GPU.
-In the next example, the animation will be done on the GPU using a compute shader.
-
-Add the declaration of the animation to the `HelloVulkan` class.
-~~~~ C++
-void animationInstances(float time);
-~~~~
-
-The first part computes the transformations for all of the Wuson models, placing each one behind another.
-~~~~ C++
-void HelloVulkan::animationInstances(float time)
-{
- const int32_t nbWuson = static_cast(m_objInstance.size() - 1);
- const float deltaAngle = 6.28318530718f / static_cast(nbWuson);
- const float wusonLength = 3.f;
- const float radius = wusonLength / (2.f * sin(deltaAngle / 2.0f));
- const float offset = time * 0.5f;
-
- for(int i = 0; i < nbWuson; i++)
- {
- int wusonIdx = i + 1;
- ObjInstance& inst = m_objInstance[wusonIdx];
- inst.transform = nvmath::rotation_mat4_y(i * deltaAngle + offset)
- * nvmath::translation_mat4(radius, 0.f, 0.f);
- inst.transformIT = nvmath::transpose(nvmath::invert(inst.transform));
- }
-~~~~
-
-Next, we update the buffer that describes the scene, which is used by the rasterizer to set each object's position, and also by the ray tracer to compute shading normals.
-~~~~ C++
- // Update the buffer
- vk::DeviceSize bufferSize = m_objInstance.size() * sizeof(ObjInstance);
- nvvkBuffer stagingBuffer = m_alloc.createBuffer(bufferSize, vk::BufferUsageFlagBits::eTransferSrc,
- vk::MemoryPropertyFlagBits::eHostVisible);
- // Copy data to staging buffer
- auto* gInst = m_alloc.map(stagingBuffer);
- memcpy(gInst, m_objInstance.data(), bufferSize);
- m_alloc.unmap(stagingBuffer);
- // Copy staging buffer to the Scene Description buffer
- nvvk::CommandPool genCmdBuf(m_device, m_graphicsQueueIndex);
- vk::CommandBuffer cmdBuf = genCmdBuf.createCommandBuffer();
- cmdBuf.copyBuffer(stagingBuffer.buffer, m_sceneDesc.buffer, vk::BufferCopy(0, 0, bufferSize));
- m_debug.endLabel(cmdBuf);
- genCmdBuf.submitAndWait(cmdBuf);
- m_alloc.destroy(stagingBuffer);
-
- m_rtBuilder.updateTlasMatrices(m_tlas);
- m_rtBuilder.updateBlas(2);
-}
-~~~~
-
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_animation](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_animation)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_anyhit.md.html b/docs/vkrt_tuto_anyhit.md.html
deleted file mode 100644
index 5d5ad98..0000000
--- a/docs/vkrt_tuto_anyhit.md.html
+++ /dev/null
@@ -1,418 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Anyhit Shaders**
-
-Authors: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/), Neil Bickford
-
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-Like closest hit shaders, any hit shaders operate on intersections between rays and geometry. However, the any hit
-shader will be executed for all hits along the ray. The closest hit shader will then be invoked on the closest accepted
-intersection.
-
-The any hit shader can be useful for discarding intersections, such as for alpha cutouts for example, but can also be
-used for simple transparency. In this example we will show what is needed to do to add this shader type and to create a
-transparency effect.
-
-!!! Note Note
- This example is based on many elements from the [Antialiasing Tutorial](vkrt_tuto_jitter_cam.md.html).
-
-(insert setup.md.html here)
-
-# Any Hit
-
-## `raytrace.rahit`
-
-Create a new shader file `raytrace.rahit` and rerun CMake to have it added to the solution.
-
-This shader starts like `raytrace.chit`, but uses less information.
-~~~~ C++
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_EXT_nonuniform_qualifier : enable
-#extension GL_EXT_scalar_block_layout : enable
-#extension GL_GOOGLE_include_directive : enable
-
-#include "random.glsl"
-#include "raycommon.glsl"
-#include "wavefront.glsl"
-
-// clang-format off
-layout(location = 0) rayPayloadInEXT hitPayload prd;
-
-layout(binding = 2, set = 1, scalar) buffer ScnDesc { sceneDesc i[]; } scnDesc;
-layout(binding = 4, set = 1) buffer MatIndexColorBuffer { int i[]; } matIndex[];
-layout(binding = 5, set = 1, scalar) buffer Vertices { Vertex v[]; } vertices[];
-layout(binding = 6, set = 1) buffer Indices { uint i[]; } indices[];
-layout(binding = 1, set = 1, scalar) buffer MatColorBufferObject { WaveFrontMaterial m[]; } materials[];
-// clang-format on
-~~~~
-
-!!! Note
- You can find the source of `random.glsl` in the Antialiasing Tutorial [here](../ray_tracing_jitter_cam/README.md#toc1.1).
-
-
-For the any hit shader, we need to know which material we hit, and whether that material supports transparency. If it is
-opaque, we simply return, which means that the hit will be accepted.
-
-~~~~ C++
-void main()
-{
- // Object of this instance
- uint objId = scnDesc.i[gl_InstanceCustomIndexEXT].objId;
- // Indices of the triangle
- uint ind = indices[nonuniformEXT(objId)].i[3 * gl_PrimitiveID + 0];
- // Vertex of the triangle
- Vertex v0 = vertices[nonuniformEXT(objId)].v[ind.x];
-
- // Material of the object
- int matIdx = matIndex[nonuniformEXT(objId)].i[gl_PrimitiveID];
- WaveFrontMaterial mat = materials[nonuniformEXT(objId)].m[matIdx];
-
- if (mat.illum != 4)
- return;
-~~~~
-
-Now we will apply transparency:
-
-~~~~ C++
- if (mat.dissolve == 0.0)
- ignoreIntersectionEXT();
- else if(rnd(prd.seed) > mat.dissolve)
- ignoreIntersectionEXT();
-}
-~~~~
-
-As you can see, we are using a random number generator to determine if the ray hits or ignores the object. If we
-accumulate enough rays, the final result will converge to what we want.
-
-## `raycommon.glsl`
-
-The random `seed` also needs to be passed in the ray payload.
-
-In `raycommon.glsl`, add the seed:
-
-~~~~ C++
-struct hitPayload
-{
- vec3 hitValue;
- uint seed;
-};
-~~~~
-
-## Adding Any Hit to `createRtPipeline`
-
-The any hit shader will be part of the hit shader group. Currently, the hit shader group only contains the closest hit shader.
-
-In `createRtPipeline()`, after loading `raytrace.rchit.spv`, load `raytrace.rahit.spv`
-
-~~~~ C++
- vk::ShaderModule ahitSM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/raytrace.rahit.spv", true, paths));
-~~~~
-
-add the any hit shader to the hit group
-
-~~~~ C++
- hg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, chitSM, "main"});
- hg.setAnyHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eAnyHitKHR, ahitSM, "main"});
- m_rtShaderGroups.push_back(hg);
-~~~~
-
-and at the end, delete it:
-
-~~~~ C++
-m_device.destroy(ahitSM);
-~~~~
-
-## Give access of the buffers to the Any Hit shader
-
-In `createDescriptorSetLayout()`, we need to allow the Any Hit shader to access some buffers.
-
-This is the case for the material and scene description buffers
-
-~~~~ C++
- // Materials (binding = 1)
- m_descSetLayoutBind.emplace_back(
- vkDS(1, vkDT::eStorageBuffer, nbObj,
- vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
- // Scene description (binding = 2)
- m_descSetLayoutBind.emplace_back( //
- vkDS(2, vkDT::eStorageBuffer, 1,
- vkSS::eVertex | vkSS::eFragment | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
-~~~~
-
-and also for the vertex, index and material index buffers:
-
-~~~~ C++
- // Materials (binding = 4)
- m_descSetLayoutBind.emplace_back( //
- vkDS(4, vkDT::eStorageBuffer, nbObj,
- vkSS::eFragment | vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
- // Storing vertices (binding = 5)
- m_descSetLayoutBind.emplace_back( //
- vkDS(5, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
- // Storing indices (binding = 6)
- m_descSetLayoutBind.emplace_back( //
- vkDS(6, vkDT::eStorageBuffer, nbObj, vkSS::eClosestHitKHR | vkSS::eAnyHitKHR));
-~~~~
-
-## Opaque Flag
-
-In the example, when creating `VkAccelerationStructureGeometryKHR` objects, we set their flags to `vk::GeometryFlagBitsKHR::eOpaque`. However, this avoided invoking the any hit shader.
-
-We could remove all of the flags, but another issue could happen: the any hit shader could be called multiple times for the same triangle. To have the any hit shader process only one hit per triangle, set the `eNoDuplicateAnyHitInvocation` flag:
-
-~~~~ C++
-geometry.setFlags(vk::GeometryFlagBitsKHR::eNoDuplicateAnyHitInvocation);
-~~~~
-
-## `raytrace.rgen`
-
-If you have done the previous [Jitter Camera/Antialiasing](../ray_tracing_jitter_cam) tutorial,
-you will need just a few changes.
-
-First, `seed` will need to be available in the any hit shader, which is the reason we have added it to the hitPayload structure.
-
-Change the local `seed` to `prd.seed` everywhere.
-
-~~~~ C++
-prd.seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, pushC.frame);
-~~~~
-
-For optimization, the `TraceRayEXT` call was using the `gl_RayFlagsOpaqueEXT` flag. But
-this will skip the any hit shader, so change it to
-
-~~~~ C++
-uint rayFlags = gl_RayFlagsNoneEXT;
-~~~~
-
-## `raytrace.rchit`
-
-Similarly, in the closest hit shader, change the flag to `gl_RayFlagsSkipClosestHitShaderEXT`, as we want to enable the any hit and miss shaders, but we still don't care
-about the closest hit shader for shadow rays. This will enable transparent shadows.
-
-~~~~ C++
-uint flags = gl_RayFlagsSkipClosestHitShaderEXT;
-~~~~
-
-# Scene and Model
-
-For a more interesting scene, you can replace the `helloVk.loadModel` calls in `main()` with the following scene:
-
-~~~~ C++
- helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths));
- helloVk.loadModel(nvh::findFile("media/scenes/sphere.obj", defaultSearchPaths),
- nvmath::scale_mat4(nvmath::vec3f(1.5f))
- * nvmath::translation_mat4(nvmath::vec3f(0.0f, 1.0f, 0.0f)));
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
-~~~~
-
-## OBJ Materials
-
-By default, all objects are opaque, you will need to change the material description.
-
-Edit the first few lines of `media/scenes/wuson.mtl` and `media/scenes/sphere.mtl` to use a new illumination model (4) with a dissolve value of 0.5:
-
-~~~~ C++
-newmtl default
-illum 4
-d 0.5
-...
-~~~~
-
-# Accumulation
-
-As mentioned earlier, for the effect to work, we need to accumulate frames over time. Please implement the following from [Jitter Camera/Antialiasing](vkrt_tuto_jitter_cam.md):
-
-* [Frame Number](vkrt_tuto_jitter_cam.md.htm#toc1.2)
-* [Storing or Updating](vkrt_tuto_jitter_cam.md.htm#toc1.4)
-* [Application Frame Update](vkrt_tuto_jitter_cam.md.htm#toc2)
-
-
-# Fixing Pipeline
-
-The above code works, but might not work in the future. The reason is, the shadow ray `traceRayEXT` call in the Closest Hit shader, uses payload 1
-and when intersecting the object, the any hit shader will be executed using payload 0. In the time of writing those lines, the driver add
-padding and there are no side effect, but this is not how thing should be done.
-
-Each `traceRayEXT` invocation should have as many Hit Groups as there are trace calls with different payload. For the other examples, it is still fine,
-because we are using the `gl_RayFlagsSkipClosestHitShaderNV` flag and the closest hit shader (payload 0) will not be called and there were not
-any hit or intersection shaders in the Hit Group. But in this example, the closest hit will be skiped, but not the any hit.
-
-**To fix this**, we need to add another hit group.
-
-This is how the current SBT looks like.
-
-
-
-And we need to add the following to the ray tracing pipeline, a copy of the previous Hit Group, with a new AnyHit using the proper payload.
-
-
-
-
-## New shaders
-
-Create two new files `raytrace_0.ahit` and `raytrace_1.ahit`, and rename `raytrace.ahit` to `raytrace_ahit.glsl`
-
-!!! WARNING CMake
- Cmake need to be re-run to add the new files to the project.
-
-In `raytrace_0.ahit` add the following code
-
-~~~~ C
-#version 460
-#extension GL_GOOGLE_include_directive : enable
-
-#define PAYLOAD_0
-#include "raytrace_rahit.glsl"
-~~~~
-
-and in `raytrace_1.ahit`, replace `PAYLOAD_0` by `PAYLOAD_1`
-
-Then in `raytrace_ahit.glsl` remove the `#version 460` and add the following code, so that we have the right layout.
-
-~~~~ C
-#ifdef PAYLOAD_0
-layout(location = 0) rayPayloadInNV hitPayload prd;
-#elif defined(PAYLOAD_1)
-layout(location = 1) rayPayloadInNV shadowPayload prd;
-#endif
-~~~~
-
-## New Payload
-
-We cannot simply have a bool for our shadow ray payload. We also need the `seed` for the random function.
-
-In the `raycommon.glsl` file, add the following structure
-
-~~~~ C
-struct shadowPayload
-{
- bool isHit;
- uint seed;
-};
-~~~~
-
-The usage of the shadow payload is done in the closest hit and shadow miss shader. First, let's modify `raytraceShadow.rmiss` to look like this
-
-~~~~ C
-#version 460
-#extension GL_NV_ray_tracing : require
-#extension GL_GOOGLE_include_directive : enable
-
-#include "raycommon.glsl"
-
-layout(location = 1) rayPayloadInNV shadowPayload prd;
-
-void main()
-{
- prd.isHit = false;
-}
-~~~~
-
-The the change in the closest hit shader `raytrace.rchit`, need to change the usage of the payload, but also the call to `traceRayEXT`
-
-Replace the payload to
-
-~~~~ C
-layout(location = 1) rayPayloadNV shadowPayload prdShadow;
-~~~~
-
-Then just before the call to `traceRayEXT`, initialize the values to
-
-~~~~ C
-prdShadow.isHit = true;
-prdShadow.seed = prd.seed;
-~~~~
-
-and after the trace, set the seed value back to the main payload
-
-~~~~ C
-prd.seed = prdShadow.seed;
-~~~~
-
-And check if the trace shadow hit an object of not
-
-~~~~ C
-if(prdShadow.isHit)
-~~~~
-
-### traceRayEXT
-
-When we call `traceRayEXT`, since we are using the payload 1 (last argument), we also
-need the trace to hit the alternative hit group, the one using the payload 1.
-To do this, we need to set the sbtRecordOffset to 1
-
-~~~~ C
-traceRayEXT(topLevelAS, // acceleration structure
- flags, // rayFlags
- 0xFF, // cullMask
- 1, // sbtRecordOffset
- 0, // sbtRecordStride
- 1, // missIndex
- origin, // ray origin
- tMin, // ray min range
- rayDir, // ray direction
- tMax, // ray max range
- 1 // payload (location = 1)
- );
-~~~~
-
-
-
-
-## Ray tracing Pipeline
-
-The final step is to add the new Hit Group. This is a change in `HelloVulkan::createRtPipeline()`.
-We need to load the new any hit shader and create a new Hit Group.
-
-Replace the `"shaders/raytrace.rahit.spv"` for `"shaders/raytrace_0.rahit.spv"`
-
-Then, after the creating of the first Hit Group, create a new one, where only the any hit using payload 1
-is added. We are skipping the closest hit shader in the trace call, so we can ignore it in the Hit Group.
-
-~~~~ C
-// Payload 1
-vk::ShaderModule ahit1SM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/raytrace_1.rahit.spv", true, paths));
-hg.setClosestHitShader(VK_SHADER_UNUSED_NV); // Not used by shadow (skipped)
-hg.setAnyHitShader(static_cast(stages.size()));
-stages.push_back({{}, vk::ShaderStageFlagBits::eAnyHitNV, ahit1SM, "main"});
-m_rtShaderGroups.push_back(hg);
-~~~~
-
-At the end of the function, delete the shader module `ahit1SM`.
-
-
-!!! NOTE Re-Run
- Everything should work as before, but now it does it right.
-
-
-
-
-
-
-
-
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_anyhit](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_anyhit)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_callable.md.html b/docs/vkrt_tuto_callable.md.html
deleted file mode 100644
index 2b4a7c8..0000000
--- a/docs/vkrt_tuto_callable.md.html
+++ /dev/null
@@ -1,202 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Instances**
-
-Author: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/)
-
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.htm).
-
-Ray tracing allow to use [callable shaders](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap8.html#shaders-callable)
-in ray-generation, closest-hit, miss or another callable shader stage.
-It is similar to an indirect function call, whitout having to link those shaders with the executable program.
-
-(insert setup.md.html here)
-
-
-# Data Storage
-
-Data can only access data passed in to the callable from parent stage. There will be only one structure pass at a time and should be declared like for payload.
-
-In the parent stage, using the `callableDataEXT` storage qualifier, it could be declared like:
-
-~~~~ C++
-layout(location = 0) callableDataEXT rayLight cLight;
-~~~~
-
-where `rayLight` struct is defined in a shared file.
-
-~~~~ C++
-struct rayLight
-{
- vec3 inHitPosition;
- float outLightDistance;
- vec3 outLightDir;
- float outIntensity;
-};
-~~~~
-
-And in the incoming callable shader, you must use the `callableDataInEXT` storage qualifier.
-
-~~~~ C++
-layout(location = 0) callableDataInEXT rayLight cLight;
-~~~~
-
-# Execution
-
-To execute one of the callable shader, the parent stage need to call `executeCallableEXT`.
-
-The first parameter is the SBT record index, the second one correspond to the 'location' index.
-
-Example of how it is called.
-
-~~~~ C++
-executeCallableEXT(pushC.lightType, 0);
-~~~~
-
-
-# Adding Callable Shaders to the SBT
-
-## Create Shader Modules
-
-In `HelloVulkan::createRtPipeline()`, immediately after adding the closest-hit shader, we will add
-3 callable shaders, for each type of light.
-
-~~~~ C++
- // Callable shaders
- vk::RayTracingShaderGroupCreateInfoKHR callGroup{vk::RayTracingShaderGroupTypeKHR::eGeneral,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
-
- vk::ShaderModule call0 =
- nvvk::createShaderModule(m_device,
- nvh::loadFile("shaders/light_point.rcall.spv", true, paths));
- vk::ShaderModule call1 =
- nvvk::createShaderModule(m_device,
- nvh::loadFile("shaders/light_spot.rcall.spv", true, paths));
- vk::ShaderModule call2 =
- nvvk::createShaderModule(m_device, nvh::loadFile("shaders/light_inf.rcall.spv", true, paths));
-
- callGroup.setGeneralShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call0, "main"});
- m_rtShaderGroups.push_back(callGroup);
- callGroup.setGeneralShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call1, "main"});
- m_rtShaderGroups.push_back(callGroup);
- callGroup.setGeneralShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eCallableKHR, call2, "main"});
- m_rtShaderGroups.push_back(callGroup);
-~~~~
-
-And at the end of the function, delete the shaders.
-
-~~~~ C++
-m_device.destroy(call0);
-m_device.destroy(call1);
-m_device.destroy(call2);
-~~~~
-
-### Shaders
-
-Here are the source of all shaders
-
-* [light_point.rcall](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/master/ray_tracing_callable/shaders/light_point.rcall)
-* [light_spot.rcall](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/master/ray_tracing_callable/shaders/light_spot.rcall)
-* [light_inf.rcall](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/blob/master/ray_tracing_callable/shaders/light_inf.rcall)
-
-
-## Passing Callable to traceRaysKHR
-
-In `HelloVulkan::raytrace()`, we have to tell where the callable shader starts. Since they were added after the hit shader, we have in the SBT the following.
-
-********************
-* +---------+
-* | ray-gen |
-* +---------+
-* | miss0 |
-* | miss1 |
-* +---------+
-* | hit0 |
-* +---------+
-* | call0 |
-* | call1 |
-* | call2 |
-* +---------+
-********************
-
-Therefore, the callable starts at `4 * progSize`
-
-~~~~ C++
-vk::DeviceSize callableGroupOffset = 4u * progSize; // Jump over the previous shaders
-vk::DeviceSize callableGroupStride = progSize;
-~~~~
-
-Then we can call `traceRaysKHR`
-
-~~~~ C++
-const vk::StridedBufferRegionKHR callableShaderBindingTable = {
- m_rtSBTBuffer.buffer, callableGroupOffset, progSize, sbtSize};
-
-cmdBuf.traceRaysKHR(&raygenShaderBindingTable, &missShaderBindingTable, &hitShaderBindingTable,
- &callableShaderBindingTable, //
- m_size.width, m_size.height, 1); //
-~~~~
-
-# Calling the Callable Shaders
-
-In the closest-hit shader, instead of having a if-else case, we can now call directly the right shader base on the type of light.
-
-~~~~ C++
-cLight.inHitPosition = worldPos;
-//#define DONT_USE_CALLABLE
-#if defined(DONT_USE_CALLABLE)
- // Point light
- if(pushC.lightType == 0)
- {
- vec3 lDir = pushC.lightPosition - cLight.inHitPosition;
- float lightDistance = length(lDir);
- cLight.outIntensity = pushC.lightIntensity / (lightDistance * lightDistance);
- cLight.outLightDir = normalize(lDir);
- cLight.outLightDistance = lightDistance;
- }
- else if(pushC.lightType == 1)
- {
- vec3 lDir = pushC.lightPosition - cLight.inHitPosition;
- cLight.outLightDistance = length(lDir);
- cLight.outIntensity =
- pushC.lightIntensity / (cLight.outLightDistance * cLight.outLightDistance);
- cLight.outLightDir = normalize(lDir);
- float theta = dot(cLight.outLightDir, normalize(-pushC.lightDirection));
- float epsilon = pushC.lightSpotCutoff - pushC.lightSpotOuterCutoff;
- float spotIntensity = clamp((theta - pushC.lightSpotOuterCutoff) / epsilon, 0.0, 1.0);
- cLight.outIntensity *= spotIntensity;
- }
- else // Directional light
- {
- cLight.outLightDir = normalize(-pushC.lightDirection);
- cLight.outIntensity = 1.0;
- cLight.outLightDistance = 10000000;
- }
-#else
- executeCallableEXT(pushC.lightType, 0);
-#endif
-~~~~
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_callable](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_callable)
-
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_further.md.html b/docs/vkrt_tuto_further.md.html
deleted file mode 100644
index f15c142..0000000
--- a/docs/vkrt_tuto_further.md.html
+++ /dev/null
@@ -1,108 +0,0 @@
-
-
-## [Jitter Camera (Anti-Aliasing)](vkrt_tuto_jitter_cam.md.htm)
-
-Anti-aliases the image by accumulating small variations of rays over time.
-
-* Random ray direction generation
-* Read/write/accumulate final image
-
-
-
-## [Handle Thousands of Objects](vkrt_tuto_instances.md.htm)
-
-The current example allocates memory for each object, each of which has several buffers.
-This shows how to get around Vulkan's limits on the total number of memory allocations by using a memory allocator.
-
-* Extend the limit of 4096 memory allocations
-* Using memory allocators: DMA, VMA
-
-
-
-## [Any Hit Shader (Transparency)](vkrt_tuto_anyhit.md.htm)
-
-Implements transparent materials by adding a new shader to the Hit group and using the material
-information to discard hits over time.
-
-* Adding anyhit (.ahit) to the ray tracing pipeline
-* Randomly letting the ray hit or not which is making simple transparency
-
-
-
-
-## [Reflections](vkrt_tuto_reflection.md.htm)
-
-Reflections can be implemented by shooting new rays from the closest hit shader, or by iteratively shooting them from
-the raygen shader. This example shows the limitations and differences of these implementations.
-
-* Calling traceRayEXT() from the closest hit shader (recursive)
-* Adding more data to the ray payload to continue the ray from the raygen shader.
-
-
-
-
-## [Multiple Closest Hits Shader and Shader Records](vkrt_tuto_manyhits.md.htm)
-
-Explains how to add more closest hit shaders, choose which instance uses which shader, and add data per SBT that can be
-retrieved in the shader, and more.
-
-* One closest hit shader per object
-* Sharing closest hit shaders for some object
-* Passing shader record to closest hit shader
-
-
-
-
-## [Animation](vkrt_tuto_animation.md.htm)
-
-This tutorial shows how animating the transformation matrices of the instances (TLAS)
-and animating the vertices of an object (BLAS) in a compute shader, could be done.
-
-* Refit of top level acceleration structure
-* Refit of bottom level acceleration structure
-
-
-
-
-## [Intersection Shader](vkrt_tuto_intersection.md.html)
-
-Adding thousands of implicit primitives and using an intersection shader to render spheres and cubes. The tutorial
-explains what is needed to get procedural hit group working.
-
-* Intersection Shader
-* Sphere intersection
-* Axis aligned bounding box intersection
-
-
-
-
-
-## [Callable Shader](vkrt_tuto_callable.md.html)
-
-Replacing if/else by callable shaders. The code to execute the lighting is done in separate callable shaders instead of been part of the code.
-
-* Adding multiple callable shaders
-* Calling ExecuteCallableEXT from the closest hit shader
-
-
-
-
-
-
-## [Ray Query](vkrt_tuto_rayquery.md.htm)
-
-Invoking ray intersection queries directly from the fragment shader to cast shadow rays.
-
-* Ray tracing directly from the fragment shader
-
-
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_indirect_scissor.md.html b/docs/vkrt_tuto_indirect_scissor.md.html
deleted file mode 100644
index b649eaf..0000000
--- a/docs/vkrt_tuto_indirect_scissor.md.html
+++ /dev/null
@@ -1,1505 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Trace Rays Indirect**
-
-Authors: David Zhao Akeley
-
-
-
-This is an extension of the [Vulkan ray tracing tutorial](vkrt_tutorial.md.html).
-
-We will discuss the `vkCmdTraceRaysIndirectKHR` command, which allows the
-`width`, `height`, and `depth` of a trace ray command to be specifed by a
-buffer on the device, rather than directly by the host. As a demonstration,
-this example will add colorful lanterns to the scene that add their own light
-and shadows, with a finite radius of effect. A compute shader will calculate
-scissor rectangles for each lantern, and an indirect trace rays command will
-dispatch rays for lanterns only within those scissor rectangles.
-
-# Outline
-
-The basic idea is to split up ray tracing into seperate passes. The first pass
-is similar to the original tutorial: it fills in the entire output image,
-calculating lighting from the main light in the scene. Subsequently, one
-pass is run within a scissor rectangle for each lantern to add its light
-contribution to the output image.
-
-The steps to accomplish this are:
-
-* Add a buffer to store lantern positions, colors, and scissor rectangles.
- These lanterns are separate from the OBJ geometry loaded in the main
- tutorial. Run a compute shader each frame to fill in the scissor rectangles.
-
-* Build a BLAS for a lantern, and add lantern instances to the TLAS.
- As lanterns are self-illuminating, the closest hit shader used to shade
- ordinary OBJ geometry is inappropriate, so, we will add a new hit group (new
- closest-hit shader) for lanterns to the SBT, and set `hitGroupId`
- for lantern instances to `1` so this hit group is used.
-
-* Modify the ray generation shader so that it emulates additive blending,
- and supports drawing within scissor rectangles (in the main tutorial,
- the raygen shader assumes the ray trace dispatch covers the whole screen).
-
-* Add shadow rays in lantern passes, cast towards the lantern whose light
- contribution is being added in the current pass. To detect whether the
- expected lantern was hit, we add a new miss shader and two new closest
- hit shaders (one for OBJ instances, one for lanterns) that return the
- index of the lantern hit (if any).
-
-* Add one `vkCmdTraceRaysIndirectKHR` call in `HelloVulkan::raytrace` for
- each lantern in the scene.
-
-If everything goes well, we should see something like this (the "lantern debug"
-checkbox enables visualizing the scissor rectangles).
-
-
-
-# Lantern Scissor Rectangles
-
-In this step we set up the buffer storing lantern info, and a compute shader
-for calculating the scissor rectangles.
-
-## Allocate Host Storage
-
-We first need to stage the vector of lanterns on the host in a vector.
-Since this is not an animation example, we won't be concerned with
-keeping track of changes to this vector: changes are forbidden after the
-acceleration structures are built.
-
-In `hello_vulkan.h`, declare a struct for holding information about
-the lanterns on the host.
-
-```` C
- // Information on each colored lantern illuminating the scene.
- struct Lantern
- {
- nvmath::vec3f position;
- nvmath::vec3f color;
- float brightness;
- float radius; // Max world-space distance that light illuminates.
- };
-````
-
-Then declare a vector of `Lantern` and add
-a new function for configuring a new lantern in the scene.
-
-```` C
- // Array of lanterns in scene. Not modifiable after acceleration structure build.
- std::vector m_lanterns;
- void addLantern(nvmath::vec3f pos, nvmath::vec3f color, float brightness, float radius);
-````
-
-The `addLantern` function is implemented as
-
-```` C
-// Add a light-emitting colored lantern to the scene. May only be called before TLAS build.
-void HelloVulkan::addLantern(nvmath::vec3f pos, nvmath::vec3f color, float brightness, float radius)
-{
- assert(m_lanternCount == 0); // Indicates TLAS build has not happened yet.
-
- m_lanterns.push_back({pos, color, brightness, radius});
-}
-````
-
-In `main.cpp`, we insert calls for adding some lanterns.
-
-```` C
- // Creation of the example
- helloVk.loadModel(nvh::findFile("media/scenes/Medieval_building.obj", defaultSearchPaths, true));
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths, true));
- helloVk.addLantern({ 8.000f, 1.100f, 3.600f}, {1.0f, 0.0f, 0.0f}, 0.4f, 4.0f);
- helloVk.addLantern({ 8.000f, 0.600f, 3.900f}, {0.0f, 1.0f, 0.0f}, 0.4f, 4.0f);
- helloVk.addLantern({ 8.000f, 1.100f, 4.400f}, {0.0f, 0.0f, 1.0f}, 0.4f, 4.0f);
- helloVk.addLantern({ 1.730f, 1.812f, -1.604f}, {0.0f, 0.4f, 0.4f}, 0.4f, 4.0f);
- helloVk.addLantern({ 1.730f, 1.862f, 1.916f}, {0.0f, 0.2f, 0.4f}, 0.3f, 3.0f);
- helloVk.addLantern({-2.000f, 1.900f, -0.700f}, {0.8f, 0.8f, 0.6f}, 0.4f, 3.9f);
- helloVk.addLantern({ 0.100f, 0.080f, -2.392f}, {1.0f, 0.0f, 1.0f}, 0.5f, 5.0f);
- helloVk.addLantern({ 1.948f, 0.080f, 0.598f}, {1.0f, 1.0f, 1.0f}, 0.6f, 6.0f);
- helloVk.addLantern({-2.300f, 0.080f, 2.100f}, {0.0f, 0.7f, 0.0f}, 0.6f, 6.0f);
- helloVk.addLantern({-1.400f, 4.300f, 0.150f}, {1.0f, 1.0f, 0.0f}, 0.7f, 7.0f);
-````
-
-## Lantern Device Storage
-
-In `hello_vulkan.h`, declare a struct for storing lanterns on the device.
-This includes the host information, plus a scissor rectangle.
-```` C
- // Information on each colored lantern, plus the info needed for dispatching the
- // indirect ray trace command used to add its brightness effect.
- // The dispatched ray trace covers pixels (offsetX, offsetY) to
- // (offsetX + indirectCommand.width - 1, offsetY + indirectCommand.height - 1).
- struct LanternIndirectEntry
- {
- // Filled in by the device using a compute shader.
- // NOTE: I rely on indirectCommand being the first member.
- VkTraceRaysIndirectCommandKHR indirectCommand;
- int32_t offsetX;
- int32_t offsetY;
-
- // Filled in by the host.
- Lantern lantern;
- };
-````
-
-!!! NOTE
- `VkTraceRaysIndirectCommandKHR` is just a struct of 3 `int32_t` defining
- the `width`, `height`, `depth` of a trace ray command.
-
-
-We also declare an equivalent structure for shaders in the file
-`LanternIndirectEntry.glsl`. We avoid using `vec3` due to differences
-in alignment in C++ and GLSL.
-```` C
-struct LanternIndirectEntry
-{
- // VkTraceRaysIndirectCommandKHR
- int indirectWidth;
- int indirectHeight;
- int indirectDepth;
-
- // Pixel coordinate of scissor rect upper-left.
- int offsetX;
- int offsetY;
-
- // Lantern starts here:
- // Can't use vec3 due to alignment.
- float x, y, z;
- float red, green, blue;
- float brightness;
- float radius;
-};
-````
-
-To store the lanterns on the device, declare the Vulkan buffer of `LanternIndirectEntry`
-in `hello_vulkan.h`
-
-```` C
- // Buffer to source vkCmdTraceRaysIndirectKHR indirect parameters and lantern color,
- // position, etc. from when doing lantern lighting passes.
- nvvk::Buffer m_lanternIndirectBuffer;
- VkDeviceSize m_lanternCount = 0; // Set to actual lantern count after TLAS build, as
- // that is the point no more lanterns may be added.
-````
-
-and fill it with a `createLanternIndirectBuffer` function. For performance,
-we allocate a device-local buffer. We need usage flags for
-
-* Storage buffer use, so the compute shader can write to it.
-* Indirect buffer use, so we can source indirect parameters from it when dispatching a ray trace.
-* Device address use, as `vkCmdTraceRaysIndirectKHR` expects a device address.
-* Transfer dst use, so the buffer can be initialized with the lantern colors and positions from `m_lanterns`.
-
-```` C
-// Allocate the buffer used to pass lantern info + ray trace indirect parameters to ray tracer.
-// Fill in the lantern info from m_lanterns (indirect info is filled per-frame on device
-// using a compute shader). Must be called only after TLAS build.
-//
-// The buffer is an array of LanternIndirectEntry, entry i is for m_lanterns[i].
-void HelloVulkan::createLanternIndirectBuffer()
-{
- assert(m_lanternCount > 0);
- assert(m_lanternCount == m_lanterns.size());
-
- // m_alloc behind the scenes uses cmdBuf to transfer data to the buffer.
- nvvk::CommandPool cmdBufGet(m_device, m_graphicsQueueIndex);
- vk::CommandBuffer cmdBuf = cmdBufGet.createCommandBuffer();
-
- using Usage = vk::BufferUsageFlagBits;
- m_lanternIndirectBuffer =
- m_alloc.createBuffer(sizeof(LanternIndirectEntry) * m_lanternCount,
- Usage::eIndirectBuffer | Usage::eTransferDst
- | Usage::eShaderDeviceAddress | Usage::eStorageBuffer,
- vk::MemoryPropertyFlagBits::eDeviceLocal);
-
- std::vector entries(m_lanternCount);
- for (size_t i = 0; i < m_lanternCount; ++i) entries[i].lantern = m_lanterns[i];
- cmdBuf.updateBuffer(m_lanternIndirectBuffer.buffer, 0, entries.size() * sizeof entries[0], entries.data());
-
- cmdBufGet.submitAndWait(cmdBuf);
-}
-````
-
-Call this function in `main.cpp`, after the AS build (the AS build will be modified later).
-
-```` C
- helloVk.initRayTracing();
- helloVk.createBottomLevelAS();
- helloVk.createTopLevelAS();
- helloVk.createLanternIndirectBuffer();
-````
-
-## Set up Compute Shader
-
-The compute shader will need the view and projection matrices, plus the Z near plane,
-screen dimensions, and length of the `LanternIndirectBuffer` array, in order to
-compute the scissor rectangles. Feed all of this in with a push constant, declared
-in `hello_vulkan.h`
-
-```` C
- // Push constant for compute shader filling lantern indirect buffer.
- // Barely fits in 128-byte push constant limit guaranteed by spec.
- struct LanternIndirectPushConstants
- {
- nvmath::vec4 viewRowX; // First 3 rows of view matrix.
- nvmath::vec4 viewRowY; // Set w=1 implicitly in shader.
- nvmath::vec4 viewRowZ;
-
- nvmath::mat4 proj; // Perspective matrix
- float nearZ; // Near plane used to create projection matrix.
-
- // Pixel dimensions of output image (needed to scale NDC to screen coordinates).
- int32_t screenX;
- int32_t screenY;
-
- // Length of the LanternIndirectEntry array.
- int32_t lanternCount;
- } m_lanternIndirectPushConstants;
-````
-
-!!! NOTE Push Constant Limit
- The Vulkan spec only guarantees 128 bytes of push constant, so to make everything
- fit, we have to chop off the implicit bottom row of the view matrix.
-
-This push constant is consumed in the compute shader `shaders/lanternIndirect.comp`.
-We go through `lanternCount` iterations of a loop that fill in the scissor
-rectangle for each `LanternIndirectEntry` (splitting the work among 128
-invocations of the work group).
-
-```` C
-#version 460
-#extension GL_GOOGLE_include_directive : enable
-
-// Compute shader for filling in raytrace indirect parameters for each lantern
-// based on the current camera position (passed as view and proj matrix in
-// push constant).
-//
-// Designed to be dispatched with only one work group; it alone fills in
-// the entire lantern array (of length lanternCount, in also push constant).
-
-#define LOCAL_SIZE 128
-layout(local_size_x = LOCAL_SIZE, local_size_y = 1, local_size_z = 1) in;
-
-#include "LanternIndirectEntry.glsl"
-
-layout(binding = 0, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
-
-layout(push_constant) uniform Constants
-{
- vec4 viewRowX;
- vec4 viewRowY;
- vec4 viewRowZ;
- mat4 proj;
- float nearZ;
- int screenX;
- int screenY;
- int lanternCount;
-}
-pushC;
-
-// Copy the technique of "2D Polyhedral Bounds of a Clipped,
-// Perspective-Projected 3D Sphere" M. Mara M. McGuire
-// http://jcgt.org/published/0002/02/05/paper.pdf
-// to compute a screen-space rectangle covering the given Lantern's
-// light radius-of-effect. Result is in screen (pixel) coordinates.
-void getScreenCoordBox(in LanternIndirectEntry lantern, out ivec2 lower, out ivec2 upper);
-
-// Use the xyz and radius of lanterns[i] plus the transformation matrices
-// in pushC to fill in the offset and indirect parameters of lanterns[i]
-// (defines the screen rectangle that this lantern's light is bounded in).
-void fillIndirectEntry(int i)
-{
- LanternIndirectEntry lantern = lanterns.lanterns[i];
- ivec2 lower, upper;
- getScreenCoordBox(lantern, lower, upper);
-
- lanterns.lanterns[i].indirectWidth = max(0, upper.x - lower.x);
- lanterns.lanterns[i].indirectHeight = max(0, upper.y - lower.y);
- lanterns.lanterns[i].indirectDepth = 1;
- lanterns.lanterns[i].offsetX = lower.x;
- lanterns.lanterns[i].offsetY = lower.y;
-}
-
-void main()
-{
- for (int i = int(gl_LocalInvocationID.x); i < pushC.lanternCount; i += LOCAL_SIZE)
- {
- fillIndirectEntry(i);
- }
-}
-
-/** Center is in camera space */
-void getBoundingBox(
- in vec3 center,
- in float radius,
- in float nearZ,
- in mat4 projMatrix,
- out vec2 ndc_low,
- out vec2 ndc_high) {
-````
-!!! TIP
- Omitted code for computing scissor rectangles, taken from "2D Polyhedral Bounds of a Clipped,
- Perspective-Projected 3D Sphere" by Michael Mara and Morgan McGuire.
- http://jcgt.org/published/0002/02/05/paper.pdf
-```` C
-}
-
-void getScreenCoordBox(in LanternIndirectEntry lantern, out ivec2 lower, out ivec2 upper)
-{
- vec4 lanternWorldCenter = vec4(lantern.x, lantern.y, lantern.z, 1);
- vec3 center = vec3(
- dot(pushC.viewRowX, lanternWorldCenter),
- dot(pushC.viewRowY, lanternWorldCenter),
- dot(pushC.viewRowZ, lanternWorldCenter));
- vec2 ndc_low, ndc_high;
- float paperNearZ = -abs(pushC.nearZ); // Paper expected negative nearZ, took 2 days to figure out!
- getBoundingBox(center, lantern.radius, paperNearZ, pushC.proj, ndc_low, ndc_high);
-
- // Convert NDC [-1,+1]^2 coordinates to screen coordinates, and clamp to stay in bounds.
-
- lower.x = clamp(int((ndc_low.x * 0.5 + 0.5) * pushC.screenX), 0, pushC.screenX);
- lower.y = clamp(int((ndc_low.y * 0.5 + 0.5) * pushC.screenY), 0, pushC.screenY);
- upper.x = clamp(int((ndc_high.x * 0.5 + 0.5) * pushC.screenX), 0, pushC.screenX);
- upper.y = clamp(int((ndc_high.y * 0.5 + 0.5) * pushC.screenY), 0, pushC.screenY);
-}
-````
-
-Now we just have to fill out the usual boilerplate for setting up the descriptor
-set (passes the `LanternIndirectEntry` array) and compute pipeline. We only have
-to allocate one descriptor as the `LanternIndirectEntry` array never changes.
-
-`hello_vulkan.h`:
-
-```` C
- nvvk::DescriptorSetBindings m_lanternIndirectDescSetLayoutBind;
- vk::DescriptorPool m_lanternIndirectDescPool;
- vk::DescriptorSetLayout m_lanternIndirectDescSetLayout;
- vk::DescriptorSet m_lanternIndirectDescSet;
- vk::PipelineLayout m_lanternIndirectCompPipelineLayout;
- vk::Pipeline m_lanternIndirectCompPipeline;
-````
-
-`hello_vulkan.cpp`:
-
-```` C
-//--------------------------------------------------------------------------------------------------
-// The compute shader just needs read/write access to the buffer of LanternIndirectEntry.
-void HelloVulkan::createLanternIndirectDescriptorSet()
-{
- using vkDT = vk::DescriptorType;
- using vkSS = vk::ShaderStageFlagBits;
- using vkDSLB = vk::DescriptorSetLayoutBinding;
-
- // Lantern buffer (binding = 0)
- m_lanternIndirectDescSetLayoutBind.addBinding( //
- vkDSLB(0, vkDT::eStorageBuffer, 1, vkSS::eCompute));
-
- m_lanternIndirectDescPool = m_lanternIndirectDescSetLayoutBind.createPool(m_device);
- m_lanternIndirectDescSetLayout = m_lanternIndirectDescSetLayoutBind.createLayout(m_device);
- m_lanternIndirectDescSet =
- m_device.allocateDescriptorSets({m_lanternIndirectDescPool, 1, &m_lanternIndirectDescSetLayout})[0];
-
- assert(m_lanternIndirectBuffer.buffer);
- vk::DescriptorBufferInfo lanternBufferInfo{
- m_lanternIndirectBuffer.buffer, 0, m_lanternCount * sizeof(LanternIndirectEntry)};
-
- std::vector writes;
- writes.emplace_back(m_lanternIndirectDescSetLayoutBind.makeWrite(m_lanternIndirectDescSet, 0, &lanternBufferInfo));
- m_device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr);
-}
-
-// Create compute pipeline used to fill m_lanternIndirectBuffer with parameters
-// for dispatching the correct number of ray traces.
-void HelloVulkan::createLanternIndirectCompPipeline()
-{
- // Compile compute shader and package as stage.
- vk::ShaderModule computeShader =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/lanternIndirect.comp.spv", true, defaultSearchPaths, true));
- vk::PipelineShaderStageCreateInfo stageInfo;
- stageInfo.setStage(vk::ShaderStageFlagBits::eCompute);
- stageInfo.setModule(computeShader);
- stageInfo.setPName("main");
-
- // Set up push constant and pipeline layout.
- constexpr auto pushSize = static_cast(sizeof(m_lanternIndirectPushConstants));
- vk::PushConstantRange pushCRange = {vk::ShaderStageFlagBits::eCompute, 0, pushSize};
- static_assert(pushSize <= 128, "Spec guarantees only 128 byte push constant");
- vk::PipelineLayoutCreateInfo layoutInfo;
- layoutInfo.setSetLayoutCount(1);
- layoutInfo.setPSetLayouts(&m_lanternIndirectDescSetLayout);
- layoutInfo.setPushConstantRangeCount(1);
- layoutInfo.setPPushConstantRanges(&pushCRange);
- m_lanternIndirectCompPipelineLayout = m_device.createPipelineLayout(layoutInfo);
-
- // Create compute pipeline.
- vk::ComputePipelineCreateInfo pipelineInfo;
- pipelineInfo.setStage(stageInfo);
- pipelineInfo.setLayout(m_lanternIndirectCompPipelineLayout);
- m_lanternIndirectCompPipeline = static_cast(m_device.createComputePipeline({}, pipelineInfo));
-
- m_device.destroy(computeShader);
-}
-````
-
-`main.cpp` (add after indirect buffer initialization).
-
-```` C
- // #VKRay
- helloVk.initRayTracing();
- helloVk.createBottomLevelAS();
- helloVk.createTopLevelAS();
- helloVk.createLanternIndirectBuffer();
- helloVk.createRtDescriptorSet();
- helloVk.createRtPipeline();
- helloVk.createLanternIndirectDescriptorSet();
- helloVk.createLanternIndirectCompPipeline();
-````
-
-## Call Compute Shader
-
-In `HelloVulkan::raytrace`, we have to fill in the earlier push constant and
-dispatch the compute shader before moving
-on to the actual ray tracing. This is rather verbose due to the need for a
-pipeline barrier synchronizing access to the `LanternIndirectEntry` array
-between the compute shader and indirect draw stages.
-
-```` C
-void HelloVulkan::raytrace(const vk::CommandBuffer& cmdBuf, const nvmath::vec4f& clearColor)
-{
- // Before tracing rays, we need to dispatch the compute shaders that
- // fill in the ray trace indirect parameters for each lantern pass.
-
- // First, barrier before, ensure writes aren't visible to previous frame.
- vk::BufferMemoryBarrier bufferBarrier;
- bufferBarrier.setSrcAccessMask(vk::AccessFlagBits::eIndirectCommandRead);
- bufferBarrier.setDstAccessMask(vk::AccessFlagBits::eShaderWrite);
- bufferBarrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
- bufferBarrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
- bufferBarrier.setBuffer(m_lanternIndirectBuffer.buffer);
- bufferBarrier.offset = 0;
- bufferBarrier.size = m_lanternCount * sizeof m_lanterns[0];
- cmdBuf.pipelineBarrier( //
- vk::PipelineStageFlagBits::eDrawIndirect, //
- vk::PipelineStageFlagBits::eComputeShader,//
- vk::DependencyFlags(0), //
- {}, {bufferBarrier}, {});
-
- // Bind compute shader, update push constant and descriptors, dispatch compute.
- cmdBuf.bindPipeline(vk::PipelineBindPoint::eCompute, m_lanternIndirectCompPipeline);
- nvmath::mat4 view = getViewMatrix();
- m_lanternIndirectPushConstants.viewRowX = view.row(0);
- m_lanternIndirectPushConstants.viewRowY = view.row(1);
- m_lanternIndirectPushConstants.viewRowZ = view.row(2);
- m_lanternIndirectPushConstants.proj = getProjMatrix();
- m_lanternIndirectPushConstants.nearZ = nearZ;
- m_lanternIndirectPushConstants.screenX = m_size.width;
- m_lanternIndirectPushConstants.screenY = m_size.height;
- m_lanternIndirectPushConstants.lanternCount = m_lanternCount;
- cmdBuf.pushConstants(
- m_lanternIndirectCompPipelineLayout,
- vk::ShaderStageFlagBits::eCompute,
- 0, m_lanternIndirectPushConstants);
- cmdBuf.bindDescriptorSets(
- vk::PipelineBindPoint::eCompute, m_lanternIndirectCompPipelineLayout, 0, {m_lanternIndirectDescSet}, {});
- cmdBuf.dispatch(1, 1, 1);
-
- // Ensure compute results are visible when doing indirect ray trace.
- bufferBarrier.setSrcAccessMask(vk::AccessFlagBits::eShaderWrite);
- bufferBarrier.setDstAccessMask(vk::AccessFlagBits::eIndirectCommandRead);
- cmdBuf.pipelineBarrier( //
- vk::PipelineStageFlagBits::eComputeShader, //
- vk::PipelineStageFlagBits::eDrawIndirect, //
- vk::DependencyFlags(0), //
- {}, {bufferBarrier}, {});
-
-
- // Now move on to the actual ray tracing.
- m_debug.beginLabel(cmdBuf, "Ray trace");
-````
-
-!!! TIP `eDrawIndirect`
- `vk::PipelineStageFlagBits::eDrawIndirect` (`VK_PIPELINE_STAGE_DRAW_INDIRECT_BIT`)
- covers the stage that sources indirect paramaters for compute and ray trace
- indirect commands, not just graphics draw indirect commands.
-
-Since the near plane and view/projection matrices are used in multiple places now,
-they were factored out to common code in `hello_vulkan.h`.
-
-```` C
- nvmath::mat4 getViewMatrix()
- {
- return CameraManip.getMatrix();
- }
-
- static constexpr float nearZ = 0.1f;
- nvmath::mat4 getProjMatrix()
- {
- const float aspectRatio = m_size.width / static_cast(m_size.height);
- return nvmath::perspectiveVK(CameraManip.getFov(), aspectRatio, nearZ, 1000.0f);
- }
-````
-
-The function for updating the uniform buffer is tweaked to match.
-
-```` C
-void HelloVulkan::updateUniformBuffer(const vk::CommandBuffer& cmdBuf)
-{
- const float aspectRatio = m_size.width / static_cast(m_size.height);
-
- CameraMatrices ubo = {};
- ubo.view = getViewMatrix();
- ubo.proj = getProjMatrix();
-````
-
-# Lantern Acceleration Structures and Closest Hit Shader
-
-## Bottom-level Acceleration Structure
-
-Lanterns will be drawn as spheres approximated by a triangular mesh. Declare
-in `hello_vulkan.h` functions for generating this mesh, and declare Vulkan
-buffers for storing the mesh's positions and indices, and a `BlasInput`
-for delivering this sphere mesh to the BLAS builder.
-
-```` C
-private:
- void fillLanternVerts(std::vector& vertices, std::vector& indices);
- void createLanternModel();
-
- // Used to store lantern model, generated at runtime.
- const float m_lanternModelRadius = 0.125;
- nvvk::Buffer m_lanternVertexBuffer;
- nvvk::Buffer m_lanternIndexBuffer;
- nvvk::RaytracingBuilderKHR::BlasInput m_lanternBlasInput{};
-
- // Index of lantern's BLAS in the BLAS array stored in m_rtBuilder.
- size_t m_lanternBlasId;
-````
-
-In order to focus on the ray tracing, I omit the code for generating those vertex and index
-buffers. The relevent code in `HelloVulkan::createLanternModel` for creating the `BlasInput` is
-
-```` C
- // Package vertex and index buffers as BlasInput.
- vk::DeviceAddress vertexAddress = m_device.getBufferAddress({m_lanternVertexBuffer.buffer});
- vk::DeviceAddress indexAddress = m_device.getBufferAddress({m_lanternIndexBuffer.buffer});
-
- uint32_t maxPrimitiveCount = uint32_t(indices.size() / 3);
-
- // Describe buffer as packed array of float vec3.
- vk::AccelerationStructureGeometryTrianglesDataKHR triangles;
- triangles.setVertexFormat(vk::Format::eR32G32B32Sfloat); // vec3 vertex position data.
- triangles.setVertexData(vertexAddress);
- triangles.setVertexStride(sizeof(nvmath::vec3f));
- // Describe index data (32-bit unsigned int)
- triangles.setIndexType(vk::IndexType::eUint32);
- triangles.setIndexData(indexAddress);
- // Indicate identity transform by setting transformData to null device pointer.
- triangles.setTransformData({});
- triangles.setMaxVertex(vertices.size());
-
- // Identify the above data as containing opaque triangles.
- vk::AccelerationStructureGeometryKHR asGeom;
- asGeom.setGeometryType(vk::GeometryTypeKHR::eTriangles);
- asGeom.setFlags(vk::GeometryFlagBitsKHR::eOpaque);
- asGeom.geometry.setTriangles(triangles);
-
- // The entire array will be used to build the BLAS.
- vk::AccelerationStructureBuildRangeInfoKHR offset;
- offset.setFirstVertex(0);
- offset.setPrimitiveCount(maxPrimitiveCount);
- offset.setPrimitiveOffset(0);
- offset.setTransformOffset(0);
-
- // Our blas is made from only one geometry, but could be made of many geometries
- m_lanternBlasInput.asGeometry.emplace_back(asGeom);
- m_lanternBlasInput.asBuildOffsetInfo.emplace_back(offset);
-````
-
-The principle difference from before is that the vertex array is now a packed array of
-float 3-vectors, hence, we call `triangles.setVertexStride(sizeof(nvmath::vec3f));`.
-
-Then, we add a call to create a lantern model and add the lantern model to the list of
-BLAS to build in `HelloVulkan::createBottomLevelAS`. Since we'll need the index of
-the lantern BLAS later to add lantern instances in the TLAS build, store the
-BLAS index for the lantern in `m_lanternBlasId`.
-
-```` C
-// Build the array of BLAS in m_rtBuilder. There are `m_objModel.size() + 1`-many BLASes.
-// The first `m_objModel.size()` are used for OBJ model BLASes, and the last one
-// is used for the lanterns (model generated at runtime).
-void HelloVulkan::createBottomLevelAS()
-{
- // BLAS - Storing each primitive in a geometry
- std::vector allBlas;
- allBlas.reserve(m_objModel.size() + 1);
-
- // Add OBJ models.
- for(const auto& obj : m_objModel)
- {
- auto blas = objectToVkGeometryKHR(obj);
-
- // We could add more geometry in each BLAS, but we add only one for now
- allBlas.emplace_back(blas);
- }
-
- // Add lantern model.
- createLanternModel();
- m_lanternBlasId = allBlas.size();
- allBlas.emplace_back(m_lanternBlasInput);
-
- m_rtBuilder.buildBlas(allBlas, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace);
-}
-````
-
-## Top-level acceleration structure
-
-In the TLAS build function, we add a loop for adding each lantern instance. This is also
-the point that the lanterns are set-in-stone (no more modifications to `m_lanterns`), so
-write `m_lanternCount`.
-
-```` C
-// Build the TLAS in m_rtBuilder. Requires that the BLASes were already built and
-// that all ObjInstance and lanterns have been added. One instance with hitGroupId=0
-// is created for every OBJ instance, and one instance with hitGroupId=1 for each lantern.
-//
-// gl_InstanceCustomIndexEXT will be the index of the instance or lantern in m_objInstance or
-// m_lanterns respectively.
-void HelloVulkan::createTopLevelAS()
-{
- assert(m_lanternCount == 0);
- m_lanternCount = m_lanterns.size();
-
- std::vector tlas;
- tlas.reserve(m_objInstance.size() + m_lanternCount);
-
- // Add the OBJ instances.
- for(uint32_t i = 0; i < static_cast(m_objInstance.size()); i++)
- {
- nvvk::RaytracingBuilderKHR::Instance rayInst;
- rayInst.transform = m_objInstance[i].transform; // Position of the instance
- rayInst.instanceCustomId = i; // gl_InstanceCustomIndexEXT
- rayInst.blasId = m_objInstance[i].objIndex;
- rayInst.hitGroupId = 0; // We will use the same hit group for all OBJ
- rayInst.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
- tlas.emplace_back(rayInst);
- }
-
- // Add lantern instances.
- for(int i = 0; i < static_cast(m_lanterns.size()); ++i)
- {
- nvvk::RaytracingBuilderKHR::Instance lanternInstance;
- lanternInstance.transform = nvmath::translation_mat4(m_lanterns[i].position);
- lanternInstance.instanceCustomId = i;
- lanternInstance.blasId = m_lanternBlasId;
- lanternInstance.hitGroupId = 1; // Next hit group is for lanterns.
- lanternInstance.flags = VK_GEOMETRY_INSTANCE_TRIANGLE_FACING_CULL_DISABLE_BIT_KHR;
- tlas.emplace_back(lanternInstance);
- }
-
- m_rtBuilder.buildTlas(tlas, vk::BuildAccelerationStructureFlagBitsKHR::ePreferFastTrace);
-}
-````
-
-The principle differences are:
-
-* `instanceCustomId` is set to the index of the lantern in `m_lanternIndirectBuffer`, so we
- can look up the lantern color in the forthcoming closest hit shader.
-
-* `hitGroupId` is set to `1`, so that lanterns will use a new closest hit shader instead
- of the old one for OBJs.
-
-!!! TIP Helper Reminders
- `instanceCustomId` corresponds to `VkAccelerationStructureInstanceKHR::instanceCustomIndex` in host
- code and `gl_InstanceCustomIndexEXT` in shader code.
-
- `hitGroupId` corresponds to `VkAccelerationStructureInstanceKHR::instanceShaderBindingTableRecordOffset`.
-
- `blasId` has no Vulkan equivalent; it is translated to a BLAS device address in the `m_rtBuilder` helper.
-
-## Lantern Primary Ray Closest Hit Shader
-
-We now implement the closest hit shader for lanterns hit by primary rays (rays
-cast starting from the eye). First, we need to do a bit of preparation:
-
-* Add a bool to `hitPayload` to control whether additive blending is enabled or
- not. The lanterns will be drawn at a constant brightness, so additive blending
- is enabled for rays hitting OBJ instances and disabled for rays hitting lanterns.
- The raygen shader will be updated later to take this bool into account.
-
-* Access the GLSL definition of `LanternIndirectEntry` so we can look up the lantern color.
-
-* Add a descriptor set to the raytrace pipeline to deliver the
-
-We do the first two tasks in `raycommon.glsl`.
-
-```` C
-#include "LanternIndirectEntry.glsl"
-
-struct hitPayload
-{
- vec3 hitValue;
- bool additiveBlending;
-};
-````
-
-The last task is done in `HelloVulkan::createRtDescriptorSet`
-
-```` C
-// This descriptor set holds the Acceleration structure, output image, and lanterns array buffer.
-//
-void HelloVulkan::createRtDescriptorSet()
-{
- using vkDT = vk::DescriptorType;
- using vkSS = vk::ShaderStageFlagBits;
- using vkDSLB = vk::DescriptorSetLayoutBinding;
-
- // ...
-
- // Lantern buffer (binding = 2)
- m_rtDescSetLayoutBind.addBinding( //
- vkDSLB(2, vkDT::eStorageBuffer, 1, vkSS::eRaygenKHR | vkSS::eClosestHitKHR));
- assert(m_lanternCount > 0);
-
- // ...
-
- std::vector writes;
-
- // ...
-
- writes.emplace_back(m_rtDescSetLayoutBind.makeWrite(m_rtDescSet, 2, &lanternBufferInfo));
- m_device.updateDescriptorSets(static_cast(writes.size()), writes.data(), 0, nullptr);
-}
-````
-
-Now we can implement the new closest hit shader. Name this shader `lantern.rchit`.
-
-```` C
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_EXT_nonuniform_qualifier : enable
-#extension GL_EXT_scalar_block_layout : enable
-#extension GL_GOOGLE_include_directive : enable
-#include "raycommon.glsl"
-
-// Closest hit shader invoked when a primary ray hits a lantern.
-
-// clang-format off
-layout(location = 0) rayPayloadInEXT hitPayload prd;
-
-layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
-
-// clang-format on
-
-void main()
-{
- // Just look up this lantern's color. Self-illuminating, so no lighting calculations.
- LanternIndirectEntry lantern = lanterns.lanterns[nonuniformEXT(gl_InstanceCustomIndexEXT)];
- prd.hitValue = vec3(lantern.red, lantern.green, lantern.blue);
- prd.additiveBlending = false;
-}
-````
-
-This shader is fairly simple, we just had to look up the lantern color and return it in the
-payload. Here, we used the fact that in the TLAS instances setup, we set a lantern instance's
-`gl_InstanceCustomIndexEXT` to its position in the lanterns array.
-
-Now we just have to add the new hit group to the pipeline. This is more of the same,
-in the `HelloVulkan::createRtPipeline` function, we add the lantern closest hit
-group after the OBJ hit group, to match the `hitGroupId`s assigned earlier in the
-TLAS build.
-
-```` C
- // OBJ Primary Ray Hit Group - Closest Hit + AnyHit (not used)
- vk::ShaderModule chitSM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/raytrace.rchit.spv", true, paths, true));
-
- vk::RayTracingShaderGroupCreateInfoKHR hg{vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
- hg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, chitSM, "main"});
- m_rtShaderGroups.push_back(hg);
-
- // Lantern Primary Ray Hit Group
- vk::ShaderModule lanternChitSM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/lantern.rchit.spv", true, paths, true));
-
- vk::RayTracingShaderGroupCreateInfoKHR lanternHg{
- vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
- lanternHg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, lanternChitSM, "main"});
- m_rtShaderGroups.push_back(lanternHg);
-
- // ...
-
- m_device.destroy(lanternChitSM);
-````
-
-We don't have to modify `HelloVulkan::createRtShaderBindingTable`. Changes to the number of
-group handles to copy into the SBT are picked up automatically from `m_rtShaderGroups.size()`.
-
-# Ray Generation Shader
-
-## Draw Within Scissor Rectangle
-
-The original ray generation shader assumed that `gl_LaunchSizeEXT` is the size of the entire
-screen. As this is no longer the case for scissor rectangles, we communicate the screen
-size through push constant instead. In addition, we also add to the push constants a number
-indicating which lantern pass is currently being drawn (-1 for the original full screen pass).
-
-Modify `m_rtPushConstants` in `hello_vulkan.h`.
-
-```` C
- // Push constant for ray trace pipeline.
- struct RtPushConstant
- {
- // Background color
- nvmath::vec4f clearColor;
-
- // Information on the light in the sky used when lanternPassNumber = -1.
- nvmath::vec3f lightPosition;
- float lightIntensity;
- int32_t lightType;
-
- // -1 if this is the full-screen pass. Otherwise, this pass is to add light
- // from lantern number lanternPassNumber. We use this to lookup trace indirect
- // parameters in m_lanternIndirectBuffer.
- int32_t lanternPassNumber;
-
- // Pixel dimensions of the output image.
- int32_t screenX;
- int32_t screenY;
-
- // See m_lanternDebug.
- int32_t lanternDebug;
- } m_rtPushConstants;
-````
-
-We also update the GLSL push constant to match. Since the raygen shader now needs
-access to the push constant, move the push constant definition from `raytrace.rchit`
-to `raycommon.glsl`.
-
-```` C
-layout(push_constant) uniform Constants
-{
- vec4 clearColor;
- vec3 lightPosition;
- float lightIntensity;
- int lightType; // 0: point, 1: infinite
- int lanternPassNumber; // -1 if this is the full-screen pass. Otherwise, used to lookup trace indirect parameters.
- int screenX;
- int screenY;
- int lanternDebug;
-}
-pushC;
-````
-
-(`lanternDebug` will be used later to toggle visualising the scissor rectangles)
-
-
-This move also requires us to tweak `raytrace.rmiss`.
-
-````
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_GOOGLE_include_directive : enable
-#include "raycommon.glsl"
-
-layout(location = 0) rayPayloadInEXT hitPayload prd;
-
-void main()
-{
- prd.hitValue = pushC.clearColor.xyz * 0.8;
- prd.additiveBlending = false;
-}
-````
-
-We will cover initializing the new push constants later, when we look at `vkCmdTraceRaysIndirectKHR`.
-
-In `raytrace.rgen`, we have to replace the old code for calculating the pixel center.
-
-```` C
-void main()
-{
- const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + vec2(0.5);
- const vec2 inUV = pixelCenter / vec2(gl_LaunchSizeEXT.xy);
-````
-
-with
-
-```` C
-layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
-
-void main()
-{
- // Global light pass is a full screen rectangle (lower corner 0,0), but
- // lantern passes are only run within rectangles that may be offset.
- ivec2 pixelOffset = ivec2(0);
- if (pushC.lanternPassNumber >= 0)
- {
- pixelOffset.x = lanterns.lanterns[pushC.lanternPassNumber].offsetX;
- pixelOffset.y = lanterns.lanterns[pushC.lanternPassNumber].offsetY;
- }
-
- const ivec2 pixelIntCoord = ivec2(gl_LaunchIDEXT.xy) + pixelOffset;
- const vec2 pixelCenter = vec2(pixelIntCoord) + vec2(0.5);
- const vec2 inUV = pixelCenter / vec2(pushC.screenX, pushC.screenY);
- vec2 d = inUV * 2.0 - 1.0;
-````
-
-Let's recap why this works. If `pushC.lanternPassNumber` is negative, we're drawing
-the first, full-screen pass, and this code behaves identically as before, except
-that `inUV` performs division by `(pushC.screenX, pushC.screenY)` instead of
-relying on `gl_LaunchSizeEXT` to be the screen size.
-
-Otherwise (`pushC.lanternPassNumber >= 0`), we're drawing a scissor rectangle for
-the given lantern number. Look up that lantern's `LanternIndirectEntry` in the
-array (notice that the descriptor binding for it is added). Its scissor rectangle
-is defined by:
-
-* `LanternIndirectEntry::offsetX`,`offsetY`: the pixel coordinate of the scissor box's
- upper-left.
-
-* `LanternIndirectEntry::width`,`height`: the dimensions of the scissor box (not
- directly used here; consumed by `vkCmdTraceRaysIndirectKHR`).
-
-The `gl_LaunchIDEXT` variable ranges from `(0,0)` to `(width-1, height-1)`, so to
-cover the correct pixels within the scissor, we just have to reposition
-`gl_LaunchIDEXT` by the offset `(offsetX, offsetY)`.
-
-## Additive Blending
-
-We also have to emulate additive blending. Instead of always writing to the output
-image:
-
-```` C
- imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.0));
-````
-
-we do
-
-```` C
- // Either add to or replace output image color based on prd.additiveBlending.
- // Global pass always replaces color as it is the first pass.
- vec3 oldColor = vec3(0);
- if (prd.additiveBlending && pushC.lanternPassNumber >= 0) {
- oldColor = imageLoad(image, pixelIntCoord).rgb;
- }
- imageStore(image, pixelIntCoord, vec4(prd.hitValue + oldColor, 1.0));
-````
-
-thus adding the ray payload's color to the old image color if `prd.additiveBlending`
-is true and this is not the first, full-screen pass (the first pass must replace the
-output image color as its existing contents are garbage).
-
-# Lantern Shadow Rays
-
-We now have to set up a system for casting shadow rays from the OBJ closest hit
-shader to the lanterns. This requires us to
-
-* Detect in `raycast.rchit` whether we are in a lantern pass, and use this
- to decide between casting shadow rays to the main light (as in the base
- tutorial) or casting shadow rays to a lantern.
-
-* Declare a payload for which lantern (if any) was hit, and add a new miss shader
- and two new closest hit shaders for filling that payload.
-
-* Use the `sbtRecordOffset` parameter of `traceRayEXT` to skip over the earlier
- hit groups.
-
-## New payload
-
-In `raytrace.rchit` (called when an OBJ instance is hit by a primary ray), declare
-the new payload and the array of lanterns.
-
-```` C
-layout(location = 2) rayPayloadEXT int hitLanternInstance;
-
-layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
-layout(binding = 2, set = 0) buffer LanternArray { LanternIndirectEntry lanterns[]; } lanterns;
-````
-
-## New shaders
-
-We need a few simple shaders to report the number of the lantern hit (if any) by the shadow ray.
-First is the miss shader, `lanternShadow.rmiss`.
-
-```` C
-// Miss shader invoked when tracing shadow rays (rays towards lantern)
-// in lantern passes. Misses shouldn't really happen, but if they do,
-// report we did not hit any lantern by setting hitLanternInstance = -1.
-layout(location = 2) rayPayloadInEXT int hitLanternInstance;
-
-void main()
-{
- hitLanternInstance = -1;
-}
-````
-
-Then a closest hit shader for OBJ instances hit by a lantern shadow ray.
-This also returns `-1` for "no lantern". Call this `lanternShadowObj.rchit`.
-
-```` C
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_GOOGLE_include_directive : enable
-#include "raycommon.glsl"
-
-// During a lantern pass, this closest hit shader is invoked when
-// shadow rays (rays towards lantern) hit a regular OBJ. Report back
-// that no lantern was hit (-1).
-
-// clang-format off
-layout(location = 2) rayPayloadInEXT int hitLanternInstance;
-
-// clang-format on
-
-void main()
-{
- hitLanternInstance = -1;
-}
-````
-
-Finally, a closest hit shader for lantern instances, named `lanternShadowLantern.rchit`.
-
-```` C
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_GOOGLE_include_directive : enable
-#include "raycommon.glsl"
-
-// During a lantern pass, this closest hit shader is invoked when
-// shadow rays (rays towards lantern) hit a lantern. Report back
-// which lantern was hit.
-
-// clang-format off
-layout(location = 2) rayPayloadInEXT int hitLanternInstance;
-
-// clang-format on
-
-void main()
-{
- hitLanternInstance = gl_InstanceCustomIndexEXT;
-}
-````
-
-Note that we really need to report back the lantern number, and
-not just a boolean "lantern hit" flag. In order to have lanterns cast
-shadows on each other, we must be able to detect that the shadow ray
-hit the "wrong" lantern.
-
-
-
-## Add Shaders to Pipeline
-
-We add the new miss shader as miss shader 2 in the SBT, and the closest hit
-shaders as hit groups 2 and 3 in the SBT, following the earlier 2 hit
-groups for primary rays. Add the following code to `HelloVulkan::createRtPipeline`
-after loading `raytraceShadow.rmiss.spv`.
-
-```` C
- // Miss shader 2 is invoked when a shadow ray for lantern lighting misses the
- // lantern. It shouldn't be invoked, but I include it just in case.
- vk::ShaderModule lanternmissSM = nvvk::createShaderModule(
- m_device, nvh::loadFile("shaders/lanternShadow.rmiss.spv", true, paths, true));
-````
-
-and add this code for loading the last 2 closest hit shaders after loading
-`lantern.rchit.spv`:
-
-```` C
- // OBJ Lantern Shadow Ray Hit Group
- vk::ShaderModule lanternShadowObjChitSM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/lanternShadowObj.rchit.spv", true, paths, true));
-
- vk::RayTracingShaderGroupCreateInfoKHR lanternShadowObjHg{
- vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
- lanternShadowObjHg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, lanternShadowObjChitSM, "main"});
- m_rtShaderGroups.push_back(lanternShadowObjHg);
-
- // Lantern Lantern Shadow Ray Hit Group
- vk::ShaderModule lanternShadowLanternChitSM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/lanternShadowLantern.rchit.spv", true, paths, true));
-
- vk::RayTracingShaderGroupCreateInfoKHR lanternShadowLanternHg{
- vk::RayTracingShaderGroupTypeKHR::eTrianglesHitGroup,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR,
- VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR};
- lanternShadowLanternHg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, lanternShadowLanternChitSM, "main"});
- m_rtShaderGroups.push_back(lanternShadowLanternHg);
-````
-
-We need to destroy the added shader modules at the end of the function.
-
-```` C
- m_device.destroy(shadowmissSM);
- // ...
- m_device.destroy(lanternShadowObjChitSM);
- m_device.destroy(lanternShadowLanternChitSM);
-````
-
-Through all this, we still load shader stages in the same order as they will appear
-in the SBT in order to keep things simple (note `stages.size()`). Add a comment
-at the top of this function to help us keep track of all the new shaders.
-
-```` C
-// Shader list:
-//
-// 0 ====== Ray Generation Shaders =====================================================
-//
-// Raygen shader: Ray generation shader. Casts primary rays from camera to scene.
-//
-// 1 ====== Miss Shaders ===============================================================
-//
-// Miss shader 0: Miss shader when casting primary rays. Fill in clear color.
-//
-// 2 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-//
-// Miss shader 1: Miss shader when casting shadow rays towards main light.
-// Reports no shadow.
-//
-// 3 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-//
-// Miss shader 2: Miss shader when casting shadow rays towards a lantern.
-// Reports no lantern hit (-1).
-//
-// 4 ====== Hit Groups for Primary Rays (sbtRecordOffset=0) ============================
-//
-// chit shader 0: Closest hit shader for primary rays hitting OBJ instances
-// (hitGroupId=0). Casts shadow ray (to sky light or to lantern,
-// depending on pass number) and returns specular
-// and diffuse light to add to output image.
-//
-// 5 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-//
-// chit shader 1: Closest hit shader for primary rays hitting lantern instances
-// (hitGroupId=1). Returns color value to replace the current
-// image pixel color with (lanterns are self-illuminating).
-//
-// 6 - - - - Hit Groups for Lantern Shadow Rays (sbtRecordOffset=2) - - - - - - - - - - -
-//
-// chit shader 2: Closest hit shader for OBJ instances hit when casting shadow
-// rays to a lantern. Returns -1 to report that the shadow ray
-// failed to reach the targetted lantern.
-//
-// 7 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-//
-// chit shader 3: Closest hit shader for lantern instances hit when casting shadow
-// rays to a lantern. Returns the gl_CustomInstanceIndexEXT [lantern
-// number] of the lantern hit.
-//
-// 8 =====================================================================================
-````
-
-## Compute Lighting Intensity
-
-Because the lanterns have color, we have to replace the scalar `lightIntensity`
-in `raytrace.rchit` with an RGB `colorIntensity`.
-
-```` C
- // Vector toward the light
- vec3 L;
- vec3 colorIntensity = vec3(pushC.lightIntensity);
- float lightDistance = 100000.0;
-````
-
-Then, we have to check if we're in a lantern pass (`lanternPassNumber >= 0`).
-If so, look up the lantern location in the `LanternIndirectEntry` array,
-and compute the light direction and intensity based on that position.
-
-```` C
- // ray direction is towards lantern, if in lantern pass.
- if (pushC.lanternPassNumber >= 0)
- {
- LanternIndirectEntry lantern = lanterns.lanterns[pushC.lanternPassNumber];
- vec3 lDir = vec3(lantern.x, lantern.y, lantern.z) - worldPos;
- lightDistance = length(lDir);
- vec3 color = vec3(lantern.red, lantern.green, lantern.blue);
- // Lantern light decreases linearly. Not physically accurate, but looks good
- // and avoids a hard "edge" at the radius limit. Use a constant value
- // if lantern debug is enabled to clearly see the covered screen rectangle.
- float distanceFade =
- pushC.lanternDebug != 0
- ? 0.3
- : max(0, (lantern.radius - lightDistance) / lantern.radius);
- colorIntensity = color * lantern.brightness * distanceFade;
- L = normalize(lDir);
- }
-````
-
-otherwise, do the old lighting calculations, except we again have to
-replace `float lightIntensity` with `vec3 colorIntensity`.
-
-```` C
- // Non-lantern pass may have point light...
- else if(pushC.lightType == 0)
- {
- vec3 lDir = pushC.lightPosition - worldPos;
- lightDistance = length(lDir);
- colorIntensity = vec3(pushC.lightIntensity / (lightDistance * lightDistance));
- L = normalize(lDir);
- }
- else // or directional light.
- {
- L = normalize(pushC.lightPosition - vec3(0));
- }
-````
-
-!!! NOTE `lanternDebug`
- When `lanternDebug` is on, I disable diminishing lighting with distance, so
- that the light will reach the edge of the scissor box, making the scissor
- box easy to see. To toggle this variable, I declare `bool m_lanternDebug`
- in `hello_vulkan.h`, and allow ImGui to control it:
-
- ```` C
- void renderUI(HelloVulkan& helloVk)
- {
- ImGuiH::CameraWidget();
- if(ImGui::CollapsingHeader("Light"))
- {
- // ...
- ImGui::Checkbox("Lantern Debug", &helloVk.m_lanternDebug);
- }
- }
- ````
-
- Then, every frame I copy `m_lanternDebug` to the push constant. The reason
- I cannot directly modify the push constant through ImGui is that ImGui expects
- a `bool` (usually 8-bits) while Vulkan expects a 32-bit boolean.
-
-## Casting Lantern Shadow Rays
-
-Use an `if` to ensure the original shadow rays are cast only in the non-lantern pass.
-
-```` C
- // Ordinary shadow from the simple tutorial.
- if (pushC.lanternPassNumber < 0) {
- isShadowed = true;
- uint flags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT
- | gl_RayFlagsSkipClosestHitShaderEXT;
- traceRayEXT(topLevelAS, // acceleration structure
- flags, // rayFlags
- 0xFF, // cullMask
- 0, // sbtRecordOffset
- 0, // sbtRecordStride
- 1, // missIndex
- origin, // ray origin
- tMin, // ray min range
- rayDir, // ray direction
- tMax, // ray max range
- 1 // payload (location = 1)
- );
- }
-````
-
-Otherwise, we cast a ray towards a lantern. This ray is different in that
-
-* We actually need the closest hit shaders to run to return `hitLanternInstance`,
- so do not provide the flags
- ` gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsSkipClosestHitShaderEXT`.
-
-* Use miss shader 2, which we added earlier.
-
-* Pass 2 as `sbtRecordOffset`, so that the closest hit shaders we just added (number 2 and 3)
- are used when hitting OBJ instances (`hitGroupId=0`) and lantern instances (`hitGroupId=1`)
- respectively.
-
-The code is
-
-```` C
- // Lantern shadow ray. Cast a ray towards the lantern whose lighting is being
- // added this pass. Only the closest hit shader for lanterns will set
- // hitLanternInstance (payload 2) to non-negative value.
- else {
- // Skip ray if no light would be added anyway.
- if (colorIntensity == vec3(0)) {
- isShadowed = true;
- }
- else {
- uint flags = gl_RayFlagsOpaqueEXT;
- hitLanternInstance = -1;
- traceRayEXT(topLevelAS, // acceleration structure
- flags, // rayFlags
- 0xFF, // cullMask
- 2, // sbtRecordOffset : lantern shadow hit groups start at index 2.
- 0, // sbtRecordStride
- 2, // missIndex : lantern shadow miss shader is number 2.
- origin, // ray origin
- tMin, // ray min range
- rayDir, // ray direction
- tMax, // ray max range
- 2 // payload (location = 2)
- );
- // Did we hit the lantern we expected?
- isShadowed = (hitLanternInstance != pushC.lanternPassNumber);
- }
- }
-````
-
-Notice that we determine whether this lantern is shadowed at this pixel by
-checking if the hit lantern number matches the lantern whose light is being
-added this pass; again, this ensures lanterns correctly shadow each others' light.
-
-## Write Payload
-
-Replace `lightIntensity` with `colorIntensity` and ask the raygen shader
-for additive blending.
-
-```` C
- prd.hitValue = colorIntensity * (attenuation * (diffuse + specular));
- prd.additiveBlending = true;
-````
-
-# Trace Rays Indirect
-
-Everything is finally set up to actually run the extra lantern passes
-in `HelloVulkan::raytrace`. We've already dispatched the compute
-shader in an earlier section. After that, we can run the first raytrace
-pass. There are minimal changes from before, we just have to
-
-* Initialize the new push constant values (especially setting
- `lanternPassNumber=-1` to indicate this is not a lantern pass).
-
-```` C
- // Initialize push constant values
- m_rtPushConstants.clearColor = clearColor;
- m_rtPushConstants.lightPosition = m_pushConstant.lightPosition;
- m_rtPushConstants.lightIntensity = m_pushConstant.lightIntensity;
- m_rtPushConstants.lightType = m_pushConstant.lightType;
- m_rtPushConstants.lanternPassNumber = -1; // Global non-lantern pass
- m_rtPushConstants.screenX = m_size.width;
- m_rtPushConstants.screenY = m_size.height;
- m_rtPushConstants.lanternDebug = m_lanternDebug;
-````
-
-* Update the addresses of the raygen, miss, and hit group sections of the SBT
- to account for the added shaders.
-
-```` C
- using Stride = vk::StridedDeviceAddressRegionKHR;
- std::array strideAddresses{
- Stride{sbtAddress + 0u * groupSize, groupStride, groupSize * 1}, // raygen
- Stride{sbtAddress + 1u * groupSize, groupStride, groupSize * 3}, // miss
- Stride{sbtAddress + 4u * groupSize, groupStride, groupSize * 4}, // hit
- Stride{0u, 0u, 0u}}; // callable
-
- // First pass, illuminate scene with global light.
- cmdBuf.traceRaysKHR(
- &strideAddresses[0], &strideAddresses[1], //
- &strideAddresses[2], &strideAddresses[3], //
- m_size.width, m_size.height, 1);
-````
-
-After that, we can open a loop for performing all lantern passes.
-
-```` C
- // Lantern passes, ensure previous pass completed, then add light contribution from each lantern.
- for (int i = 0; i < static_cast(m_lanternCount); ++i)
- {
-````
-
-Because the additive blending in the shader requires read-modify-write operations,
-we need a barrier between every pass.
-
-```` C
- // Barrier to ensure previous pass finished.
- vk::Image offscreenImage{m_offscreenColor.image};
- vk::ImageSubresourceRange colorRange(
- vk::ImageAspectFlagBits::eColor, 0, VK_REMAINING_MIP_LEVELS, 0, VK_REMAINING_ARRAY_LAYERS
- );
- vk::ImageMemoryBarrier imageBarrier;
- imageBarrier.setOldLayout(vk::ImageLayout::eGeneral);
- imageBarrier.setNewLayout(vk::ImageLayout::eGeneral);
- imageBarrier.setSrcQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
- imageBarrier.setDstQueueFamilyIndex(VK_QUEUE_FAMILY_IGNORED);
- imageBarrier.setImage(offscreenImage);
- imageBarrier.setSubresourceRange(colorRange);
- imageBarrier.setSrcAccessMask(vk::AccessFlagBits::eShaderWrite);
- imageBarrier.setDstAccessMask(vk::AccessFlagBits::eShaderRead);
- cmdBuf.pipelineBarrier(
- vk::PipelineStageFlagBits::eRayTracingShaderKHR, //
- vk::PipelineStageFlagBits::eRayTracingShaderKHR, //
- vk::DependencyFlags(0), //
- {}, {}, {imageBarrier});
-````
-
-Then, we can pass the number of the lantern pass being performed (`i`), and look
-up the indirect parameters for that entry. Unlike draw and dispatch indirect, the
-indirect paramater location is passed as a raw device address, so we need to
-perform manual device pointer arithmetic to look up the $i^{th}$ entry of the
-`LanternIndirectEntry` array. Take advantage of the fact that `VkTraceRaysIndirectCommandKHR`
-is the first member of `LanternIndirectEntry`.
-
-```` C
- // Set lantern pass number.
- m_rtPushConstants.lanternPassNumber = i;
- cmdBuf.pushConstants(m_rtPipelineLayout,
- vk::ShaderStageFlagBits::eRaygenKHR
- | vk::ShaderStageFlagBits::eClosestHitKHR
- | vk::ShaderStageFlagBits::eMissKHR,
- 0, m_rtPushConstants);
-
- // Execute lantern pass.
- cmdBuf.traceRaysIndirectKHR(
- &strideAddresses[0], &strideAddresses[1], //
- &strideAddresses[2], &strideAddresses[3], //
- m_device.getBufferAddress({m_lanternIndirectBuffer.buffer}) + i * sizeof(LanternIndirectEntry));
- }
-````
-
-Everything should be in order now. We can see in this image that the cyan and purple lanterns
-are both shadowed by the doodad hanging off the side of the building, and the spikes on the
-roof cut shadows in the yellow lantern's light.
-
-
-
-Zoom out and enable the lantern debug checkbox to see the scissor rectangles.
-
-
-
-## Cleanup
-
-One last loose end, we have to clean up all the new resources is `HelloVulkan::destroyResources`.
-
-```` C
-// Destroying all allocations
-//
-void HelloVulkan::destroyResources()
-{
- // ...
-
- // #VKRay
- // ...
- m_device.destroy(m_lanternIndirectDescPool);
- m_device.destroy(m_lanternIndirectDescSetLayout);
- m_device.destroy(m_lanternIndirectCompPipeline);
- m_device.destroy(m_lanternIndirectCompPipelineLayout);
- m_alloc.destroy(m_lanternIndirectBuffer);
- m_alloc.destroy(m_lanternVertexBuffer);
- m_alloc.destroy(m_lanternIndexBuffer);
-}
-````
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_indirect_scissor](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_indirect_scissor)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_instances.md.html b/docs/vkrt_tuto_instances.md.html
deleted file mode 100644
index b4cc436..0000000
--- a/docs/vkrt_tuto_instances.md.html
+++ /dev/null
@@ -1,272 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Instances**
-
-Authors: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/), Neil Bickford
-
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-Ray tracing can easily handle having many object instances at once. For instance, a top level acceleration structure can
-have many different instances of a bottom level acceleration structure. However, when we have many different objects, we
-can run into problems with memory allocation. Many Vulkan implementations support no more than 4096 allocations, while
-our current application creates 4 allocations per object (Vertex, Index, and Material), then one for the BLAS. That
-means we are hitting the limit with just above 1000 objects.
-
-(insert setup.md.html here)
-
-# Many Instances
-
-First, let's look how the scene would look like when we have just a few objects, with many instances.
-
-In `main.cpp`, add the following includes:
-
-~~~~ C++
-#include
-~~~~
-
-Then replace the calls to `helloVk.loadModel` in `main()` by
-
-~~~~ C++
- // Creation of the example
- helloVk.loadModel(nvh::findFile("media/scenes/cube.obj", defaultSearchPaths));
- helloVk.loadModel(nvh::findFile("media/scenes/cube_multi.obj", defaultSearchPaths));
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
-
- std::random_device rd; // Will be used to obtain a seed for the random number engine
- std::mt19937 gen(rd()); // Standard mersenne_twister_engine seeded with rd()
- std::normal_distribution dis(1.0f, 1.0f);
- std::normal_distribution disn(0.05f, 0.05f);
-
- for(int n = 0; n < 2000; ++n)
- {
- HelloVulkan::ObjInstance inst;
- inst.objIndex = n % 2;
- inst.txtOffset = 0;
- float scale = fabsf(disn(gen));
- nvmath::mat4f mat =
- nvmath::translation_mat4(nvmath::vec3f{dis(gen), 2.0f + dis(gen), dis(gen)});
- mat = mat * nvmath::rotation_mat4_x(dis(gen));
- mat = mat * nvmath::scale_mat4(nvmath::vec3f(scale));
- inst.transform = mat;
- inst.transformIT = nvmath::transpose(nvmath::invert((inst.transform)));
- helloVk.m_objInstance.push_back(inst);
- }
-~~~~
-
-!!! Note:
- This will create 3 models (OBJ) and their instances, and then add 2000 instances
- distributed between green cubes and cubes with one color per face.
-
-# Many Objects
-
-Instead of creating many instances, create many objects.
-
-Remove the previous code and replace it with the following
-
-~~~~ C++
- // Creation of the example
- std::random_device rd; //Will be used to obtain a seed for the random number engine
- std::mt19937 gen(rd()); //Standard mersenne_twister_engine seeded with rd()
- std::normal_distribution dis(1.0f, 1.0f);
- std::normal_distribution disn(0.05f, 0.05f);
- for(int n = 0; n < 2000; ++n)
- {
- helloVk.loadModel(nvh::findFile("media/scenes/cube_multi.obj", defaultSearchPaths));
- HelloVulkan::ObjInstance& inst = helloVk.m_objInstance.back();
-
- float scale = fabsf(disn(gen));
- nvmath::mat4f mat =
- nvmath::translation_mat4(nvmath::vec3f{dis(gen), 2.0f + dis(gen), dis(gen)});
- mat = mat * nvmath::rotation_mat4_x(dis(gen));
- mat = mat * nvmath::scale_mat4(nvmath::vec3f(scale));
- inst.transform = mat;
- inst.transformIT = nvmath::transpose(nvmath::invert((inst.transform)));
- }
-
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
-~~~~
-
-The example might still work, but the console will print the following error after loading 1363 objects. All other objects allocated after the 1363rd will fail to be displayed.
-!!! Error
- Error: VUID_Undefined
- Number of currently valid memory objects is not less than the maximum allowed (4096).
- - Object[0] - Type Device
-
-!!! Note:
- This is the best case; the application can run out of memory and crash if substantially more objects are created (e.g. 20,000)
-
-# Device Memory Allocator (DMA)
-
-It is possible to use a memory allocator to fix this issue.
-
-## `hello_vulkan.h`
-
-In `hello_vulkan.h`, add the following defines at the top of the file to indicate which allocator to use
-
-~~~~ C++
-// #VKRay
-//#define ALLOC_DEDICATED
-#define ALLOC_DMA
-~~~~
-
-
-Replace the definition of buffers and textures and include the right allocator.
-
-~~~~ C++
-#if defined(ALLOC_DEDICATED)
-#include "nvvk/allocator_dedicated_vk.hpp"
-using nvvkBuffer = nvvk::BufferDedicated;
-using nvvkTexture = nvvk::TextureDedicated;
-#elif defined(ALLOC_DMA)
-#include "nvvk/allocator_dma_vk.hpp"
-using nvvkBuffer = nvvk::BufferDma;
-using nvvkTexture = nvvk::TextureDma;
-#endif
-~~~~
-
-And do the same for the allocator
-
-~~~~ C++
-#if defined(ALLOC_DEDICATED)
- nvvk::AllocatorDedicated m_alloc; // Allocator for buffer, images, acceleration structures
-#elif defined(ALLOC_DMA)
- nvvk::AllocatorDma m_alloc; // Allocator for buffer, images, acceleration structures
- nvvk::DeviceMemoryAllocator m_memAllocator;
- nvvk::StagingMemoryManagerDma m_staging;
-#endif
-~~~~
-
-## `hello_vulkan.cpp`
-
-In the source file there are also a few changes to make.
-
-DMA needs to be initialized, which will be done in the `setup()` function:
-
-~~~~ C++
-#if defined(ALLOC_DEDICATED)
- m_alloc.init(device, physicalDevice);
-#elif defined(ALLOC_DMA)
- m_memAllocator.init(device, physicalDevice);
- m_memAllocator.setAllocateFlags(VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR, true);
- m_staging.init(m_memAllocator);
- m_alloc.init(device, m_memAllocator, m_staging);
-#endif
-~~~~
-
-When using DMA, memory buffer mapping is done through the DMA interface (instead of the VKDevice).
-Therefore, change the lines at the end of `updateUniformBuffer()` to use the common allocator interface.
-
-~~~~ C++
-void* data = m_alloc.map(m_cameraMat);
-memcpy(data, &ubo, sizeof(ubo));
-m_alloc.unmap(m_cameraMat);
-~~~~
-
-The RaytracerBuilder was made to allow various allocators, therefore nothing to change in the call to `m_rtBuilder.setup()`
-
-
-## Destruction
-
-The VMA allocator need to be released in `HelloVulkan::destroyResources()` after the last `m_alloc.destroy`.
-
-~~~~ C++
-#if defined(ALLOC_DMA)
- m_dmaAllocator.deinit();
-#endif
-~~~~
-
-# Result
-
-Instead of thousands of allocations, our example will have only 14 allocations. Note that some of these allocations are allocated by Dear ImGui, and not by DMA. These are the 14 objects with blue borders below:
-
-
-
-Finally, here is the Vulkan Device Memory view from Nsight Graphics:
-
-
-
-
-# VMA: Vulkan Memory Allocator
-
-We can also modify the code to use the [Vulkan Memory Allocator](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator) from AMD.
-
-Download [vk_mem_alloc.h](https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator/blob/master/src/vk_mem_alloc.h) from GitHub and add this to the `shared_sources` folder.
-
-There is already a variation of the allocator for VMA, which is located under [nvpro-samples](https://github.com/nvpro-samples/shared_sources/tree/master/nvvk). This allocator has the same simple interface as the `AllocatorDedicated` class in `allocator_dedicated_vkpp.hpp`, but will use VMA for memory management.
-
-VMA might use dedicated memory, which we do, so you need to add the following extension to the
-creation of the context in `main.cpp`.
-
-~~~~ C++
- contextInfo.addDeviceExtension(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME);
-~~~~
-
-## hello_vulkan.h
-
-Follow the changes done before and add the following
-
-~~~~ C++
-#define ALLOC_VMA
-~~~~
-
-~~~~ C++
-#elif defined(ALLOC_VMA)
-#include "nvvk/allocator_vma_vk.hpp"
-using nvvkBuffer = nvvk::BufferVma;
-using nvvkTexture = nvvk::TextureVma;
-~~~~
-
-~~~~ C++
-#elif defined(ALLOC_VMA)
- nvvk::AllocatorVma m_alloc; // Allocator for buffer, images, acceleration structures
- nvvk::StagingMemoryManagerVma m_staging;
- VmaAllocator m_memAllocator;
-~~~~
-
-
-## hello_vulkan.cpp
-First, the following should only be defined once in the entire program, and it should be defined before `#include "hello_vulkan.h"`:
-
-~~~~ C++
-#define VMA_IMPLEMENTATION
-~~~~
-
-In `setup()`
-
-~~~~ C++
-#elif defined(ALLOC_VMA)
- VmaAllocatorCreateInfo allocatorInfo = {};
- allocatorInfo.instance = instance;
- allocatorInfo.physicalDevice = physicalDevice;
- allocatorInfo.device = device;
- allocatorInfo.flags = VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
- vmaCreateAllocator(&allocatorInfo, &m_memAllocator);
- m_staging.init(device, physicalDevice, m_memAllocator);
- m_alloc.init(device, m_memAllocator, m_staging);
-~~~~
-
-In `destroyResources()`
-
-~~~~ C++
-#elif defined(ALLOC_VMA)
- vmaDestroyAllocator(m_vmaAllocator);
-~~~~
-
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_instances](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_instances)
-
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_intersection.md.html b/docs/vkrt_tuto_intersection.md.html
deleted file mode 100644
index 2ba73f5..0000000
--- a/docs/vkrt_tuto_intersection.md.html
+++ /dev/null
@@ -1,564 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Intersection Shader**
-
-Author: [Martin-Karl Lefrançois](https://devblogs.nvidia.com/author/mlefrancois/)
-
-
-
-
-# Introduction
-
-This tutorial chapter shows how to use intersection shader and render different primitives with different materials.
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.htm).
-
-(insert setup.md.html here)
-
-## High Level Implementation
-
-On a high level view, we will
-
-* Add 2.000.000 axis aligned bounding boxes in a BLAS
-* 2 materials will be added
-* Every second intersected object will be a sphere or a cube and will use one of the two material.
-
-To do this, we will need to:
-
-* Add an intersection shader (.rint)
-* Add a new closest hit shader (.chit)
-* Create `VkAccelerationStructureGeometryKHR` from `VkAccelerationStructureGeometryAabbsDataKHR`
-
-## Creating all spheres
-
-In the HelloVulkan class, we will add the structures we will need. First the structure that defines a sphere.
-
-~~~~ C++
- struct Sphere
- {
- nvmath::vec3f center;
- float radius;
- };
-~~~~
-
-Then we need the Aabb structure holding all the spheres, but also used for the creation of the BLAS (`VK_GEOMETRY_TYPE_AABBS_KHR`).
-
-~~~~ C++
- struct Aabb
- {
- nvmath::vec3f minimum;
- nvmath::vec3f maximum;
- };
-~~~~
-
-All the information will need to be hold in buffers, which will be available to the shaders.
-
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_jitter_cam.md.html b/docs/vkrt_tuto_jitter_cam.md.html
deleted file mode 100644
index 873873a..0000000
--- a/docs/vkrt_tuto_jitter_cam.md.html
+++ /dev/null
@@ -1,299 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Antialiasing**
-
-
-
-
-# Introduction
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-In this extension, we will implement antialiasing by jittering the offset of each ray for each pixel over time, instead of always shooting each ray from the middle of its pixel.
-
-(insert setup.md.html here)
-
-
-## Random Functions
-
-We will use some simple functions for random number generation, which suffice for this example.
-
-Create a new shader file `random.glsl` with the following code. Add it to the `shaders` directory and rerun CMake, and include this new file in `raytrace.rgen`:
-
-~~~~ C++
-// Generate a random unsigned int from two unsigned int values, using 16 pairs
-// of rounds of the Tiny Encryption Algorithm. See Zafar, Olano, and Curtis,
-// "GPU Random Numbers via the Tiny Encryption Algorithm"
-uint tea(uint val0, uint val1)
-{
- uint v0 = val0;
- uint v1 = val1;
- uint s0 = 0;
-
- for(uint n = 0; n < 16; n++)
- {
- s0 += 0x9e3779b9;
- v0 += ((v1 << 4) + 0xa341316c) ^ (v1 + s0) ^ ((v1 >> 5) + 0xc8013ea4);
- v1 += ((v0 << 4) + 0xad90777d) ^ (v0 + s0) ^ ((v0 >> 5) + 0x7e95761e);
- }
-
- return v0;
-}
-
-// Generate a random unsigned int in [0, 2^24) given the previous RNG state
-// using the Numerical Recipes linear congruential generator
-uint lcg(inout uint prev)
-{
- uint LCG_A = 1664525u;
- uint LCG_C = 1013904223u;
- prev = (LCG_A * prev + LCG_C);
- return prev & 0x00FFFFFF;
-}
-
-// Generate a random float in [0, 1) given the previous RNG state
-float rnd(inout uint prev)
-{
- return (float(lcg(prev)) / float(0x01000000));
-}
-~~~~
-
-## Frame Number
-
-Since our jittered samples will be accumulated across frames, we need to know which frame we are currently rendering. A frame number of 0 will indicate a new frame, and we will accumulate the data for larger frame numbers.
-
-Note that the uniform image is read/write, which makes it possible to accumulate previous frames.
-
-In `raytrace.rgen`, add the push constant block from `raytrace.rchit`, adding a new `frame` member:
-
-~~~~ C++
-layout(push_constant) uniform Constants
-{
- vec4 clearColor;
- vec3 lightPosition;
- float lightIntensity;
- int lightType;
- int frame;
-}
-pushC;
-~~~~
-
-Also add this frame member to the `RtPushConstant` struct in `hello_vulkan.h`:
-
-~~~~ C++
- struct RtPushConstant
- {
- nvmath::vec4f clearColor;
- nvmath::vec3f lightPosition;
- float lightIntensity;
- int lightType;
- int frame{0};
- } m_rtPushConstants;
-~~~~
-
-## Random and Jitter
-
-In `raytrace.rgen`, at the beginning of `main()`, initialize the random seed:
-
-~~~~ C++
- // Initialize the random number
- uint seed = tea(gl_LaunchIDEXT.y * gl_LaunchSizeEXT.x + gl_LaunchIDEXT.x, pushC.frame);
-~~~~
-
-Then we need two random numbers to vary the X and Y inside the pixel, except for frame 0, where we always shoot
-in the center.
-
-~~~~ C++
-float r1 = rnd(seed);
-float r2 = rnd(seed);
-// Subpixel jitter: send the ray through a different position inside the pixel
-// each time, to provide antialiasing.
-vec2 subpixel_jitter = pushC.frame == 0 ? vec2(0.5f, 0.5f) : vec2(r1, r2);
-~~~~
-
-Now we only need to change how we compute the pixel center:
-
-~~~~ C++
-const vec2 pixelCenter = vec2(gl_LaunchIDEXT.xy) + subpixel_jitter;
-~~~~
-
-## Storing or Updating
-
-At the end of `main()`, if the frame number is equal to 0, we write directly to the image.
-Otherwise, we combine the new image with the previous `frame` frames.
-
-~~~~ C++
- // Do accumulation over time
- if(pushC.frame > 0)
- {
- float a = 1.0f / float(pushC.frame + 1);
- vec3 old_color = imageLoad(image, ivec2(gl_LaunchIDEXT.xy)).xyz;
- imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(mix(old_color, prd.hitValue, a), 1.f));
- }
- else
- {
- // First frame, replace the value in the buffer
- imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(prd.hitValue, 1.f));
- }
-~~~~
-
-# Application Frame Update
-
-We need to increment the current rendering frame, but we also need to reset it when something in the
-scene is changing.
-
-Add two new functions to the `HelloVulkan` class:
-
-~~~~ C++
- void resetFrame();
- void updateFrame();
-~~~~
-
-The implementation of `updateFrame` resets the frame counter if the camera has changed; otherwise, it increments the frame counter.
-
-~~~~ C++
-//--------------------------------------------------------------------------------------------------
-// If the camera matrix has changed, resets the frame.
-// otherwise, increments frame.
-//
-void HelloVulkan::updateFrame()
-{
- static nvmath::mat4f refCamMatrix;
-
- auto& m = CameraManip.getMatrix();
- if(memcmp(&refCamMatrix.a00, &m.a00, sizeof(nvmath::mat4f)) != 0)
- {
- resetFrame();
- refCamMatrix = m;
- }
- m_rtPushConstants.frame++;
-}
-~~~~
-
-Since `resetFrame` will be called before `updateFrame` increments the frame counter, `resetFrame` will set the frame counter to -1:
-
-~~~~ C++
-void HelloVulkan::resetFrame()
-{
- m_rtPushConstants.frame = -1;
-}
-~~~~
-
-At the begining of `HelloVulkan::raytrace`, call
-
-~~~~ C++
- updateFrame();
-~~~~
-
-The application will now antialias the image when ray tracing is enabled.
-
-Adding `resetFrame()` in `HelloVulkan::onResize()` will also take care of clearing the buffer while resizing the window.
-
-
-
-## Resetting Frame on UI Change
-
-The frame number should also be reset when any parts of the scene change, such as the light direction or the background color. In `renderUI()` in `main.cpp`, check for UI changes and reset the frame number when they happen:
-
-~~~~ C++
-void renderUI(HelloVulkan& helloVk)
-{
- static int item = 1;
- bool changed = false;
- if(ImGui::Combo("Up Vector", &item, "X\0Y\0Z\0\0"))
- {
- nvmath::vec3f pos, eye, up;
- CameraManip.getLookat(pos, eye, up);
- up = nvmath::vec3f(item == 0, item == 1, item == 2);
- CameraManip.setLookat(pos, eye, up);
- changed = true;
- }
- changed |=
- ImGui::SliderFloat3("Light Position", &helloVk.m_pushConstant.lightPosition.x, -20.f, 20.f);
- changed |=
- ImGui::SliderFloat("Light Intensity", &helloVk.m_pushConstant.lightIntensity, 0.f, 100.f);
- changed |= ImGui::RadioButton("Point", &helloVk.m_pushConstant.lightType, 0);
- ImGui::SameLine();
- changed |= ImGui::RadioButton("Infinite", &helloVk.m_pushConstant.lightType, 1);
- if(changed)
- helloVk.resetFrame();
-}
-~~~~
-
-We also need to check for UI changes inside the main loop inside `main()`:
-
-~~~~ C++
- bool changed = false;
- // Edit 3 floats representing a color
- changed |= ImGui::ColorEdit3("Clear color", reinterpret_cast(&clearColor));
- // Switch between raster and ray tracing
- changed |= ImGui::Checkbox("Ray Tracer mode", &useRaytracer);
- if(changed)
- helloVk.resetFrame();
-~~~~
-
-# Quality
-
-After enough samples, the quality of the rendering will be sufficiently high that it might make sense to avoid accumulating further images.
-
-Add a member variable to `HelloVulkan`
-
-~~~~ C++
-int m_maxFrames{100};
-~~~~
-
-and also add a way to control it in `renderUI()`, making sure that `m_maxFrames` cannot be set below 1:
-
-~~~~ C++
-changed |= ImGui::InputInt("Max Frames", &helloVk.m_maxFrames);
-helloVk.m_maxFrames = std::max(helloVk.m_maxFrames, 1);
-~~~~
-
-Then in `raytrace()`, immediately after the call to `updateFrame()`, return if the current frame has exceeded the max frame.
-
-~~~~ C++
- if(m_rtPushConstants.frame >= m_maxFrames)
- return;
-~~~~
-
-Since the output image won't be modified by the ray tracer, we will simply display the last good image, reducing GPU usage when the target quality has been reached.
-
-# More Samples in RayGen
-
-To improve efficiency, we can perform multiple samples directly in the ray generation shader. This will be faster than calling `raytrace()` the equivalent number of times.
-
-To do this, add a constant to `raytrace.rgen` (this could alternatively be added to the push constant block and controlled by the application):
-
-~~~~ C++
-const int NBSAMPLES = 10;
-~~~~
-
-In `main()`, after initializing the random number seed, create a loop that encloses the lines from the generation of `r1` and `r2` to the `traceRayEXT` call, and accumulates the colors returned by `traceRayEXT`. At the end of the loop, divide by the number of samples that were taken.
-
-~~~~ C++
- vec3 hitValues = vec3(0);
-
- for(int smpl = 0; smpl < NBSAMPLES; smpl++)
- {
- float r1 = rnd(seed);
- float r2 = rnd(seed);
- // ...
- // TraceRayEXT( ... );
- hitValues += prd.hitValue;
- }
- prd.hitValue = hitValues / NBSAMPLES;
-~~~~
-
-For a given value of `m_maxFrames` and `NBSAMPLE`, the image this converges to will have `m_maxFrames * NBSAMPLE` antialiasing samples. For instance, if `m_maxFrames = 10` and `NBSAMPLE = 10`, this will be equivalent in quality to an image using `m_maxFrames = 100` and `NBSAMPLE = 1`. However, using `NBSAMPLE=10` in the ray generation shader will be faster than calling `raytrace()` with `NBSAMPLE=1` 10 times in a row.
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_jitter_cam](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_jitter_cam)
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_manyhits.md.html b/docs/vkrt_tuto_manyhits.md.html
deleted file mode 100644
index 3e52e1d..0000000
--- a/docs/vkrt_tuto_manyhits.md.html
+++ /dev/null
@@ -1,360 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Multiple Closest Hit Shaders**
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-The ray tracing tutorial only uses one closest hit shader, but it is also possible to have multiple closest hit shaders.
-For example, this could be used to give different models different shaders, or to use a less complex shader when tracing
-reflections.
-
-(insert setup.md.html here)
-
-# Setting up the Scene
-
-For this example, we will load the `wuson` model and create another translated instance of it.
-
-Then you can change the `helloVk.loadModel` calls to the following:
-
-~~~~ C++
- // Creation of the example
- helloVk.loadModel(nvh::findFile("media/scenes/wuson.obj", defaultSearchPaths),
- nvmath::translation_mat4(nvmath::vec3f(-1, 0, 0)));
- HelloVulkan::ObjInstance inst;
- inst.objIndex = 0;
- inst.transform = nvmath::translation_mat4(nvmath::vec3f(1, 0, 0));
- inst.transformIT = nvmath::transpose(nvmath::invert(inst.transform));
- helloVk.m_objInstance.push_back(inst);
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths));
-~~~~
-
-# Adding a new Closest Hit Shader
-
-We will need to create a new closest hit shader (CHIT), to add it to the raytracing pipeline, and to indicate which instance will use this shader.
-
-## `raytrace2.rchit`
-
-We can make a very simple shader to differentiate this closest hit shader from the other one.
-As an example, create a new file called `raytrace2.rchit`, and add it to Visual Studio's `shaders` filter with the other shaders.
-
-~~~~ C++
-#version 460
-#extension GL_EXT_ray_tracing : require
-#extension GL_GOOGLE_include_directive : enable
-
-#include "raycommon.glsl"
-
-layout(location = 0) rayPayloadInEXT hitPayload prd;
-
-void main()
-{
- prd.hitValue = vec3(1,0,0);
-}
-~~~~
-
-## `createRtPipeline`
-
-This new shader needs to be added to the raytracing pipeline. So, in `createRtPipeline` in `hello_vulkan.cpp`, load the new closest hit shader immediately after loading the first one.
-
-~~~~ C++
- vk::ShaderModule chit2SM =
- nvvk::createShaderModule(m_device, //
- nvh::loadFile("shaders/raytrace2.rchit.spv", true, paths));
-~~~~
-
-Then add a new hit group group immediately after adding the first hit group:
-
-~~~~ C++
- // Second group
- hg.setClosestHitShader(static_cast(stages.size()));
- stages.push_back({{}, vk::ShaderStageFlagBits::eClosestHitKHR, chit2SM, "main"});
- m_rtShaderGroups.push_back(hg);
-~~~~
-
-## `raytrace.rgen`
-
-As a test, you can try changing the `sbtRecordOffset` parameter of the `traceRayEXT` call in `raytrace.rgen`.
-If you set the offset to `1`, then all ray hits will use the new CHIT, and the raytraced output should look like the image below:
-
-
-
-!!! Warning
- After testing this out, make sure to revert this change in `raytrace.rgen` before continuing.
-
-## `hello_vulkan.h`
-
-In the `ObjInstance` structure, we will add a new member variable that specifies which hit shader the instance will use:
-
-~~~~ C++
-uint32_t hitgroup{0}; // Hit group of the instance
-~~~~
-
-This change also needs to be reflected in the `sceneDesc` structure in `wavefront.glsl`:
-
-~~~~ C++
-struct sceneDesc
-{
- int objId;
- int txtOffset;
- mat4 transfo;
- mat4 transfoIT;
- int hitGroup;
-};
-~~~~
-
-!!! Warning:
- The solution will not automatically recompile the shaders after this change to `wavefront.glsl`; instead, you will need to recompile all of the SPIR-V shaders.
-
-## `hello_vulkan.cpp`
-
-Finally, we need to tell the top-level acceleration structure which hit group to use for each instance. In `createTopLevelAS()` in `hello_vulkan.cpp`, change the line setting `rayInst.hitGroupId` to
-
-~~~~ C++
-rayInst.hitGroupId = m_objInstance[i].hitgroup;
-~~~~
-
-## Choosing the Hit shader
-
-Back in `main.cpp`, after loading the scene's models, we can now have both `wuson` models use the new CHIT by adding the following:
-
-~~~~ C++
- helloVk.m_objInstance[0].hitgroup = 1;
- helloVk.m_objInstance[1].hitgroup = 1;
-~~~~
-
-
-
-# Shader Record Data `shaderRecordKHR`
-
-When creating the [Shader Binding Table](https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/chap33.html#shader-binding-table), see previous, each entry in the table consists of a handle referring to the shader that it invokes. We have packed all data to the size of `shaderGroupHandleSize`, but each entry could be made larger, to store data that can later be referenced by a `shaderRecordKHR` block in the shader.
-
-This information can be used to pass extra information to a shader, for each entry in the SBT.
-
-!!! Note: Note
- Since each entry in an SBT group must have the same size, each entry of the group has to have enough space to accommodate the largest element in the entire group.
-
-The following diagram represents our current SBT, with the addition of some data to `HitGroup1`. As mentioned in the **note**, even if
-`HitGroup0` doesn't have any shader record data, it still needs to have the same size as `HitGroup1`.
-
-**************************
-*+-----------+----------+*
-*| RayGen | Handle 0 |*
-*+-----------+----------+*
-*| Miss | Handle 1 |*
-*+-----------+----------+*
-*| Miss | Handle 2 |*
-*+-----------+----------+*
-*| HitGroup0 | Handle 3 |*
-*| | -Empty- |*
-*+-----------+----------+*
-*| HitGroup1 | Handle 4 |*
-*| | Data 0 |*
-*+-----------+----------+*
-**************************
-
-## `hello_vulkan.h`
-
-In the HelloVulkan class, we will add a structure to hold the hit group data.
-
-
-
-## `raytrace2.rchit`
-
-In the closest hit shader, we can retrieve the shader record using the `layout(shaderRecordEXT)` descriptor
-
-~~~~ C++
-layout(shaderRecordEXT) buffer sr_ { vec4 c; } shaderRec;
-~~~~
-
-and use this information to return the color:
-
-~~~~ C++
-void main()
-{
- prd.hitValue = shaderRec.c.rgb;
-}
-~~~~
-
-!!! Note
- Adding a new shader requires to rerun CMake to added to the project compilation system.
-
-
-## `main.cpp`
-
-In `main`, after we set which hit group an instance will use, we can add the data we want to set through the shader record.
-
-~~~~ C++
- helloVk.m_hitShaderRecord.resize(1);
- helloVk.m_hitShaderRecord[0].color = nvmath::vec4f(1, 1, 0, 0); // Yellow
-~~~~
-
-## `HelloVulkan::createRtShaderBindingTable`
-
-Since we are no longer compacting all handles in a continuous buffer, we need to fill the SBT as described above.
-
-After retrieving the handles of all 5 groups (raygen, miss, miss shadow, hit0, and hit1)
-using `getRayTracingShaderGroupHandlesKHR`, store the pointers to easily retrieve them.
-
-~~~~ C++
- // Retrieve the handle pointers
- std::vector handles(groupCount);
- for(uint32_t i = 0; i < groupCount; i++)
- {
- handles[i] = &shaderHandleStorage[i * groupHandleSize];
- }
-~~~~
-
-The size of each group can be described as follows:
-
-~~~~ C++
- // Sizes
- uint32_t rayGenSize = groupSizeAligned;
- uint32_t missSize = groupSizeAligned;
- uint32_t hitSize =
- nvh::align_up(groupHandleSize + static_cast(sizeof(HitRecordBuffer)), groupSizeAligned);
- uint32_t newSbtSize = rayGenSize + 2 * missSize + 3 * hitSize;
-~~~~
-
-Then write the new SBT like this, where only Hit 1 has extra data.
-
-~~~~ C++
- std::vector sbtBuffer(newSbtSize);
- {
- uint8_t* pBuffer = sbtBuffer.data();
-
- memcpy(pBuffer, handles[0], groupHandleSize); // Raygen
- pBuffer += rayGenSize;
- memcpy(pBuffer, handles[1], groupHandleSize); // Miss 0
- pBuffer += missSize;
- memcpy(pBuffer, handles[2], groupHandleSize); // Miss 1
- pBuffer += missSize;
-
- uint8_t* pHitBuffer = pBuffer;
- memcpy(pHitBuffer, handles[3], groupHandleSize); // Hit 0
- // No data
- pBuffer += hitSize;
-
- pHitBuffer = pBuffer;
- memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 1
- pHitBuffer += groupHandleSize;
- memcpy(pHitBuffer, &m_hitShaderRecord[0], sizeof(HitRecordBuffer)); // Hit 1 data
- pBuffer += hitSize;
- }
-~~~~
-
-Then change the call to `m_alloc.createBuffer` to create the SBT buffer from `sbtBuffer`:
-
-~~~~ C++
- m_rtSBTBuffer = m_alloc.createBuffer(cmdBuf, sbtBuffer, vk::BufferUsageFlagBits::eRayTracingKHR);
-~~~~
-
-
-## `raytrace`
-
-Finally, since the size of the hit group is now larger than just the handle,
-we need to set the new value of the hit group stride in `HelloVulkan::raytrace`.
-
-~~~~ C++
-vk::DeviceSize hitGroupSize =
- nvh::align_up(m_rtProperties.shaderGroupHandleSize + sizeof(HitRecordBuffer),
- m_rtProperties.shaderGroupBaseAlignment);
-
-
-std::array strideAddresses{
- Stride{sbtAddress + 0u * groupSize, groupStride, groupSize * 1}, // raygen
- Stride{sbtAddress + 1u * groupSize, groupStride, groupSize * 2}, // miss
- Stride{sbtAddress + 3u * groupSize, hitGroupSize, hitGroupSize * 3}, // hit
- Stride{0u, 0u, 0u}}; // callable
-~~~~
-
-!!! Note:
- The result should now show both `wuson` models with a yellow color.
-
-
-
-# Extending Hit
-
-The SBT can be larger than the number of shading models, which could then be used to have one shader per instance with its own data.
-For some applications, instead of retrieving the material information as in the main tutorial using a storage buffer and indexing
-into it using the `gl_InstanceCustomIndexEXT`, it is possible to set all of the material information in the SBT.
-
-The following modification will add another entry to the SBT with a different color per instance. The new SBT hit group (2) will use the same CHIT handle (4) as hit group 1.
-
-**************************
-*+-----------+----------+*
-*| RayGen | Handle 0 |*
-*+-----------+----------+*
-*| Miss | Handle 1 |*
-*+-----------+----------+*
-*| Miss | Handle 2 |*
-*+-----------+----------+*
-*| HitGroup0 | Handle 3 |*
-*| | -Empty- |*
-*+-----------+----------+*
-*| HitGroup1 | Handle 4 |*
-*| | Data 0 |*
-*+-----------+----------+*
-*| HitGroup2 | Handle 4 |*
-*| | Data 1 |*
-*+-----------+----------+*
-**************************
-
-## `main.cpp`
-
-In the description of the scene in `main`, we will tell the `wuson` models to use hit groups 1 and 2 respectively, and to have different colors.
-
-~~~~ C++
- helloVk.m_objInstance[0].hitgroup = 1;
- helloVk.m_objInstance[1].hitgroup = 2;
- helloVk.m_hitShaderRecord.resize(2);
- helloVk.m_hitShaderRecord[0].color = nvmath::vec4f(0, 1, 0, 0); // Green
- helloVk.m_hitShaderRecord[1].color = nvmath::vec4f(0, 1, 1, 0); // Cyan
-~~~~
-
-## `createRtShaderBindingTable`
-
-The size of the SBT will now account for its 3 hit groups:
-
-~~~~ C++
- uint32_t newSbtSize = rayGenSize + 2 * missSize + 3 * hitSize;
-~~~~
-
-Finally, we need to add the new entry as well at the end of the buffer, reusing the handle of the second Hit Group and setting a different color.
-
-~~~~ C++
- pHitBuffer = pBuffer;
- memcpy(pHitBuffer, handles[4], groupHandleSize); // Hit 2
- pHitBuffer += groupHandleSize;
- memcpy(pHitBuffer, &m_hitShaderRecord[1], sizeof(HitRecordBuffer)); // Hit 2 data
- pBuffer += hitSize;
-~~~~
-
-!!! Warning
- Adding entries like this can be error-prone and inconvenient for decent
- scene sizes. Instead, it is recommended to wrap the storage of handles, data,
- and size per group in a SBT utility to handle this automatically.
-
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_manyhits](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_manyhits)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_rayquery.md.html b/docs/vkrt_tuto_rayquery.md.html
deleted file mode 100644
index c0086f0..0000000
--- a/docs/vkrt_tuto_rayquery.md.html
+++ /dev/null
@@ -1,137 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Ray Query**
-
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-(insert setup.md.html here)
-
-# Ray Query
-
-This extension is allowing to execute ray intersection queries in any shader stages. In this example, we will add
-ray queries [(GLSL_EXT_ray_query)](https://github.com/KhronosGroup/GLSL/blob/master/extensions/ext/GLSL_EXT_ray_query.txt) to the fragment shader to cast shadow rays.
-
-In the contrary to all other examples, with this one, we are removing code. There are no need to have a SBT and a raytracing pipeline, the only thing that
-will matter, is the creation of the acceleration structure.
-
-Starting from the end of the tutorial, [ray_tracing__simple](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing__simple) we will remove
-all functions that were dedicated to ray tracing and keep only the construction of the BLAS and TLAS.
-
-# Cleanup
-
-First, let's remove all extra code
-
-## hello_vulkan (header)
-
-Remove most functions and members to keep only what is need to create the acceleration structure:
-
-~~~~ C++
-// #VKRay
-void initRayTracing();
-nvvk::RaytracingBuilderKHR::Blas objectToVkGeometryKHR(const ObjModel& model);
-void createBottomLevelAS();
-void createTopLevelAS();
-
-vk::PhysicalDeviceRayTracingPropertiesKHR m_rtProperties;
-nvvk::RaytracingBuilderKHR m_rtBuilder;
-~~~~
-
-## hello_vulkan (source)
-
-From the source code, remove the code for all functions that was previously removed.
-
-## Shaders
-
-You can safely remove all raytrace.* shaders
-
-
-# Support for Fragment shader
-
-In `HelloVulkan::createDescriptorSetLayout`, add the acceleration structure to the description layout.
-
-~~~~ C++
-// The top level acceleration structure
-m_descSetLayoutBind.emplace_back( //
- vkDS(7, vkDT::eAccelerationStructureKHR, 1, vkSS::eFragment));
-~~~~
-
-In `HelloVulkan::updateDescriptorSet`, write the value to the descriptor set.
-
-~~~~ C++
- vk::AccelerationStructureKHR tlas = m_rtBuilder.getAccelerationStructure();
- vk::WriteDescriptorSetAccelerationStructureKHR descASInfo;
- descASInfo.setAccelerationStructureCount(1);
- descASInfo.setPAccelerationStructures(&tlas);
- writes.emplace_back(m_descSetLayoutBind.makeWrite(m_descSet, 7, descASInfo));
-~~~~
-
-
-## Shader
-
-The last modification is in the fragment shader, where we will add the ray intersection query to trace shadow rays.
-
-First, the version has bumpped to 460
-
-~~~~ C++
-#version 460
-~~~~
-
-Then we need to add new extensions
-
-~~~~ C++
-#extension GL_EXT_ray_tracing : enable
-#extension GL_EXT_ray_query : enable
-~~~~
-
-We have to add the layout to access the top level acceleration structure.
-
-~~~~ C++
-layout(binding = 7, set = 0) uniform accelerationStructureEXT topLevelAS;
-~~~~
-
-
-Ad the end of the shader, add the following code to initiate the ray query. As we are only interested to know if the ray
-has hit something, we can keep the minimal.
-
-~~~~ C++
-// Ray Query for shadow
-vec3 origin = worldPos;
-vec3 direction = L; // vector to light
-float tMin = 0.01f;
-float tMax = lightDistance;
-
-// Initializes a ray query object but does not start traversal
-rayQueryEXT rayQuery;
-rayQueryInitializeEXT(rayQuery, topLevelAS, gl_RayFlagsTerminateOnFirstHitEXT, 0xFF, origin, tMin,
- direction, tMax);
-
-// Start traversal: return false if traversal is complete
-while(rayQueryProceedEXT(rayQuery))
-{
-}
-
-// Returns type of committed (true) intersection
-if(rayQueryGetIntersectionTypeEXT(rayQuery, true) != gl_RayQueryCommittedIntersectionNoneEXT)
-{
- // Got an intersection == Shadow
- outColor *= 0.1;
-}
-~~~~
-
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_rayquery](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_rayquery)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tuto_reflection.md.html b/docs/vkrt_tuto_reflection.md.html
deleted file mode 100644
index a930c58..0000000
--- a/docs/vkrt_tuto_reflection.md.html
+++ /dev/null
@@ -1,270 +0,0 @@
-
-**NVIDIA Vulkan Ray Tracing Tutorial**
-**Reflections**
-
-
-
-
-This is an extension of the Vulkan ray tracing [tutorial](vkrt_tutorial.md.html).
-
-(insert setup.md.html here)
-
-# Setting Up the scene
-
-First, we will create a scene with two reflective planes and a multicolored cube in the center. Change the `helloVk.loadModel` calls in `main()` to
-
-~~~~ C++
- // Creation of the example
- helloVk.loadModel(nvh::findFile("media/scenes/cube.obj", defaultSearchPaths),
- nvmath::translation_mat4(nvmath::vec3f(-2, 0, 0))
- * nvmath::scale_mat4(nvmath::vec3f(.1f, 5.f, 5.f)));
- helloVk.loadModel(nvh::findFile("media/scenes/cube.obj", defaultSearchPaths),
- nvmath::translation_mat4(nvmath::vec3f(2, 0, 0))
- * nvmath::scale_mat4(nvmath::vec3f(.1f, 5.f, 5.f)));
- helloVk.loadModel(nvh::findFile("media/scenes/cube_multi.obj", defaultSearchPaths));
- helloVk.loadModel(nvh::findFile("media/scenes/plane.obj", defaultSearchPaths),
- nvmath::translation_mat4(nvmath::vec3f(0, -1, 0)));
-~~~~
-
-Then find `cube.mtl` in `media/scenes` and modify the material to be 95% reflective, without any diffuse
-contribution:
-
-~~~~ C++
-newmtl cube_instance_material
-illum 3
-d 1
-Ns 32
-Ni 0
-Ka 0 0 0
-Kd 0 0 0
-Ks 0.95 0.95 0.95
-~~~~
-
-# Recursive Reflections
-
-Vulkan ray tracing allows recursive calls to traceRayEXT, up to a limit defined by `VkPhysicalDeviceRayTracingPropertiesKHR`.
-
-In `createRtPipeline()` in `hello_vulkan.cpp`, bring the maximum recursion depth up to 10, making sure not to exceed the physical device's maximum recursion limit:
-
-~~~~ C++
- rayPipelineInfo.setMaxRecursionDepth(
- std::max(10u, m_rtProperties.maxRecursionDepth)); // Ray depth
-~~~~
-
-## `raycommon.glsl`
-
-We will need to track the depth and the attenuation of the ray.
-In the `hitPayload` struct in `raycommon.glsl`, add the following:
-
-~~~~ C++
- int depth;
- vec3 attenuation;
-~~~~
-
-## `raytrace.rgen`
-
-In the ray generation shader, we will initialize all payload values before calling `traceRayEXT`.
-
-~~~~ C++
- prd.depth = 0;
- prd.hitValue = vec3(0);
- prd.attenuation = vec3(1.f, 1.f, 1.f);
-~~~~
-
-## `raytrace.rchit`
-
-At the end of the closest hit shader, before setting `prd.hitValue`, we need to shoot a ray if the material is reflective.
-
-~~~~ C++
- // Reflection
- if(mat.illum == 3 && prd.depth < 10)
- {
- vec3 origin = worldPos;
- vec3 rayDir = reflect(gl_WorldRayDirectionEXT, normal);
- prd.attenuation *= mat.specular;
-
- prd.depth++;
- traceRayEXT(topLevelAS, // acceleration structure
- gl_RayFlagsNoneEXT, // rayFlags
- 0xFF, // cullMask
- 0, // sbtRecordOffset
- 0, // sbtRecordStride
- 0, // missIndex
- origin, // ray origin
- 0.1, // ray min range
- rayDir, // ray direction
- 100000.0, // ray max range
- 0 // payload (location = 0)
- );
- prd.depth--;
- }
-~~~~
-
-The calculated `hitValue` needs to be accumulated, since the payload is global for the
-entire execution from raygen, so change the last line of `main()` to
-
-~~~~ C++
-prd.hitValue += vec3(attenuation * lightIntensity * (diffuse + specular)) * prd.attenuation;
-~~~~
-
-## `raytrace.rmiss`
-
-Finally, the miss shader also needs to attenuate its contribution:
-
-~~~~ C++
- prd.hitValue = clearColor.xyz * 0.8 * prd.attenuation;
-~~~~
-
-## Working, but limited
-
-This is working, but it is limited to the number of recursions the GPU can do, and could also impact performance. Trying to go over the limit of recursions would eventually generate a device lost error.
-
-# Iterative Reflections
-
-Instead of dispatching new rays from the closest hit shader, we will return the information in the payload to shoot new rays if needed.
-
-## 'raycommon.glsl'
-
-Enhance the structure to add information to start new rays if wanted.
-
-~~~~ C++
- int done;
- vec3 rayOrigin;
- vec3 rayDir;
-~~~~
-
-## `raytrace.rgen`
-
-Initialize the new members of the payload:
-
-~~~~ C++
- prd.done = 1;
- prd.rayOrigin = origin.xyz;
- prd.rayDir = direction.xyz;
-~~~~
-
-Instead of calling traceRayEXT only once, we will call it in a loop until we are done.
-
-Wrap the trace call in `raytrace.rgen` like this:
-
-~~~~ C++
- vec3 hitValue = vec3(0);
- for(;;)
- {
- traceRayEXT( /*.. */);
-
- hitValue += prd.hitValue * prd.attenuation;
-
- prd.depth++;
- if(prd.done == 1 || prd.depth >= 10)
- break;
-
- origin.xyz = prd.rayOrigin;
- direction.xyz = prd.rayDir;
- prd.done = 1; // Will stop if a reflective material isn't hit
- }
-~~~~
-
-And make sure to write the correct value
-
-~~~~ C++
-imageStore(image, ivec2(gl_LaunchIDEXT.xy), vec4(hitValue, 1.0));
-~~~~
-
-## `raytrace.rchit`
-
-We no longer need to shoot rays from the closest hit shader, so we can replace the block at the end with
-
-~~~~ C++
- if(mat.illum == 3)
- {
- vec3 origin = worldPos;
- vec3 rayDir = reflect(gl_WorldRayDirectionEXT, normal);
- prd.attenuation *= mat.specular;
- prd.done = 0;
- prd.rayOrigin = origin;
- prd.rayDir = rayDir;
- }
-~~~~
-
-The calculation of the hitValue also no longer needs to be additive, or take attenuation into account:
-
-~~~~ C++
- prd.hitValue = vec3(attenuation * lightIntensity * (diffuse + specular));
-~~~~
-
-## `raytrace.rmiss`
-
-Since the ray generation shader now handles attenuation, we no longer need to attenuate the value returned in the miss shader:
-
-~~~~ C++
- prd.hitValue = clearColor.xyz * 0.8;
-~~~~
-
-## Max Recursion
-
-Finally, we no longer need to have a deep recursion setting in `createRtPipeline` -- just a depth of 2, one for the initial ray generation segment and another for shadow rays.
-
-~~~~ C++
- rayPipelineInfo.setMaxRecursionDepth(2); // Ray depth
-~~~~
-
-In `raytrace.rgen`, we can now make the maximum ray depth significantly larger -- such as 100, for instance -- without causing a device lost error.
-
-# Controlling Depth
-
-As an extra, we can also add UI to control the maximum depth.
-
-In the `RtPushConstant` structure, we can add a new `maxDepth` member to pass to the shader.
-
-~~~~ C++
- struct RtPushConstant
- {
- nvmath::vec4f clearColor;
- nvmath::vec3f lightPosition;
- float lightIntensity;
- int lightType;
- int maxDepth{10};
- } m_rtPushConstants;
-~~~~
-
-In the `raytrace.rgen` shader, we will collect the push constant data
-
-~~~~ C++
-layout(push_constant) uniform Constants
-{
- vec4 clearColor;
- vec3 lightPosition;
- float lightIntensity;
- int lightType;
- int maxDepth;
-}
-pushC;
-~~~~
-
-Then test for the value for when to stop
-
-~~~~ C++
- if(prd.done == 1 || prd.depth >= pushC.maxDepth)
- break;
-~~~~
-
-Finally, in `main.cpp` in the `renderUI` function, we will add a slider to control the value.
-
-~~~~ C++
- ImGui::SliderInt("Max Depth", &helloVk.m_rtPushConstants.maxDepth, 1, 100);
-~~~~
-
-# Final Code
-
-You can find the final code in the folder [ray_tracing_reflections](https://github.com/nvpro-samples/vk_raytracing_tutorial_KHR/tree/master/ray_tracing_reflections)
-
-
-
-
-
-
-
-
diff --git a/docs/vkrt_tutorial.md.html b/docs/vkrt_tutorial.md.html
index fb303b9..4a06a56 100644
--- a/docs/vkrt_tutorial.md.html
+++ b/docs/vkrt_tutorial.md.html
@@ -21,7 +21,7 @@ methods and functions. The sections are organized by components, with subsection