├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── Changelog.md ├── Docs ├── NrcGuide.md ├── QuickStart.md ├── SharcGuide.md └── figures │ ├── banner.png │ ├── nrc_debugnsight.png │ ├── nrc_guidebanner.png │ ├── nrc_integworkflow.svg │ ├── nrc_intropath.png │ ├── nrc_introtermination.svg │ ├── nrc_rm0_addquery.png │ ├── nrc_rm10_cacheview.gif │ ├── nrc_rm1_showquery.png │ ├── nrc_rm2_trainingheatmap.png │ ├── nrc_rm3_trainingheatmapsmooth.png │ ├── nrc_rm4_primarytrainingradiance.png │ ├── nrc_rm5_primarytrainingradiancesmooth.png │ ├── nrc_rm6_secondarytrainingradiance.png │ ├── nrc_rm7_secondarytrainingradiancesmooth.png │ ├── nrc_rm8_queryindex.png │ ├── nrc_rm9_trainingqueryindex.png │ ├── quickstart_ui.png │ ├── sharc_00_debug.jpg │ ├── sharc_00_normal.jpg │ ├── sharc_01_cache_off.jpg │ ├── sharc_01_cache_on.jpg │ ├── sharc_render.svg │ ├── sharc_render_debug.jpg │ ├── sharc_render_normal.jpg │ ├── sharc_sample_normal.jpg │ ├── sharc_sample_occupancy.jpg │ ├── sharc_sample_sharc.jpg │ └── sharc_update.svg ├── External └── CMakeLists.txt ├── License.md ├── Readme.md └── Samples └── Pathtracer ├── Brdf.h ├── CMakeLists.txt ├── Denoiser.hlsl ├── GlobalCb.h ├── LightingCb.h ├── NrcIntegration.cpp ├── NrcIntegration.h ├── NrcUtils.cpp ├── NrcUtils.h ├── Nrd ├── DenoiserNRD.hlsli └── NRD.hlsli ├── NrdConfig.cpp ├── NrdConfig.h ├── NrdIntegration.cpp ├── NrdIntegration.h ├── Pathtracer.cpp ├── Pathtracer.h ├── Pathtracer.hlsl ├── PathtracerUi.cpp ├── PathtracerUi.h ├── PathtracerUtils.h ├── RenderTargets.cpp ├── RenderTargets.h ├── SharcResolve.hlsl ├── Tonemapping.hlsl └── shaders.cfg /.gitignore: -------------------------------------------------------------------------------- 1 | bin/ 2 | build*/ 3 | .vs 4 | .vscode 5 | *.sublime-project 6 | *.sublime-workspace 7 | nrd.cfg 8 | External/AgilitySDK/ 9 | External/dxc/ -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Donut"] 2 | path = External/Donut 3 | url = https://github.com/NVIDIAGameWorks/donut.git 4 | branch = main 5 | [submodule "external/RayTracingDenoiser"] 6 | path = External/RayTracingDenoiser 7 | url = https://github.com/NVIDIAGameWorks/RayTracingDenoiser.git 8 | [submodule "Libraries/Sharc"] 9 | path = Libraries/Sharc 10 | url = https://github.com/NVIDIAGameWorks/Spatial-Hash-Radiance-Cache.git 11 | [submodule "Libraries/Nrc"] 12 | path = Libraries/Nrc 13 | url = https://github.com/NVIDIAGameWorks/Neural-Radiance-Cache.git 14 | [submodule "Assets/Media"] 15 | path = Assets/Media 16 | url = https://github.com/NVIDIAGameWorks/RTXGI-Assets.git 17 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. 2 | # 3 | # NVIDIA CORPORATION and its licensors retain all intellectual property 4 | # and proprietary rights in and to this software, related documentation 5 | # and any modifications thereto. Any use, reproduction, disclosure or 6 | # distribution of this software and related documentation without an express 7 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 8 | 9 | cmake_minimum_required (VERSION 3.19) 10 | 11 | project( 12 | RTXGI2Samples 13 | DESCRIPTION "Samples showcasing NRC and SHARC features" 14 | LANGUAGES CXX 15 | ) 16 | 17 | set(CMAKE_CXX_STANDARD 20) 18 | set(CMAKE_CXX_STANDARD_REQUIRED ON) 19 | set(CMAKE_CXX_EXTENSIONS ON) 20 | 21 | # Set MSVC debug level 22 | if(MSVC) 23 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /D_ITERATOR_DEBUG_LEVEL=1") 24 | set (CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") 25 | endif() 26 | 27 | # Warnings as errors 28 | set(CMAKE_COMPILE_WARNING_AS_ERROR ON) 29 | 30 | # ----------------------------------------------------------------------------- 31 | # Other packages downloaded from zips 32 | # ----------------------------------------------------------------------------- 33 | 34 | # Helper to download and unzip a package from a URL 35 | # Uses a zero-length file to identify the version of the package 36 | function(CheckAndDownloadPackage NAME VERSION LOCAL_PATH URL ARCHIVE_TYPE) 37 | # Do we already have the correct version? 38 | if(NOT EXISTS ${LOCAL_PATH}/${VERSION}.ver) 39 | # Was there a previous version that we need to delete? 40 | if(EXISTS ${LOCAL_PATH}) 41 | message(STATUS "Deleting old " ${NAME}) 42 | file(REMOVE_RECURSE ${LOCAL_PATH}) 43 | endif() 44 | message(STATUS "Obtaining " ${NAME} " " ${VERSION}) 45 | file(DOWNLOAD ${URL} ${LOCAL_PATH}.${ARCHIVE_TYPE}) 46 | message(STATUS "Extracting " ${NAME}) 47 | file(ARCHIVE_EXTRACT INPUT ${LOCAL_PATH}.${ARCHIVE_TYPE} DESTINATION ${LOCAL_PATH}) 48 | file(REMOVE ${LOCAL_PATH}.${ARCHIVE_TYPE}) 49 | # Create an empty file so we know which version we have 50 | file(WRITE ${LOCAL_PATH}/${VERSION}.ver) 51 | endif() 52 | endfunction() 53 | 54 | CheckAndDownloadPackage("Agility SDK" "v1.610.4" ${CMAKE_CURRENT_SOURCE_DIR}/external/AgilitySDK https://www.nuget.org/api/v2/package/Microsoft.Direct3D.D3D12/1.610.4 "zip") 55 | CheckAndDownloadPackage("DXC" "v1.7.2308" ${CMAKE_CURRENT_SOURCE_DIR}/external/dxc https://github.com/microsoft/DirectXShaderCompiler/releases/download/v1.7.2308/dxc_2023_08_14.zip "zip") 56 | 57 | # ----------------------------------------------------------------------------- 58 | # Configure options 59 | # ----------------------------------------------------------------------------- 60 | 61 | # Setup shader compiler 62 | set(DXC_DXIL_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/external/dxc/bin/x64/dxc.exe" CACHE STRING "DXC shader compiler path") 63 | set(DXC_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/dxc/bin/x64/dxc.exe") 64 | set(DXC_SPIRV_PATH "${CMAKE_CURRENT_SOURCE_DIR}/external/dxc/bin/x64/dxc.exe") 65 | 66 | # Setup denoiser support (NRD) 67 | option(USE_RAY_TRACING_DENOISER "Include NRD as part of the sample." ON) 68 | set(NRD_PATH "${CMAKE_CURRENT_SOURCE_DIR}/External/RayTracingDenoiser") 69 | 70 | # Setup asset importer support 71 | option(DONUT_WITH_ASSIMP "" OFF) 72 | 73 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/Bin") 74 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 75 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_MINSIZEREL "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 76 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 77 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELWITHDEBINFO "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}") 78 | set(DONUT_SHADERS_OUTPUT_DIR "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/framework") 79 | 80 | 81 | # Setup option for local NRC library development 82 | set(CUSTOM_NRC_DISTRO_PATH "" CACHE PATH "Enter the path to NRC distro directory containing lib, include, and bin.") 83 | 84 | # Options for NRC DLL signing verification 85 | option(NRC_ENABLE_DLL_CHECK "Enable NRC DLL signing verification." ON) 86 | if(NRC_ENABLE_DLL_CHECK) 87 | add_compile_definitions(NRC_ENABLE_DLL_CHECK) 88 | endif() 89 | 90 | # Check if the custom directory path is provided. 91 | if(NOT CUSTOM_NRC_DISTRO_PATH) 92 | message(STATUS "Custom NRC distro path is not provided.") 93 | set(NRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}/Libraries/Nrc) 94 | else() 95 | set(NRC_DIR ${CUSTOM_NRC_DISTRO_PATH}) 96 | endif() 97 | 98 | if(EXISTS ${NRC_DIR}/Bin/NRC_Vulkan.dll) 99 | set(SETUP_NRC_WITH_VULKAN TRUE) 100 | add_compile_definitions(NRC_WITH_VULKAN) 101 | endif() 102 | 103 | unset(DONUT_WITH_VULKAN CACHE) 104 | unset(NVRHI_WITH_VULKAN CACHE) 105 | 106 | # Force Vulkan support 107 | option(DONUT_WITH_VULKAN "Enable the Vulkan version of Donut" ON) 108 | 109 | # Add projects 110 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/External/Donut) 111 | 112 | message(STATUS "NVRHI Vulkan support " "${NVRHI_WITH_VULKAN}") 113 | message(STATUS "Donut Vulkan support " "${DONUT_WITH_VULKAN}") 114 | 115 | if(NVRHI_WITH_VULKAN OR NVRHI_WITH_DX12) 116 | add_subdirectory(Samples/Pathtracer) 117 | # Set Pathtracer sample project as a startup project 118 | set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Pathtracer) 119 | endif() 120 | 121 | 122 | # Add SHARC includes to the solution 123 | set(SHARC_LIBRARY_ROOT "${CMAKE_CURRENT_SOURCE_DIR}/Libraries/Sharc") 124 | file(GLOB_RECURSE INCLUDE_SHARC "${SHARC_LIBRARY_ROOT}/*.h") 125 | 126 | # Add NRC includes to the solution 127 | file(GLOB_RECURSE INCLUDE_NRC 128 | "${NRC_DIR}/include/*.hlsli" 129 | "${NRC_DIR}/include/*.h" 130 | ) 131 | 132 | add_library(libraries ${INCLUDE_SHARC} ${INCLUDE_NRC}) 133 | set_target_properties(libraries PROPERTIES LINKER_LANGUAGE CXX) 134 | set(VS_FOLDER_NAME "Libraries") 135 | set_target_properties(libraries PROPERTIES FOLDER ${VS_FOLDER_NAME}) 136 | 137 | source_group("Sharc" FILES ${INCLUDE_SHARC}) 138 | source_group("Nrc" FILES ${INCLUDE_NRC}) 139 | 140 | # Include external 141 | add_subdirectory(External) 142 | -------------------------------------------------------------------------------- /Changelog.md: -------------------------------------------------------------------------------- 1 | # RTXGI SDK Change Log 2 | 3 | ## 2.3.2 4 | 5 | ### RTXGI 6 | - Refactor directory and source file names for consistency. 7 | 8 | ### NRC 9 | - Update to v0.13.4.0. 10 | - Changes encompass minor refactoring. 11 | 12 | ## 2.3.1 13 | NRC 14 | - Update to version 0.13.3.0. 15 | - Internal CUDA SDK fix. 16 | - Minor documentation additions for NRC settings. 17 | 18 | ## 2.3.0 19 | 20 | ### RTXGI 21 | - Project structure change: the Donut framework is now located inside `/external` and no longer resides directly in the repo root. 22 | - Update NRD and Donut dependencies. 23 | - Removal of obsolete function definitions from `/samples/pathtracer/pathtracer.hlsl`. 24 | - Minor refactor to improve consistency and readability. 25 | - Minor documentation updates for `/docs/QuickStart.md` to reflect recent UI changes. 26 | - UI restructure for the path tracer sample project to intuitively group settings. 27 | 28 | ### SHaRC 29 | - Update to version 1.4.3.0. 30 | - Split SHaRC parameters to SharcParameters and SharcState which is used only during the update stage. 31 | - API naming changes to account for `Sharc` and `HashGrid` prefixes to avoid collisions. Most of tweakable `#defines` can now be overridden outside of the main source files. 32 | - Added extra dynamic parameters to give move control with multiple SHaRC instances. 33 | - Moved GLSL code snippets to a separate file. 34 | - Addition of an optional anti-firefly filter. 35 | - Minor bug fixes with maximum number of accumulated frames. 36 | 37 | ### NRC 38 | - Update to version 0.13.2.0. 39 | - API modification to support loading of custom paths for dependent DLLs. 40 | - API modification to enable network config file hot-reloading. 41 | - Bugfix for Vulkan memory type checking. 42 | - Bugfix type definition for `NrcPackableFloat` when using 16-bit packing. 43 | - Bugfix for stub functions in `Nrc.hlsli`. 44 | - Expose the ability to configure the number of training iterations. 45 | - Refactor and removal of deprecated or obsolete options. 46 | - Update documentation to reflect recent changes. 47 | 48 | ## 2.2.0 49 | 50 | ### RTXGI 51 | - Bug fix for the `brdf.h` PDF calculation and epsilon size. 52 | - Addition of global surface properties override for roughness and metalness values. 53 | - DLL signing verification implementation for the NRC integration. 54 | - Auto-enablement of detected raytracing-capable hardware when using the pathtracer sample. 55 | - Documentation update. 56 | 57 | ### SHaRC 58 | - Update to version 1.3.1.0. 59 | - The radiance cache now relies on frame-based accumulation. The user should provide the amount of frames for accumulation to the `SharcResolveEntry()` invocation as well as the amount of frames to keep the records which do not receive any updates in the cache before they get evicted. Frame limits can be tied to FPS. This change also improves responsiveness of the cache to the lighting changes. 60 | - Robust accumulation which works better with high sample count 61 | - Documentation updates, including debugging tips and parameters tweaking 62 | - Misc bug fixes 63 | 64 | ### NRC 65 | - Update to v0.12.1 66 | - Update dependencies including CUDA Toolkit to v12.5.1. 67 | - Modifications to `Nrc.hlsli` to comply with Slang requirements for global variables and macro defines. 68 | - Addition of debug visualization of the cache as part of the `ResolveModes`. 69 | - Removal of `queryVertexIndex` debug mechanism in favour of the Resolve pass approach. 70 | - Addition of DLL signing verification capabilities. 71 | - Bug fix for allowing the context to be recreated internally on scene bounds change. 72 | - Documentation update. 73 | 74 | ## 2.1.0 75 | 76 | ### Fixed issues 77 | - Internal fix for NRC to allow it to run on NVIDIA 20xx series GPUs 78 | - Window resizing for the pathtracer sample 79 | 80 | ### API changes 81 | - NRC's `CountersData` buffer is now of type `StructuredBuffer` 82 | - SHARC's `VoxelData` buffers are now of type `StructuredBuffer` 83 | - SHARC modifications to improve GLSL compatibility 84 | 85 | ### Misc. changes 86 | - Readability improvements for the code sample and documentation 87 | - Update to dependencies: 88 | - NRD version in use is [v4.6.1](https://github.com/NVIDIAGameWorks/RayTracingDenoiser/tree/db4f66f301406344211d86463d9f3ba43e74412a) 89 | - Donut version in use is [e053410](https://github.com/NVIDIAGameWorks/donut/tree/e05341011f82ca72dd0d37adc8ef9235ef5607b3) 90 | 91 | ## 2.0.0 92 | Initial release. -------------------------------------------------------------------------------- /Docs/QuickStart.md: -------------------------------------------------------------------------------- 1 | # Quick Start Guide 2 | 3 | This document lists the necessary steps to get up and running with the RTXGI SDK pathtracer sample showcasing an example integration of the NRC and SHaRC libraries in a simplified unidirectional Monte Carlo path tracer. 4 | 5 | For programming/integration guides specific to each technique, see the individual [NRC][NrcGuide] and [SHaRC][SharcGuide] guides. 6 | 7 | ## Build steps 8 | Assuming the tools specified in the [prerequisites][Prereq] section are available: 9 | 10 | 1. Clone the project recursively to a preferred location for the `local-repo-path` field: 11 | ``` 12 | git clone --progress --recursive --branch main -v "https://github.com/NVIDIAGameWorks/RTXGI.git" "local-repo-path" 13 | ``` 14 | 15 | 2. Configure and then generate the solution using CMake GUI (or the CLI). The `source` directory should be the root of the repository and `build` directory should be a new directory located in the repository root. 16 | 17 | 3. The newly generated solution `rtxgi2_samples.sln` should be located in the `build` directory. Open it and run the `pathtracer` project. Optionally, use the debug command line argument `-vk` to run the NVRHI Vulkan rendering backend version. 18 | 19 | ## The pathtracer sample 20 | This showcases an elementary path tracer that relies on either NRC or SHaRC to terminate early into the respective radiance/irradiance cache for an improved signal. 21 | 22 | ![overview](figures/quickstart_ui.png) 23 | 24 | **Generic settings.** New scenes can be loaded from here via a JSON file that specifies GLTF assets and user-defined properties for common constructs such as lights and camera. Additionally, four more settings that affect the pathtracer and either of the radiance caching techniques can be toggled here. 25 | 26 | **Path tracing settings.** This section addresses typical pathtracer settings such as the number of bounces, samples per pixel, a global dial for altering material properties, and a list of debug views that ensure the scene data is correctly represented (normals, worldspace positions, etc.). From this panel, the radiance/irradiance caching modes as well as the denoising modes can be selected. 27 | 28 | **NRC settings.** In this section, NRC can be toggled, fine-tuned, as well as debugged visually via the `Resolve Mode`. For further details see the [in-depth NRC guide][NrcGuide]. 29 | 30 | **SHaRC settings.** These provide a way to toggle the tech, manually invoke a clearing of the cache, fine-tune factors that contribute to the hash-grid data, as well as visually inspect the direct contents of the cache via the `Enable Debug` option. For further details see the [in-depth SHaRC guide][SharcGuide]. 31 | 32 | **Lighting.** This section allows for modifying the initial light data specified in the JSON scene file. 33 | 34 | **Tone mapping.** Post processing section that currently only accounts for tone mapping - useful for clamping radiance values. 35 | 36 | [NrcGuide]: NrcGuide.md 37 | [SharcGuide]: SharcGuide.md 38 | [Prereq]: ../Readme.md/#prerequisites 39 | -------------------------------------------------------------------------------- /Docs/SharcGuide.md: -------------------------------------------------------------------------------- 1 | # SHaRC Integration Guide 2 | 3 | SHaRC algorithm integration doesn't require substantial modifications to the existing path tracer code. The core algorithm consists of two passes. The first pass uses sparse tracing to fill the world-space radiance cache using existing path tracer code, second pass samples cached data on ray hit to speed up tracing. 4 | 5 |
6 | 7 | 8 |
Image 1. Path traced output at 1 path per pixel left and with SHaRC cache usage right
9 |
10 | 11 | ## Integration Steps 12 | 13 | An implementation of SHaRC using the RTXGI SDK needs to perform the following steps: 14 | 15 | At Load-Time 16 | 17 | Create main resources: 18 | * `Hash entries` buffer - structured buffer with 64-bits entries to store the hashes 19 | * `Voxel data` buffer - structured buffer with 128-bit entries which stores accumulated radiance and sample count. Two instances are used to store current and previous frame data 20 | * `Copy offset` buffer - structured buffer with 32-bits per entry used for data compaction 21 | 22 | The number of entries in each buffer should be the same, it represents the number of scene voxels used for radiance caching. A solid baseline for most scenes can be the usage of $2^{22}$ elements. Commonly a power of 2 values are suggested. Higher element count can be used for scenes with high depth complexity, lower element count reduce memmory pressure, but can result in more hash collisions. 23 | 24 | > :warning: **All buffers should be initially cleared with '0'** 25 | 26 | At Render-Time 27 | 28 | * **Populate cache data** using sparse tracing against the scene 29 | * **Combine old and new cache data**, perform data compaction 30 | * **Perform tracing** with early path termination using cached data 31 | 32 | ## Hash Grid Visualization 33 | 34 | `Hash grid` visualization itself doesn’t require any GPU resources to be used. The simplest debug visualization uses world space position derived from the primary ray hit intersection. 35 | 36 | ```C++ 37 | HashGridParameters gridParameters; 38 | gridParameters.cameraPosition = g_Constants.cameraPosition; 39 | gridParameters.logarithmBase = SHARC_GRID_LOGARITHM_BASE; 40 | gridParameters.sceneScale = g_Constants.sharcSceneScale; 41 | gridParameters.levelBias = SHARC_GRID_LEVEL_BIAS; 42 | 43 | float3 color = HashGridDebugColoredHash(positionWorld, gridParameters); 44 | ``` 45 | 46 |
47 | 48 | 49 |
Image 2. SHaRC hash grid vizualization
50 |
51 | 52 | Logarithm base controls levels of detail distribution and voxel size ratio change between neighboring levels, it doesn’t make voxel sizes bigger or smaller on average. To control voxel size use ```sceneScale``` parameter instead. HashGridParameters::levelBias should be used to control at which level near the camera the voxel level get's clamped to avoid getting detailed levels if it is not required. 53 | 54 | ## Implementation Details 55 | 56 | ### Render Loop Change 57 | 58 | Instead of the original trace call, we should have the following four passes with SHaRC: 59 | 60 | * SHaRC Update - RT call which updates the cache with the new data on each frame. Requires `SHARC_UPDATE 1` shader define 61 | * SHaRC Resolve - Compute call which combines new cache data with data obtained on the previous frame 62 | * SHaRC Compaction - Compute call to perform data compaction after previous resolve call 63 | * SHaRC Render/Query - RT call which traces scene paths and performs early termination using cached data. Requires `SHARC_QUERY 1` shader define 64 | 65 | ### Resource Binding 66 | 67 | The SDK provides shader-side headers and code snippets that implement most of the steps above. Shader code should include [SharcCommon.h](https://github.com/NVIDIAGameWorks/SHARC/blob/main/include/SharcCommon.h) which already includes [HashGridCommon.h](https://github.com/NVIDIAGameWorks/SHARC/blob/main/include/HashGridCommon.h) 68 | 69 | | **Render Pass** | **Hash Entries** | **Voxel Data** | **Voxel Data Previous** | **Copy Offset** | 70 | |:-----------------|:----------------:|:--------------:|:-----------------------:|:---------------:| 71 | | SHaRC Update | RW | RW | Read | RW* | 72 | | SHaRC Resolve | Read | RW | Read | Write | 73 | | SHaRC Compaction | RW | | | RW | 74 | | SHaRC Render | Read | Read | | | 75 | 76 | *Read - resource can be read-only* 77 | *Write - resource can be write-only* 78 | 79 | *Buffer is used if SHARC_ENABLE_64_BIT_ATOMICS is set to 0 80 | 81 | Each pass requires appropriate transition/UAV barries to wait for the previous stage completion. 82 | 83 | ### SHaRC Update 84 | 85 | > :warning: Requires `SHARC_UPDATE 1` shader define. `Voxel Data` buffer should be cleared with `0` if `Resolve` pass is active 86 | 87 | This pass runs a full path tracer loop for a subset of screen pixels with some modifications applied. We recommend starting with random pixel selection for each 5x5 block to process only 4% of the original paths per frame. This typically should result in a good data set for the cache update and have a small performance overhead at the same time. Positions should be different between frames, producing whole-screen coverage over time. Each path segment during the update step is treated individually, this way we should reset path throughput to 1.0 and accumulated radiance to 0.0 on each bounce. For each new sample(path) we should first call `SharcInit()`. On a miss event `SharcUpdateMiss()` is called and the path gets terminated, for hit we should evaluate radiance at the hit point and then call `SharcUpdateHit()`. If `SharcUpdateHit()` call returns false, we can immediately terminate the path. Once a new ray has been selected we should update the path throughput and call `SharcSetThroughput()`, after that path throughput can be safely reset back to 1.0. 88 | 89 |
90 | 91 |
Figure 1. Path tracer loop during SHaRC Update pass
92 |
93 | 94 | ### SHaRC Resolve and Compaction 95 | 96 | `Resolve` pass is performed using compute shader which runs `SharcResolveEntry()` for each element. `Compaction` pass uses `SharcCopyHashEntry()` call. 97 | > :tip: Check [Resource Binding](#resource-binding) section for details on the required resources and their usage for each pass 98 | 99 | `SharcResolveEntry()` takes maximum number of accumulated frames as an input parameter to control the quality and responsivness of the cached data. Larger values can increase the quality at increase response times. `staleFrameNumMax` parameter is used to control the lifetime of cached elements, it is used to control cache occupancy 100 | 101 | > :warning: Small `staleFrameNumMax` values can negatively impact performance, `SHARC_STALE_FRAME_NUM_MIN` constant is used to prevent such behaviour 102 | 103 | ### SHaRC Render 104 | 105 | > :warning: Requires `SHARC_QUERY 1` shader define 106 | 107 | During rendering with SHaRC cache usage we should try obtaining cached data using `SharcGetCachedRadiance()` on each hit except the primary hit if any. Upon success, the path tracing loop should be immediately terminated. 108 | 109 |
110 | 111 |
Figure 2. Path tracer loop during SHaRC Render pass
112 |
113 | 114 | To avoid potential rendering artifacts certain aspects should be taken into account. If the path segment length is less than a voxel size(checked using `GetVoxelSize()`) we should continue tracing until the path segment is long enough to be safely usable. Unlike diffuse lobes, specular ones should be treated with care. For the glossy specular lobe, we can estimate its "effective" cone spread and if it exceeds the spatial resolution of the voxel grid then the cache can be used. Cone spread can be estimated as: 115 | 116 | $$2.0 * ray.length * sqrt(0.5 * a^2 / (1 - a^2))$$ 117 | where `a` is material roughness squared. 118 | 119 | ## Parameters Selection and Debugging 120 | 121 | For the rendering step adding debug heatmap for the bounce count can help with understanding cache usage efficiency. 122 | 123 |
124 | 125 | 126 |
Image 3. Tracing depth heatmap, left - SHaRC off, right - SHaRC on (green - 1 indirect bounce, red - 2+ indirect bounces)
127 |
128 | 129 | Sample count uses SHARC_SAMPLE_NUM_BIT_NUM(18) bits to store accumulated sample number. 130 | > :note: `SHARC_SAMPLE_NUM_MULTIPLIER` is used internally to improve precision of math operations for elements with low sample number, every new sample will increase the internal counter by 'SHARC_SAMPLE_NUM_MULTIPLIER'. 131 | 132 | SHaRC radiance values are internally premultiplied with `SHARC_RADIANCE_SCALE` and accumulated using 32-bit integer representation per component. 133 | 134 | > :note: [SharcCommon.h](https://github.com/NVIDIAGameWorks/SHARC/blob/main/include/SharcCommon.h) provides several methods to verify potential overflow in internal data structures. `SharcDebugBitsOccupancySampleNum()` and `SharcDebugBitsOccupancyRadiance()` can be used to verify consistency in the sample count and corresponding radiance values representation. 135 | 136 | `HashGridDebugOccupancy()` should be used to validate cache occupancy. With a static camera around 10-20% of elements should be used on average, on fast camera movement the occupancy will go up. Increased occupancy can negatively impact performance, to control that we can increase the element count as well as decrease the threshold for the stale frames to evict outdated elements more agressivly. 137 | 138 |
139 | 140 |
Image 4. Debug overlay to visualize cache occupancy through HashGridDebugOccupancy()
141 |
142 | 143 | ## Memory Usage 144 | 145 | ```Hash entries``` buffer, two ```Voxel data``` and ```Copy offset``` buffers totally require 352 (64 + 128 * 2 + 32) bits per voxel. For $2^{22}$ cache elements this will require ~185 MBs of video memory. Total number of elements may vary depending on the voxel size and scene scale. Larger buffer sizes may be needed to reduce potential hash collisions. -------------------------------------------------------------------------------- /Docs/figures/banner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/banner.png -------------------------------------------------------------------------------- /Docs/figures/nrc_debugnsight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_debugnsight.png -------------------------------------------------------------------------------- /Docs/figures/nrc_guidebanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_guidebanner.png -------------------------------------------------------------------------------- /Docs/figures/nrc_intropath.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_intropath.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm0_addquery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm0_addquery.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm10_cacheview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm10_cacheview.gif -------------------------------------------------------------------------------- /Docs/figures/nrc_rm1_showquery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm1_showquery.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm2_trainingheatmap.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm2_trainingheatmap.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm3_trainingheatmapsmooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm3_trainingheatmapsmooth.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm4_primarytrainingradiance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm4_primarytrainingradiance.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm5_primarytrainingradiancesmooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm5_primarytrainingradiancesmooth.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm6_secondarytrainingradiance.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm6_secondarytrainingradiance.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm7_secondarytrainingradiancesmooth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm7_secondarytrainingradiancesmooth.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm8_queryindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm8_queryindex.png -------------------------------------------------------------------------------- /Docs/figures/nrc_rm9_trainingqueryindex.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/nrc_rm9_trainingqueryindex.png -------------------------------------------------------------------------------- /Docs/figures/quickstart_ui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/quickstart_ui.png -------------------------------------------------------------------------------- /Docs/figures/sharc_00_debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_00_debug.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_00_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_00_normal.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_01_cache_off.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_01_cache_off.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_01_cache_on.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_01_cache_on.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_render.svg: -------------------------------------------------------------------------------- 1 | Trace rayEvaluate geometry and materialsCompute shadingGenerate new rayModify throughputSharcGetCachedRadiance()SHaRC Render -------------------------------------------------------------------------------- /Docs/figures/sharc_render_debug.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_render_debug.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_render_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_render_normal.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_sample_normal.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_sample_normal.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_sample_occupancy.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_sample_occupancy.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_sample_sharc.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NVIDIA-RTX/RTXGI/e381c965835422e76ccb15f7b9fa51c68d57f144/Docs/figures/sharc_sample_sharc.jpg -------------------------------------------------------------------------------- /Docs/figures/sharc_update.svg: -------------------------------------------------------------------------------- 1 | Trace rayEvaluate geometry and materialsCompute shadingGenerate new rayModify throughputSharcUpdateHit()SHaRC UpdateReset throughputSharcSetThroughput() -------------------------------------------------------------------------------- /External/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | # 4 | # NVIDIA CORPORATION and its licensors retain all intellectual property 5 | # and proprietary rights in and to this software, related documentation 6 | # and any modifications thereto. Any use, reproduction, disclosure or 7 | # distribution of this software and related documentation without an express 8 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | 10 | ########################################################################## 11 | ################################ UTIL #################################### 12 | ########################################################################## 13 | function(util_get_shader_profile_from_name FILE_NAME DXC_PROFILE) 14 | get_filename_component(EXTENSION ${FILE_NAME} EXT) 15 | if ("${EXTENSION}" STREQUAL ".cs.hlsl") 16 | set(DXC_PROFILE "cs" PARENT_SCOPE) 17 | endif() 18 | if ("${EXTENSION}" STREQUAL ".vs.hlsl") 19 | set(DXC_PROFILE "vs" PARENT_SCOPE) 20 | endif() 21 | if ("${EXTENSION}" STREQUAL ".gs.hlsl") 22 | set(DXC_PROFILE "gs" PARENT_SCOPE) 23 | endif() 24 | if ("${EXTENSION}" STREQUAL ".ps.hlsl") 25 | set(DXC_PROFILE "ps" PARENT_SCOPE) 26 | endif() 27 | endfunction() 28 | 29 | function(util_generate_shader_config_file OUT_FILE_NAME DIR DEFINES) 30 | file(GLOB_RECURSE HLSL_FILES "${CMAKE_CURRENT_SOURCE_DIR}/${DIR}/*.hlsl") 31 | 32 | set(out_content "") 33 | foreach(FILE_NAME ${HLSL_FILES}) 34 | get_filename_component(NAME_ONLY ${FILE_NAME} NAME) 35 | set(DXC_PROFILE "") 36 | util_get_shader_profile_from_name(${FILE_NAME} DXC_PROFILE) 37 | set(out_content "${out_content}${DIR}/${NAME_ONLY} -T ${DXC_PROFILE} -E main ${DEFINES}\n") 38 | endforeach() 39 | 40 | file(WRITE ${OUT_FILE_NAME} ${out_content}) 41 | endfunction() 42 | 43 | ########################################################################## 44 | ########################### RayTracingDenoiser ########################### 45 | ########################################################################## 46 | 47 | # see https://github.com/NVIDIAGameWorks/RayTracingDenoiser#cmake-options 48 | # set(NRD_DXC_CUSTOM_PATH ${DXC_CUSTOM_PATH}) 49 | set(NRD_DXC_PATH "${DXC_DXIL_EXECUTABLE}" CACHE STRING "DXC shader compiler path for NRD") 50 | set(NRD_SHADER_OUTPUT_PATH "${SHADER_OUTPUT_PATH}" CACHE STRING "") 51 | set(NRD_PROJECT_FOLDER "External/RayTracingDenoiser") 52 | set(NRD_NORMAL_ENCODING "2" CACHE STRING "Normal encoding variant (0-4, matches nrd::NormalEncoding)") 53 | set(NRD_ROUGHNESS_ENCODING "1" CACHE STRING "Roughness encoding variant (0-2, matches nrd::RoughnessEncoding)") 54 | set(NRD_IS_SUBMODULE ON) 55 | # NRD uses a custom output path for some reason 56 | set(GLOBAL_BIN_OUTPUT_PATH ${CMAKE_BINARY_DIR} CACHE STRING "") 57 | set(NRD_SHADERS_PATH "${CMAKE_BINARY_DIR}/NRDShaders" CACHE STRING "") 58 | option(NRD_DISABLE_SHADER_COMPILATION "" ON) 59 | option(NRD_USE_PRECOMPILED_SHADERS "" OFF) 60 | 61 | add_subdirectory("RayTracingDenoiser") 62 | 63 | # Let CMake generate the nrd_shaders.cfg file 64 | util_generate_shader_config_file( 65 | "nrd.cfg" 66 | "RayTracingDenoiser/Shaders/Source" 67 | "-D NRD_COMPILER_DXC=1 -D NRD_NORMAL_ENCODING=${NRD_NORMAL_ENCODING} -D NRD_ROUGHNESS_ENCODING=${NRD_ROUGHNESS_ENCODING}" 68 | ) 69 | 70 | # ShaderMake general arguments 71 | set ( 72 | SHADERMAKE_GENERAL_ARGS 73 | "-I ${CMAKE_CURRENT_SOURCE_DIR}/RayTracingDenoiser/External/MathLib -I ${CMAKE_CURRENT_SOURCE_DIR}/RayTracingDenoiser/Shaders/Include -I ${CMAKE_CURRENT_SOURCE_DIR}/RayTracingDenoiser/Shaders/Resources" 74 | ) 75 | 76 | # Compile all shaders in nrd_shaders.cfg 77 | include(${CMAKE_CURRENT_SOURCE_DIR}/donut/compileshaders.cmake) 78 | 79 | donut_compile_shaders( 80 | TARGET nrd_shaders 81 | CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/nrd.cfg 82 | SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/RayTracingDenoiser/Shaders/Include 83 | FOLDER "NRD" 84 | DXIL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/nrd/dxil 85 | SPIRV_DXC ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/nrd/spirv 86 | CFLAGS "-WX -O3 -enable-16bit-types -all_resources_bound" 87 | SHADERMAKE_OPTIONS_DXIL ${SHADERMAKE_GENERAL_ARGS} 88 | SHADERMAKE_OPTIONS_SPIRV ${SHADERMAKE_GENERAL_ARGS} 89 | ) 90 | 91 | set(RTXDI_SKIP_SHADER_VALIDATION ON CACHE STRING "Skip RTXDI shader validation") -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | # RTXGI 2 | ![banner](Docs/figures/banner.png) 3 |
4 |
5 | · 6 | Change Log 7 | · 8 | Quick Start 9 | · 10 | NRC Guide 11 | · 12 | SHaRC Guide 13 | · 14 |
15 |
16 | 17 | Advances in path tracing techniques have allowed for the capture of lighting data from the environment, enabling the use of indirect illumination in real-time with both improved accuracy and speed. RTXGI SDK implements two such techniques, replacing traditional probe-based irradiance caching with a world-space radiance cache which can be used to sample outgoing radiance each time scene geometry is hit during path tracing. 18 | 19 | These techniques may be combined with a regular path tracing pipeline for the primary rays, sampling cached data only for indirect bounce evaluation. By replacing the whole path trace with a single ray hit evaluation and cache lookup, the cost is reduced with little to no compromise in signal quality, while remaining responsive to change and supporting large-scale dynamic scenes with complex lighting setups. 20 | 21 | RTXGI SDK provides an example integration (DX12 and Vulkan) of two state-of-the-art radiance caching techniques for path tracing - a (currently experimental) AI-based approach known as Neural Radiance Cache (NRC), and Spatially Hashed Radiance Cache (SHaRC). The former requires Tensor Cores while the latter has certain limitations but is currently supported on a wider range of hardware without any vendor-specific requirements. RTXGI SDK also hosts documentation and distribution corresponding to both of these techniques, see [Project Structure][ProjectStructure] section for further details. 22 | 23 | 24 | ## Project structure 25 | |Directory |Details | 26 | |----------------------------|---------------------------------------------| 27 | |[/Docs][Docs] |_Documentation for showcased tech_ | 28 | |[/External][External] |_Helper dependencies for the samples_ | 29 | |[/Assets][Assets] |_Assets and scene definitions_ | 30 | |[/Samples][Samples] |_Samples showcasing usage of NRC, SHaRC_ | 31 | |[/Libraries][Libraries] |_Binaries, src, includes for NRC, SHaRC_ | 32 | 33 | 34 | ## Getting up and running 35 | 36 | ### Prerequisites 37 | Any DXR GPU for SHaRC **|** NV GPUs ≥ Turing (arch 70) for NRC **|** [CMake v3.24.3][CMake] **|** [Git LFS][LFS] **|** [Vulkan SDK 1.3.268.0][VKSDK] **|** [VS 2022][VS22] **|** Windows SDK ≥ 10.0.20348.0 **|** Driver ≥ 555.85 38 | 39 | ### Further steps 40 | - [Quick start guide][QuickStart] for building and running the pathtracer example. 41 | - [NRC integration guide][NrcGuide] and the [SHaRC integration guide][SharcGuide] respectively. 42 | - [Changelog][ChangeLog] for release information. 43 | 44 | ## Contact 45 | RTXGI SDK is actively being developed. Please report any issues directly through the GitHub issue tracker, and for any information or suggestions contact us at rtxgi-sdk-support@nvidia.com. 46 | 47 | ## Citation 48 | Use the following BibTex entry to cite the usage of RTXGI in published research: 49 | ```bibtex 50 | @online{RTXGI, 51 | title = {{{NVIDIA}}\textregistered{} {RTXGI}}, 52 | author = {{NVIDIA}}, 53 | year = 2024, 54 | url = {https://github.com/NVIDIAGameWorks/RTXGI}, 55 | urldate = {2024-03-18}, 56 | } 57 | ``` 58 | 59 | ## License 60 | See [LICENSE.md](LICENSE.md) 61 | 62 | ## RTXGI v1.x 63 | Version v1.x of RTXGI which includes the DDGI algorithm is located at https://github.com/NVIDIAGameWorks/RTXGI-DDGI. 64 | 65 | 66 | [ChangeLog]: Changelog.md 67 | [QuickStart]: Docs/QuickStart.md 68 | [SharcGuide]: Docs/SharcGuide.md 69 | [NrcGuide]: Docs/NrcGuide.md 70 | [ProjectStructure]: #project-structure 71 | [Docs]: Docs 72 | [External]: External 73 | [Assets]: Assets 74 | [Samples]: Samples/Pathtracer 75 | [Libraries]: Libraries 76 | [CMake]: https://cmake.org/download/ 77 | [LFS]: https://git-lfs.com/ 78 | [VKSDK]: https://vulkan.lunarg.com/sdk/home#windows 79 | [VS22]: https://visualstudio.microsoft.com/vs/ 80 | -------------------------------------------------------------------------------- /Samples/Pathtracer/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved. 2 | # 3 | # NVIDIA CORPORATION and its licensors retain all intellectual property 4 | # and proprietary rights in and to this software, related documentation 5 | # and any modifications thereto. Any use, reproduction, disclosure or 6 | # distribution of this software and related documentation without an express 7 | # license agreement from NVIDIA CORPORATION is strictly prohibited. 8 | 9 | cmake_minimum_required (VERSION 3.19) 10 | 11 | include(${CMAKE_CURRENT_SOURCE_DIR}/../../External/Donut/compileshaders.cmake) 12 | 13 | file(GLOB shaders "*.hlsl") 14 | file(GLOB sources "*.cpp" "*.h") 15 | 16 | set(project Pathtracer) 17 | set(folder "Samples/Pathtracer") 18 | 19 | option (NRD_EMBEDS_SPIRV_SHADERS "NRD embeds SPIRV shaders" OFF) 20 | option (NRD_EMBEDS_DXIL_SHADERS "NRD embeds DXIL shaders" OFF) 21 | option (NRD_EMBEDS_DXBC_SHADERS "NRD embeds DXBC shaders" OFF) 22 | 23 | set(NRC_LIB_DIR ${NRC_DIR}/Lib) 24 | 25 | # Is the NRC package a dev package (internal), or production 26 | if(EXISTS ${NRC_LIB_DIR}/NRC_D3D12_relwithdebinfo.lib) 27 | set (NRC_DEV_BUILD true) 28 | else() 29 | set (NRC_DEV_BUILD false) 30 | endif() 31 | 32 | if(NRC_DEV_BUILD) 33 | set(NRC_LIBRARY_NAME_D3D12_DEBUG NRC_D3D12_debug) 34 | set(NRC_LIBRARY_NAME_D3D12_RELEASE NRC_D3D12_relwithdebinfo) 35 | 36 | if(DONUT_WITH_VULKAN AND SETUP_NRC_WITH_VULKAN) 37 | set(NRC_LIBRARY_NAME_VULKAN_DEBUG NRC_Vulkan_debug) 38 | set(NRC_LIBRARY_NAME_VULKAN_RELEASE NRC_Vulkan_relwithdebinfo) 39 | endif() 40 | else() 41 | set(NRC_LIBRARY_NAME_D3D12_DEBUG NRC_D3D12) 42 | set(NRC_LIBRARY_NAME_D3D12_RELEASE NRC_D3D12) 43 | 44 | if(DONUT_WITH_VULKAN AND SETUP_NRC_WITH_VULKAN) 45 | set(NRC_LIBRARY_NAME_VULKAN_DEBUG NRC_Vulkan) 46 | set(NRC_LIBRARY_NAME_VULKAN_RELEASE NRC_Vulkan) 47 | endif() 48 | endif() 49 | 50 | # Set SHARC includes 51 | set(SHARC_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/../../Libraries) 52 | 53 | # Shader Model 6_6 requires Agility SDK on Windows 10. 54 | if(DONUT_WITH_VULKAN) 55 | set (SHADERMAKE_GENERAL_ARGS_SPIRV "--shaderModel 6_6 -I ${NRC_DIR}/include -I ${SHARC_INCLUDE_DIR}/Sharc/include --vulkanMemoryLayout dx") 56 | endif() 57 | set (SHADERMAKE_GENERAL_ARGS_DXIL "--shaderModel 6_6 --useAPI --WX --PDB -I ${NRC_DIR}/include -I ${SHARC_INCLUDE_DIR}/Sharc/include") 58 | 59 | donut_compile_shaders( 60 | TARGET ${project}_shaders 61 | CONFIG ${CMAKE_CURRENT_SOURCE_DIR}/shaders.cfg 62 | SOURCES ${shaders} 63 | FOLDER ${folder} 64 | SPIRV_DXC ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/${project}/spirv 65 | DXIL ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/shaders/${project}/dxil 66 | SHADERMAKE_OPTIONS_SPIRV ${SHADERMAKE_GENERAL_ARGS_SPIRV} 67 | SHADERMAKE_OPTIONS_DXIL ${SHADERMAKE_GENERAL_ARGS_DXIL} 68 | ) 69 | 70 | add_executable(${project} WIN32 ${sources}) 71 | target_link_libraries(${project} donut_render donut_app donut_engine NRD) 72 | add_dependencies(${project} ${project}_shaders nrd_shaders) 73 | set_target_properties(${project} PROPERTIES FOLDER ${folder}) 74 | 75 | ADD_DEFINITIONS(-DUNICODE) 76 | ADD_DEFINITIONS(-D_UNICODE) 77 | 78 | # NRC libs 79 | if(NRC_DEV_BUILD) 80 | target_link_libraries(${project} debug "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_D3D12_DEBUG}.lib") 81 | target_link_libraries(${project} optimized "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_D3D12_RELEASE}.lib") 82 | else() 83 | target_link_libraries(${project} "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_D3D12_RELEASE}.lib") 84 | endif() 85 | 86 | if(DONUT_WITH_VULKAN AND SETUP_NRC_WITH_VULKAN) 87 | if(NRC_DEV_BUILD) 88 | target_link_libraries(${project} debug "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_VULKAN_DEBUG}.lib") 89 | target_link_libraries(${project} optimized "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_VULKAN_RELEASE}.lib") 90 | else() 91 | target_link_libraries(${project} "${NRC_LIB_DIR}/${NRC_LIBRARY_NAME_VULKAN_RELEASE}.lib") 92 | endif() 93 | endif() 94 | 95 | # NRC includes 96 | include_directories("${NRC_DIR}/include" "${NRD_PATH}/Include") 97 | 98 | # Copy Agility SDK binaries (if needed) one level below project executable to avoid known issues. 99 | # Details in the section "Known Issues" https://devblogs.microsoft.com/directx/gettingstarted-dx12agility/ 100 | add_custom_command(TARGET ${project} POST_BUILD 101 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 102 | ${CMAKE_CURRENT_SOURCE_DIR}/../../External/AgilitySDK/build/native/bin/x64/D3D12Core.dll 103 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/D3D12/D3D12Core.dll 104 | ) 105 | 106 | add_custom_command(TARGET ${project} POST_BUILD 107 | COMMAND ${CMAKE_COMMAND} -E copy_if_different 108 | ${CMAKE_CURRENT_SOURCE_DIR}/../../External/AgilitySDK/build/native/bin/x64/d3d12SDKLayers.dll 109 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/D3D12/d3d12SDKLayers.dll 110 | ) 111 | 112 | # Copy runtime libraries 113 | file(GLOB NVRTC64_PATH "${NRC_DIR}/Bin/nvrtc64_*") 114 | file(GLOB CUDART64_PATH "${NRC_DIR}/Bin/cudart64_*") 115 | file(GLOB NVRTC_BUILTINS64 "${NRC_DIR}/Bin/nvrtc-builtins64_*") 116 | add_custom_command( 117 | TARGET ${project} POST_BUILD 118 | COMMAND ${CMAKE_COMMAND} -E copy "${NVRTC64_PATH}" 119 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 120 | COMMAND ${CMAKE_COMMAND} -E copy "${CUDART64_PATH}" 121 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 122 | COMMAND ${CMAKE_COMMAND} -E copy "${NVRTC_BUILTINS64}" 123 | ${CMAKE_RUNTIME_OUTPUT_DIRECTORY} 124 | ) 125 | 126 | set( copy_nrc_d3d_command 127 | ${CMAKE_COMMAND} -E copy 128 | "${NRC_DIR}/Bin/$<$:${NRC_LIBRARY_NAME_D3D12_DEBUG}>$<$:${NRC_LIBRARY_NAME_D3D12_RELEASE}>.dll" 129 | $ 130 | ) 131 | add_custom_command( TARGET ${project} POST_BUILD 132 | COMMAND "${copy_nrc_d3d_command}" 133 | COMMAND_EXPAND_LISTS 134 | ) 135 | 136 | if(DONUT_WITH_VULKAN AND SETUP_NRC_WITH_VULKAN) 137 | set( copy_nrc_vulkan_command 138 | ${CMAKE_COMMAND} -E copy 139 | "${NRC_DIR}/Bin/$<$:${NRC_LIBRARY_NAME_VULKAN_DEBUG}>$<$:${NRC_LIBRARY_NAME_VULKAN_RELEASE}>.dll" 140 | $ 141 | ) 142 | add_custom_command( TARGET ${project} POST_BUILD 143 | COMMAND "${copy_nrc_vulkan_command}" 144 | COMMAND_EXPAND_LISTS 145 | ) 146 | endif() 147 | 148 | -------------------------------------------------------------------------------- /Samples/Pathtracer/Denoiser.hlsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "GlobalCb.h" 12 | #include "LightingCb.h" 13 | 14 | #include "NRD/NRD.hlsli" 15 | 16 | #define BLOCK_SIZE 16 17 | 18 | ConstantBuffer g_Lighting : register(b0, space0); 19 | ConstantBuffer g_Global : register(b1, space0); 20 | 21 | RWTexture2D u_Output : register(u0, space0); 22 | 23 | RWTexture2D u_OutputDiffuseHitDistance : register(u0, space1); 24 | RWTexture2D u_OutputSpecularHitDistance : register(u1, space1); 25 | RWTexture2D u_OutputViewSpaceZ : register(u2, space1); 26 | RWTexture2D u_OutputNormalRoughness : register(u3, space1); 27 | RWTexture2D u_OutputMotionVectors : register(u4, space1); 28 | RWTexture2D u_OutputEmissive : register(u5, space1); 29 | RWTexture2D u_OutputDiffuseAlbedo : register(u6, space1); 30 | RWTexture2D u_OutputSpecularAlbedo : register(u7, space1); 31 | 32 | [numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)] 33 | void reblurPackData(in uint2 did : SV_DispatchThreadID) 34 | { 35 | float viewSpaceZ = u_OutputViewSpaceZ[did]; 36 | if (viewSpaceZ == 0) 37 | return; 38 | 39 | float4 normalRoughness = u_OutputNormalRoughness[did]; 40 | float3 emissive = u_OutputEmissive[did].xyz; 41 | 42 | #if ENABLE_NRC 43 | float3 nrcRadiance = u_Output[did].xyz; 44 | #endif // ENABLE_NRC 45 | 46 | // Diffuse 47 | { 48 | float4 diffuseData = u_OutputDiffuseHitDistance[did]; 49 | 50 | #if ENABLE_NRC 51 | if (diffuseData.w) 52 | { 53 | diffuseData.xyz += nrcRadiance; 54 | nrcRadiance = 0.0f; 55 | } 56 | #endif // ENABLE_NRC 57 | 58 | if (any(diffuseData.xyz)) 59 | { 60 | float3 diffuseAlbedo = u_OutputDiffuseAlbedo[did].xyz; 61 | diffuseAlbedo += diffuseAlbedo == 0.0f; 62 | diffuseData.xyz -= emissive; 63 | diffuseData.xyz /= diffuseAlbedo; 64 | } 65 | 66 | float normalizedHitDistance = REBLUR_FrontEnd_GetNormHitDist(diffuseData.w, viewSpaceZ, g_Global.nrdHitDistanceParams); 67 | diffuseData = REBLUR_FrontEnd_PackRadianceAndNormHitDist(diffuseData.xyz, normalizedHitDistance); 68 | u_OutputDiffuseHitDistance[did] = diffuseData; 69 | } 70 | 71 | #if ENABLE_SPECULAR_LOBE 72 | // Specular 73 | { 74 | float4 specularData = u_OutputSpecularHitDistance[did]; 75 | 76 | #if ENABLE_NRC 77 | specularData.xyz += nrcRadiance; 78 | #endif // ENABLE_NRC 79 | 80 | if (any(specularData.xyz)) 81 | { 82 | float3 specularAlbedo = u_OutputSpecularAlbedo[did].xyz; 83 | specularAlbedo += specularAlbedo == 0.0f; 84 | specularData.xyz -= emissive; 85 | specularData.xyz /= specularAlbedo; 86 | } 87 | 88 | float normalizedHitDistance = REBLUR_FrontEnd_GetNormHitDist(specularData.w, viewSpaceZ, g_Global.nrdHitDistanceParams, normalRoughness.w); 89 | specularData = REBLUR_FrontEnd_PackRadianceAndNormHitDist(specularData.xyz, normalizedHitDistance); 90 | u_OutputSpecularHitDistance[did] = specularData; 91 | } 92 | #endif // ENABLE_SPECULAR_LOBE 93 | 94 | normalRoughness = NRD_FrontEnd_PackNormalAndRoughness(normalRoughness.xyz, normalRoughness.w); 95 | u_OutputNormalRoughness[did] = normalRoughness; 96 | } 97 | 98 | [numthreads(BLOCK_SIZE, BLOCK_SIZE, 1)] 99 | void resolve(in uint2 did : SV_DispatchThreadID) 100 | { 101 | float4 outputColor = u_OutputEmissive[did]; 102 | float viewSpaceZ = u_OutputViewSpaceZ[did]; 103 | 104 | if (viewSpaceZ != 0) 105 | { 106 | // Diffuse 107 | { 108 | float4 diffuseData = u_OutputDiffuseHitDistance[did]; 109 | float3 diffuseAlbedo = u_OutputDiffuseAlbedo[did].xyz; 110 | diffuseData = REBLUR_BackEnd_UnpackRadianceAndNormHitDist(diffuseData); 111 | 112 | outputColor.xyz += diffuseData.xyz * diffuseAlbedo; 113 | } 114 | 115 | #if ENABLE_SPECULAR_LOBE 116 | // Specular 117 | { 118 | float4 specularData = u_OutputSpecularHitDistance[did]; 119 | float3 specularAlbedo = u_OutputSpecularAlbedo[did].xyz; 120 | specularData = REBLUR_BackEnd_UnpackRadianceAndNormHitDist(specularData); 121 | 122 | outputColor.xyz += specularData.xyz * specularAlbedo; 123 | } 124 | #endif // ENABLE_SPECULAR_LOBE 125 | 126 | u_Output[did] = outputColor; 127 | } 128 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/GlobalCb.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | /* 3 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 4 | * 5 | * NVIDIA CORPORATION and its licensors retain all intellectual property 6 | * and proprietary rights in and to this software, related documentation 7 | * and any modifications thereto. Any use, reproduction, disclosure or 8 | * distribution of this software and related documentation without an express 9 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 10 | */ 11 | 12 | #ifndef GLOBAL_CB_H 13 | #define GLOBAL_CB_H 14 | 15 | struct GlobalConstants 16 | { 17 | int enableJitter; 18 | int sharcDebug; 19 | int enableBackFaceCull; 20 | int bouncesMax; 21 | 22 | int frameIndex; 23 | uint enableAccumulation; 24 | float recipAccumulatedFrames; 25 | int accumulatedFramesMax; 26 | 27 | int enableEmissives; 28 | int enableLighting; 29 | int enableTransmission; 30 | int enableOcclusion; 31 | 32 | int enableAbsorbtion; 33 | int enableTransparentShadows; 34 | int enableSoftShadows; 35 | int enableRussianRoulette; 36 | 37 | int samplesPerPixel; 38 | int targetLight; 39 | uint debugOutputMode; 40 | float intensityScale; 41 | 42 | float throughputThreshold; 43 | float exposureScale; 44 | uint toneMappingOperator; 45 | uint clamp; 46 | 47 | uint nrcEnableTerminationHeuristic; 48 | uint nrcSkipDeltaVertices; 49 | uint pad0; 50 | float nrcTerminationHeuristicThreshold; 51 | 52 | float4 nrdHitDistanceParams; 53 | 54 | float roughnessMin; 55 | float roughnessMax; 56 | float metalnessMin; 57 | float metalnessMax; 58 | }; 59 | 60 | #define EXIT_MAX_BOUNCE 0 61 | #define EXIT_HIT_SKY 1 62 | #define EXIT_RUSSIAN_ROULETTE 2 63 | #define EXIT_WITHIN_SURFACE 3 64 | #define EXIT_SMALL_THROUGHPUT 4 65 | 66 | // Set to 1 to over-ride the UI 67 | #define USE_DEFAULT_SETTINGS 0 68 | 69 | #endif -------------------------------------------------------------------------------- /Samples/Pathtracer/LightingCb.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #ifndef LIGHTING_CB_H 12 | #define LIGHTING_CB_H 13 | 14 | #include 15 | #include 16 | 17 | // Does not affect local lights shading 18 | #define ENABLE_SPECULAR_LOBE 1 19 | 20 | #if ENABLE_NRC 21 | #define NRC_RW_STRUCTURED_BUFFER(T) RWStructuredBuffer 22 | #include "NRCStructures.h" 23 | #endif // ENABLE_NRC 24 | 25 | #define MAX_LIGHTS 8 26 | 27 | struct LightingConstants 28 | { 29 | float4 skyColor; 30 | 31 | int lightCount; 32 | int sharcAccumulationFrameNum; 33 | int sharcStaleFrameNum; 34 | int sharcEnableAntifirefly; 35 | 36 | int sharcEntriesNum; 37 | int sharcDownscaleFactor; 38 | float sharcSceneScale; 39 | float sharcRoughnessThreshold; 40 | 41 | float4 sharcCameraPosition; 42 | float4 sharcCameraPositionPrev; 43 | 44 | PlanarViewConstants view; 45 | PlanarViewConstants viewPrev; 46 | PlanarViewConstants updatePassView; 47 | 48 | LightConstants sunLight; 49 | LightConstants headLight; 50 | LightConstants lights[MAX_LIGHTS]; 51 | 52 | #if ENABLE_NRC 53 | NrcConstants nrcConstants; 54 | #endif // ENABLE_NRC 55 | }; 56 | 57 | #endif // LIGHTING_CB_H -------------------------------------------------------------------------------- /Samples/Pathtracer/NrcIntegration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "NrcIntegration.h" 12 | #include "NrcUtils.h" 13 | #include 14 | #ifdef NRC_WITH_VULKAN 15 | #include 16 | #endif 17 | 18 | #ifdef NRC_ENABLE_DLL_CHECK 19 | #include "NrcSecurity.h" 20 | #include 21 | #include 22 | #endif 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include 32 | #include 33 | #include "../../donut/nvrhi/src/vulkan/vulkan-backend.h" 34 | 35 | using namespace donut::math; 36 | 37 | #define SAFE_RELEASE(x) \ 38 | { \ 39 | if (x) \ 40 | { \ 41 | x->Release(); \ 42 | x = NULL; \ 43 | } \ 44 | } 45 | 46 | static const D3D12_HEAP_PROPERTIES g_uploadHeapProperties = { D3D12_HEAP_TYPE_UPLOAD, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; 47 | static const D3D12_HEAP_PROPERTIES g_defaultHeapProperties = { D3D12_HEAP_TYPE_DEFAULT, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; 48 | static const D3D12_HEAP_PROPERTIES g_readbackHeapProperties = { D3D12_HEAP_TYPE_READBACK, D3D12_CPU_PAGE_PROPERTY_UNKNOWN, D3D12_MEMORY_POOL_UNKNOWN, 0, 0 }; 49 | 50 | 51 | static const bool g_enableSDKMemoryAllocation = true; 52 | static const bool g_useCustomCPUMemoryAllocator = false; 53 | 54 | // Utility 55 | static void NrcLoggerCallback(const char* message, nrc::LogLevel logLevel) 56 | { 57 | 58 | static std::mutex loggerMutex; 59 | 60 | // Make the logging thread-safe 61 | loggerMutex.lock(); 62 | { 63 | const int kMinLogLevel = (int)nrc::LogLevel::Info; 64 | if (((int)logLevel >= kMinLogLevel) || (logLevel == nrc::LogLevel::Error)) 65 | { 66 | std::wstring wstr = NrcUtils::StringToWstring(message); 67 | OutputDebugString(wstr.c_str()); 68 | #if 0 69 | if (logLevel == nrc::LogLevel::Error) 70 | { 71 | // Halt on NRC error 72 | NrcUtils::Validate(E_FAIL, LPWSTR(wstr.c_str())); 73 | } 74 | #endif 75 | } 76 | } 77 | loggerMutex.unlock(); 78 | } 79 | 80 | static void NrcMemoryEventsCallback(nrc::MemoryEventType eventType, size_t size, const char* bufferName) 81 | { 82 | 83 | static std::mutex loggerMutex; 84 | 85 | loggerMutex.lock(); 86 | { 87 | std::string message = "NRC SDK Memory Stats: "; 88 | 89 | switch (eventType) 90 | { 91 | case nrc::MemoryEventType::Allocation: 92 | message += std::to_string(size) + " bytes allocated (" + bufferName + ")\n"; 93 | break; 94 | case nrc::MemoryEventType::Deallocation: 95 | message += std::to_string(size) + " bytes deallocated (" + bufferName + ")\n"; 96 | break; 97 | case nrc::MemoryEventType::MemoryStats: 98 | message += std::to_string(size) + " bytes currently allocated in total\n"; 99 | break; 100 | } 101 | 102 | #if _DEBUG 103 | OutputDebugStringA(message.c_str()); 104 | #endif 105 | } 106 | loggerMutex.unlock(); 107 | } 108 | 109 | static void* NrcCustomAllocatorCallback(const size_t bytes) 110 | { 111 | return (void*)new char[bytes]; 112 | } 113 | 114 | static void NrcCustomDeallocatorCallback(void* pointer, const size_t bytes) 115 | { 116 | delete[] pointer; 117 | } 118 | 119 | static void FillBufferDescs(nvrhi::BufferDesc* bufferDescs, nrc::BuffersAllocationInfo const& buffersAllocationInfo) 120 | { 121 | // Create an array of nvrhi::BufferDesc for the nrc buffers 122 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 123 | { 124 | nvrhi::BufferDesc& bufferDesc = bufferDescs[i]; 125 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 126 | const nrc::AllocationInfo& allocationInfo = buffersAllocationInfo[nrc::BufferIdx(i)]; 127 | 128 | bufferDesc = nvrhi::BufferDesc(); 129 | bufferDesc.isConstantBuffer = false; 130 | bufferDesc.isVolatile = false; 131 | bufferDesc.cpuAccess = nvrhi::CpuAccessMode::None; 132 | bufferDesc.keepInitialState = true; 133 | if (allocationInfo.elementCount > 0) 134 | { 135 | bufferDesc.byteSize = static_cast(allocationInfo.elementCount) * allocationInfo.elementSize; 136 | bufferDesc.structStride = allocationInfo.elementSize; 137 | bufferDesc.canHaveUAVs = allocationInfo.allowUAV; 138 | bufferDesc.canHaveRawViews = (bufferIdx == nrc::BufferIdx::Counter); 139 | bufferDesc.initialState = nvrhi::ResourceStates::UnorderedAccess; 140 | bufferDesc.setDebugName(allocationInfo.debugName); 141 | } 142 | } 143 | } 144 | 145 | static void CreateResources(nvrhi::BufferDesc const* bufferDescs, NrcBufferHandles& m_nrcBufferHandles, nvrhi::IDevice* device) 146 | { 147 | // Create NVRHI buffers 148 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 149 | { 150 | nvrhi::BufferDesc const& bufferDesc = bufferDescs[i]; 151 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 152 | if (bufferDesc.byteSize > 0) 153 | m_nrcBufferHandles[bufferIdx] = device->createBuffer(bufferDesc); 154 | else 155 | m_nrcBufferHandles[bufferIdx] = nullptr; 156 | } 157 | } 158 | 159 | #ifdef NRC_ENABLE_DLL_CHECK 160 | const std::wstring GetDllPath(const std::wstring& dllName) 161 | { 162 | HMODULE hMod = GetModuleHandle(dllName.c_str()); 163 | 164 | wchar_t path[MAX_PATH]; 165 | DWORD size = GetModuleFileNameW(hMod, path, MAX_PATH); 166 | assert(size != 0); 167 | 168 | return std::wstring(path, size); 169 | } 170 | #endif 171 | 172 | class NrcD3d12Integration : public NrcIntegration 173 | { 174 | public: 175 | bool Initialize(nvrhi::IDevice* device) 176 | { 177 | nrc::GlobalSettings globalSettings; 178 | 179 | // First, set logger callbacks for catching messages from NRC 180 | globalSettings.loggerFn = &NrcLoggerCallback; 181 | globalSettings.memoryLoggerFn = &NrcMemoryEventsCallback; 182 | 183 | // Optionally, use custom CPU memory provided by the user application 184 | if (g_useCustomCPUMemoryAllocator) 185 | { 186 | globalSettings.allocatorFn = &NrcCustomAllocatorCallback; 187 | globalSettings.deallocatorFn = &NrcCustomDeallocatorCallback; 188 | } 189 | 190 | globalSettings.enableGPUMemoryAllocation = g_enableSDKMemoryAllocation; 191 | // Only enable debug buffers in development and not production 192 | globalSettings.enableDebugBuffers = true; 193 | m_enableDebugBuffers = globalSettings.enableDebugBuffers; 194 | 195 | // Initialize the NRC Library 196 | nrc::Status status = nrc::Status::OK; 197 | 198 | // Verify the signature of the loaded NRC DLL 199 | #ifdef NRC_ENABLE_DLL_CHECK 200 | nrc::security::VerifySignature(GetDllPath(L"NRC_D3D12.dll").c_str()) ? status = nrc::Status::OK : status = nrc::Status::InternalError; 201 | if (status != nrc::Status::OK) 202 | return m_initialized; 203 | #endif 204 | 205 | status = nrc::d3d12::Initialize(globalSettings); 206 | if (status != nrc::Status::OK) 207 | return m_initialized; 208 | 209 | // Create an NRC Context 210 | ID3D12Device* nativeDevice = device->getNativeObject(nvrhi::ObjectTypes::D3D12_Device); 211 | ID3D12Device5* nativeDevice5 = nullptr; 212 | if (SUCCEEDED(nativeDevice->QueryInterface(IID_PPV_ARGS(&nativeDevice5)))) 213 | { 214 | status = nrc::d3d12::Context::Create(nativeDevice5, m_nrcContext); 215 | assert(status == nrc::Status::OK); 216 | 217 | m_device = device; 218 | 219 | if (!m_initialized && (status == nrc::Status::OK)) 220 | m_initialized = true; 221 | } 222 | return m_initialized; 223 | } 224 | 225 | void Shutdown() 226 | { 227 | if (m_nrcContext) 228 | { 229 | nrc::d3d12::Context::Destroy(*m_nrcContext); 230 | m_nrcContext = nullptr; 231 | } 232 | 233 | nrc::d3d12::Shutdown(); 234 | 235 | m_initialized = false; 236 | } 237 | 238 | void Configure(const nrc::ContextSettings& contextSettings) 239 | { 240 | nrc::Status status; 241 | 242 | // Configuration has changed 243 | m_contextSettings = contextSettings; 244 | 245 | nrc::d3d12::Context::GetBuffersAllocationInfo(contextSettings, m_buffersAllocation); 246 | nvrhi::BufferDesc bufferDescs[(int)nrc::BufferIdx::Count]; 247 | FillBufferDescs(bufferDescs, m_buffersAllocation); 248 | 249 | if (g_enableSDKMemoryAllocation) 250 | { 251 | // NRC library manages memory in this case. 252 | // Pass it the new configuration 253 | status = m_nrcContext->Configure(contextSettings); 254 | 255 | // The NRC SDK is managing buffer allocations, so we need to pull those native buffers into NVRHI 256 | nrc::d3d12::Buffers const& buffers = *(m_nrcContext->GetBuffers()); 257 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 258 | { 259 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 260 | nrc::d3d12::BufferInfo const& bufferInfo = buffers[bufferIdx]; 261 | if (bufferInfo.resource != nullptr) 262 | m_bufferHandles[bufferIdx] = m_device->createHandleForNativeBuffer(nvrhi::ObjectTypes::D3D12_Resource, bufferInfo.resource, bufferDescs[i]); 263 | else 264 | m_bufferHandles[bufferIdx] = nullptr; 265 | } 266 | } 267 | else 268 | { 269 | // Create NVRHI buffers 270 | CreateResources(bufferDescs, m_bufferHandles, m_device); 271 | 272 | // Pass the buffers to NRC 273 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 274 | { 275 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 276 | m_buffers[bufferIdx].resource = reinterpret_cast(m_bufferHandles[bufferIdx]->getNativeObject(nvrhi::ObjectTypes::D3D12_Resource).pointer); 277 | m_buffers[bufferIdx].allocatedSize = bufferDescs[i].byteSize; 278 | } 279 | status = m_nrcContext->Configure(contextSettings, &m_buffers); 280 | } 281 | 282 | if (status != nrc::Status::OK) 283 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC Configure step failed.")); 284 | } 285 | 286 | void BeginFrame(nvrhi::ICommandList* cmdList, const nrc::FrameSettings& frameSettings) 287 | { 288 | nrc::Status status; 289 | 290 | ID3D12GraphicsCommandList4* nativeCmdList = reinterpret_cast(cmdList->getNativeObject(nvrhi::ObjectTypes::D3D12_GraphicsCommandList).pointer); 291 | if (nativeCmdList) 292 | { 293 | status = m_nrcContext->BeginFrame(nativeCmdList, frameSettings); 294 | if (status != nrc::Status::OK) 295 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC BeginFrame call failed.")); 296 | } 297 | } 298 | 299 | float QueryAndTrain(nvrhi::ICommandList* cmdList, bool calculateTrainingLoss) 300 | { 301 | ID3D12GraphicsCommandList4* nativeCmdList = reinterpret_cast(cmdList->getNativeObject(nvrhi::ObjectTypes::D3D12_GraphicsCommandList).pointer); 302 | float trainingLoss = 0.0f; 303 | 304 | if (nativeCmdList) 305 | { 306 | nrc::Status status = m_nrcContext->QueryAndTrain(nativeCmdList, (calculateTrainingLoss ? &trainingLoss : nullptr)); 307 | if (status != nrc::Status::OK) 308 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC QueryAndTrain call failed.")); 309 | } 310 | 311 | return trainingLoss; 312 | } 313 | 314 | void Resolve(nvrhi::ICommandList* cmdList, nvrhi::TextureHandle outputBuffer) 315 | { 316 | ID3D12Resource* outputResource = reinterpret_cast(outputBuffer->getNativeObject(nvrhi::ObjectTypes::D3D12_Resource).pointer); 317 | ID3D12GraphicsCommandList4* nativeCmdList = reinterpret_cast(cmdList->getNativeObject(nvrhi::ObjectTypes::D3D12_GraphicsCommandList).pointer); 318 | if (nativeCmdList) 319 | { 320 | nrc::Status status = m_nrcContext->Resolve(nativeCmdList, outputResource); 321 | if (status != nrc::Status::OK) 322 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC Resolve call failed.")); 323 | } 324 | } 325 | 326 | void EndFrame(nvrhi::CommandQueue* cmdQueue) 327 | { 328 | ID3D12CommandQueue* nativeCmdQueue = 329 | reinterpret_cast(m_device->getNativeQueue(nvrhi::ObjectTypes::D3D12_CommandQueue, nvrhi::CommandQueue::Graphics).pointer); 330 | if (nativeCmdQueue) 331 | { 332 | nrc::Status status = m_nrcContext->EndFrame(nativeCmdQueue); 333 | if (status != nrc::Status::OK) 334 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC EndFrame call failed.")); 335 | } 336 | } 337 | 338 | size_t GetCurrentMemoryConsumption() const 339 | { 340 | const nrc::d3d12::Buffers& buffers = *(m_nrcContext->GetBuffers()); 341 | 342 | size_t totalAllocatedMemory = 0; 343 | for (nrc::d3d12::BufferInfo const& buffer : buffers.buffers) 344 | totalAllocatedMemory += buffer.allocatedSize; 345 | 346 | return totalAllocatedMemory; 347 | } 348 | 349 | void PopulateShaderConstants(struct NrcConstants& outConstants) const 350 | { 351 | m_nrcContext->PopulateShaderConstants(outConstants); 352 | } 353 | 354 | nrc::d3d12::Buffers m_buffers; 355 | 356 | private: 357 | nrc::d3d12::Context* m_nrcContext; 358 | }; 359 | 360 | #ifdef NRC_WITH_VULKAN 361 | class NrcVulkanIntegration : public NrcIntegration 362 | { 363 | public: 364 | bool Initialize(nvrhi::IDevice* device) 365 | { 366 | nrc::GlobalSettings globalSettings; 367 | 368 | // First, set logger callbacks for catching messages from NRC 369 | globalSettings.loggerFn = &NrcLoggerCallback; 370 | globalSettings.memoryLoggerFn = &NrcMemoryEventsCallback; 371 | 372 | // Optionally, use custom CPU memory provided by the user application 373 | if (g_useCustomCPUMemoryAllocator) 374 | { 375 | globalSettings.allocatorFn = &NrcCustomAllocatorCallback; 376 | globalSettings.deallocatorFn = &NrcCustomDeallocatorCallback; 377 | } 378 | 379 | globalSettings.enableGPUMemoryAllocation = g_enableSDKMemoryAllocation; 380 | // Only enable debug buffers in development and not production 381 | globalSettings.enableDebugBuffers = true; 382 | m_enableDebugBuffers = globalSettings.enableDebugBuffers; 383 | 384 | // Initialize the NRC Library 385 | nrc::Status status = nrc::Status::OK; 386 | 387 | // Verify the signature of the loaded NRC DLL 388 | #ifdef NRC_ENABLE_DLL_CHECK 389 | nrc::security::VerifySignature(GetDllPath(L"NRC_Vulkan.dll").c_str()) ? status = nrc::Status::OK : status = nrc::Status::InternalError; 390 | if (status != nrc::Status::OK) 391 | return m_initialized; 392 | #endif 393 | 394 | status = nrc::vulkan::Initialize(globalSettings); 395 | if (status != nrc::Status::OK) 396 | return m_initialized; 397 | 398 | // Create an NRC Context 399 | VkDevice nativeDevice = device->getNativeObject(nvrhi::ObjectTypes::VK_Device); 400 | VkPhysicalDevice nativeGPU = device->getNativeObject(nvrhi::ObjectTypes::VK_PhysicalDevice); 401 | VkInstance apiInstance = device->getNativeObject(nvrhi::ObjectTypes::VK_Instance); 402 | 403 | if (nativeDevice != nullptr && nativeGPU != nullptr) 404 | { 405 | status = nrc::vulkan::Context::Create(nativeDevice, nativeGPU, apiInstance, m_nrcContext); 406 | assert(status == nrc::Status::OK); 407 | m_device = device; 408 | 409 | if (!m_initialized && (status == nrc::Status::OK)) 410 | m_initialized = true; 411 | } 412 | return m_initialized; 413 | } 414 | 415 | void Shutdown() 416 | { 417 | if (m_nrcContext) 418 | { 419 | nrc::vulkan::Context::Destroy(*m_nrcContext); 420 | m_nrcContext = nullptr; 421 | } 422 | 423 | nrc::vulkan::Shutdown(); 424 | 425 | m_initialized = false; 426 | } 427 | 428 | void Configure(const nrc::ContextSettings& contextSettings) 429 | { 430 | nrc::Status status; 431 | 432 | // Configuration has changed 433 | m_contextSettings = contextSettings; 434 | 435 | nrc::vulkan::Context::GetBuffersAllocationInfo(contextSettings, m_buffersAllocation); 436 | nvrhi::BufferDesc bufferDescs[(int)nrc::BufferIdx::Count]; 437 | FillBufferDescs(bufferDescs, m_buffersAllocation); 438 | 439 | if (g_enableSDKMemoryAllocation) 440 | { 441 | // NRC library manages memory in this case. 442 | // Pass it the new configuration 443 | status = m_nrcContext->Configure(contextSettings); 444 | 445 | // The NRC SDK is managing buffer allocations, so we need to pull those native buffers into NVRHI 446 | nrc::vulkan::Buffers const& buffers = *(m_nrcContext->GetBuffers()); 447 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 448 | { 449 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 450 | nrc::vulkan::BufferInfo const& bufferInfo = buffers[bufferIdx]; 451 | if (bufferInfo.resource != nullptr) 452 | m_bufferHandles[bufferIdx] = m_device->createHandleForNativeBuffer(nvrhi::ObjectTypes::VK_Buffer, bufferInfo.resource, bufferDescs[i]); 453 | else 454 | m_bufferHandles[bufferIdx] = nullptr; 455 | } 456 | } 457 | else 458 | { 459 | // Create NVRHI buffers 460 | CreateResources(bufferDescs, m_bufferHandles, m_device); 461 | 462 | // Pass the buffers to NRC 463 | for (uint i = 0; i < (uint)nrc::BufferIdx::Count; ++i) 464 | { 465 | const nrc::BufferIdx bufferIdx = (nrc::BufferIdx)i; 466 | m_buffers[bufferIdx].resource = reinterpret_cast(m_bufferHandles[bufferIdx]->getNativeObject(nvrhi::ObjectTypes::VK_Buffer).pointer); 467 | m_buffers[bufferIdx].allocatedSize = bufferDescs[i].byteSize; 468 | m_buffers[bufferIdx].allocatedOffset = 0; 469 | 470 | auto addressInfo = vk::BufferDeviceAddressInfo().setBuffer(m_buffers[bufferIdx].resource); 471 | 472 | VkDevice nativeDevice = m_device->getNativeObject(nvrhi::ObjectTypes::VK_Device); 473 | m_buffers[bufferIdx].deviceAddress = vk::Device(nativeDevice).getBufferAddress(addressInfo); 474 | } 475 | status = m_nrcContext->Configure(contextSettings, &m_buffers); 476 | } 477 | 478 | if (status != nrc::Status::OK) 479 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC Configure step failed.")); 480 | } 481 | 482 | void BeginFrame(nvrhi::ICommandList* cmdList, const nrc::FrameSettings& frameSettings) 483 | { 484 | VkCommandBuffer cmdBuffer = cmdList->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer); 485 | if (cmdBuffer) 486 | { 487 | nrc::Status status = m_nrcContext->BeginFrame(cmdBuffer, frameSettings); 488 | if (status != nrc::Status::OK) 489 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC BeginFrame call failed.")); 490 | } 491 | } 492 | 493 | void EndFrame(nvrhi::CommandQueue* cmdQueue) 494 | { 495 | VkQueue nativeCmdQueue = reinterpret_cast(cmdQueue); 496 | if (nativeCmdQueue) 497 | { 498 | nrc::Status status = m_nrcContext->EndFrame(nativeCmdQueue); 499 | if (status != nrc::Status::OK) 500 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC EndFrame call failed.")); 501 | } 502 | } 503 | 504 | float QueryAndTrain(nvrhi::ICommandList* cmdList, bool calculateTrainingLoss) 505 | { 506 | VkCommandBuffer buffer = cmdList->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer); 507 | float trainingLoss = 0.0f; 508 | 509 | if (buffer) 510 | { 511 | nrc::Status status = m_nrcContext->QueryAndTrain(buffer, (calculateTrainingLoss ? &trainingLoss : nullptr)); 512 | if (status != nrc::Status::OK) 513 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC QueryAndTrain call failed.")); 514 | } 515 | 516 | return trainingLoss; 517 | } 518 | 519 | void Resolve(nvrhi::ICommandList* cmdList, nvrhi::TextureHandle outputBuffer) 520 | { 521 | VkImage outputImage = outputBuffer->getNativeObject(nvrhi::ObjectTypes::VK_Image); 522 | VkImageView outputView = outputBuffer->getNativeView(nvrhi::ObjectTypes::VK_ImageView); 523 | VkCommandBuffer cmdBuffer = cmdList->getNativeObject(nvrhi::ObjectTypes::VK_CommandBuffer); 524 | if (cmdBuffer) 525 | { 526 | nrc::Status status = m_nrcContext->Resolve(cmdBuffer, outputView); 527 | if (status != nrc::Status::OK) 528 | NrcUtils::Validate(E_FAIL, LPWSTR(L"NRC Resolve call failed.")); 529 | } 530 | } 531 | 532 | void PopulateShaderConstants(struct NrcConstants& outConstants) const 533 | { 534 | m_nrcContext->PopulateShaderConstants(outConstants); 535 | } 536 | 537 | void AllocateOrCheckAllResources() 538 | { 539 | nrc::BuffersAllocationInfo bufferAllocations; 540 | nrc::vulkan::Context::GetBuffersAllocationInfo(m_contextSettings, bufferAllocations); 541 | } 542 | 543 | size_t GetCurrentMemoryConsumption() const 544 | { 545 | const nrc::vulkan::Buffers& buffers = *(m_nrcContext->GetBuffers()); 546 | 547 | size_t totalAllocatedMemory = 0; 548 | for (nrc::vulkan::BufferInfo const& buffer : buffers.buffers) 549 | totalAllocatedMemory += buffer.allocatedSize; 550 | 551 | return totalAllocatedMemory; 552 | } 553 | 554 | nrc::vulkan::Buffers m_buffers; 555 | 556 | private: 557 | nrc::vulkan::Context* m_nrcContext; 558 | }; 559 | 560 | std::unique_ptr CreateNrcIntegration(nvrhi::GraphicsAPI api) 561 | { 562 | if (api == nvrhi::GraphicsAPI::VULKAN) 563 | return std::make_unique(); 564 | else 565 | return std::make_unique(); 566 | } 567 | 568 | #endif -------------------------------------------------------------------------------- /Samples/Pathtracer/NrcIntegration.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | // NVRHI handles to NRC Buffers 18 | struct NrcBufferHandles 19 | { 20 | nvrhi::BufferHandle nrcBufferHandles[(int)nrc::BufferIdx::Count]; 21 | 22 | // Allow direct access to the array with nrc::BufferIdx 23 | const nvrhi::BufferHandle& operator[](nrc::BufferIdx idx) const 24 | { 25 | return nrcBufferHandles[(int)idx]; 26 | } 27 | nvrhi::BufferHandle& operator[](nrc::BufferIdx idx) 28 | { 29 | return nrcBufferHandles[(int)idx]; 30 | } 31 | }; 32 | 33 | class NrcIntegration 34 | { 35 | public: 36 | virtual bool Initialize(nvrhi::IDevice* device) = 0; 37 | 38 | virtual void Shutdown() = 0; 39 | 40 | virtual void Configure(const nrc::ContextSettings& contextSettings) = 0; 41 | 42 | virtual void BeginFrame(nvrhi::ICommandList* cmdList, const nrc::FrameSettings& frameSettings) = 0; 43 | 44 | virtual float QueryAndTrain(nvrhi::ICommandList* cmdList, bool calculateTrainingLoss) = 0; 45 | 46 | virtual void Resolve(nvrhi::ICommandList* cmdList, nvrhi::TextureHandle outputBuffer) = 0; 47 | 48 | virtual void EndFrame(nvrhi::CommandQueue* cmdQueue) = 0; 49 | 50 | virtual size_t GetCurrentMemoryConsumption() const = 0; 51 | 52 | virtual void PopulateShaderConstants(struct NrcConstants& outConstants) const = 0; 53 | 54 | bool IsInitialized() const 55 | { 56 | return m_initialized; 57 | }; 58 | 59 | NrcBufferHandles m_bufferHandles; 60 | 61 | protected: 62 | nvrhi::IDevice* m_device; 63 | bool m_initialized = false; 64 | bool m_enableDebugBuffers; 65 | nrc::BuffersAllocationInfo m_buffersAllocation; 66 | nrc::ContextSettings m_contextSettings; 67 | nrc::FrameSettings m_frameSettings; 68 | }; 69 | 70 | std::unique_ptr CreateNrcIntegration(nvrhi::GraphicsAPI); 71 | -------------------------------------------------------------------------------- /Samples/Pathtracer/NrcUtils.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include "NrcUtils.h" 14 | 15 | #include 16 | #include 17 | #include 18 | 19 | using namespace std; 20 | 21 | namespace NrcUtils 22 | { 23 | void Validate(HRESULT hr, LPWSTR msg) 24 | { 25 | if (FAILED(hr)) 26 | { 27 | MessageBox(NULL, msg, L"Error", MB_OK); 28 | PostQuitMessage(EXIT_FAILURE); 29 | } 30 | } 31 | 32 | std::wstring StringToWstring(const std::string& wstr) 33 | { 34 | std::wstring_convert> converter; 35 | return converter.from_bytes(wstr); 36 | } 37 | } // namespace NrcUtils 38 | -------------------------------------------------------------------------------- /Samples/Pathtracer/NrcUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #ifndef WIN32_LEAN_AND_MEAN 14 | #define WIN32_LEAN_AND_MEAN // Exclude rarely-used items from Windows headers. 15 | #endif 16 | 17 | #include 18 | #include 19 | 20 | namespace NrcUtils 21 | { 22 | void Validate(HRESULT hr, LPWSTR message); 23 | 24 | std::wstring StringToWstring(const std::string& wstr); 25 | } // namespace NrcUtils -------------------------------------------------------------------------------- /Samples/Pathtracer/Nrd/DenoiserNRD.hlsli: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #ifndef __DENOISER_NRD_HLSLI__ 12 | #define __DENOISER_NRD_HLSLI__ 13 | 14 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 15 | #define NRD_HEADER_ONLY 16 | //#include 17 | #include "../../external/RayTracingDenoiser/Shaders/Include/NRDEncoding.hlsli" 18 | #if NRD_NORMAL_ENCODING != 2 // 2 == NRD_NORMAL_ENCODING_R10G10B10A2_UNORM 19 | #error not configured correctly 20 | #endif 21 | //#include 22 | #include "../../external/RayTracingDenoiser/Shaders/Include/NRD.hlsli" 23 | /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 24 | 25 | #pragma pack_matrix(row_major) 26 | 27 | RWTexture2D u_DenoiserViewspaceZ : register(u31); 28 | RWTexture2D u_DenoiserMotionVectors : register(u32); 29 | RWTexture2D u_DenoiserNormalRoughness : register(u33); 30 | RWTexture2D u_DenoiserDiffRadianceHitDist : register(u34); 31 | RWTexture2D u_DenoiserSpecRadianceHitDist : register(u35); 32 | RWTexture2D u_DenoiserDisocclusionThresholdMix : register(u36); 33 | RWTexture2D u_CombinedHistoryClampRelax : register(u37); 34 | 35 | namespace DenoiserNRD 36 | { 37 | static float3 PostDenoiseProcess(const float3 diffBSDFEstimate, const float3 specBSDFEstimate, inout float4 diffRadianceHitDistDenoised, inout float4 specRadianceHitDistDenoised) 38 | { 39 | #if USE_RELAX 40 | diffRadianceHitDistDenoised.xyz = RELAX_BackEnd_UnpackRadiance(diffRadianceHitDistDenoised).xyz; 41 | specRadianceHitDistDenoised.xyz = RELAX_BackEnd_UnpackRadiance(specRadianceHitDistDenoised).xyz; 42 | #else 43 | diffRadianceHitDistDenoised.xyz = REBLUR_BackEnd_UnpackRadianceAndNormHitDist(diffRadianceHitDistDenoised).xyz; 44 | specRadianceHitDistDenoised.xyz = REBLUR_BackEnd_UnpackRadianceAndNormHitDist(specRadianceHitDistDenoised).xyz; 45 | #endif 46 | 47 | diffRadianceHitDistDenoised.xyz *= diffBSDFEstimate; 48 | specRadianceHitDistDenoised.xyz *= specBSDFEstimate; 49 | 50 | return diffRadianceHitDistDenoised.xyz + specRadianceHitDistDenoised.xyz; 51 | } 52 | } // namespace DenoiserNRD 53 | 54 | #endif // #ifndef __DENOISER_NRD_HLSLI__ -------------------------------------------------------------------------------- /Samples/Pathtracer/NrdConfig.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "NrdConfig.h" 12 | 13 | namespace NrdConfig 14 | { 15 | nrd::RelaxSettings GetDefaultRELAXSettings() 16 | { 17 | nrd::RelaxSettings settings; 18 | settings.enableAntiFirefly = true; 19 | settings.hitDistanceReconstructionMode = nrd::HitDistanceReconstructionMode::AREA_3X3; 20 | 21 | settings.historyFixFrameNum = 4; 22 | settings.spatialVarianceEstimationHistoryThreshold = 4; 23 | 24 | // settings.enableReprojectionTestSkippingWithoutMotion = false; 25 | 26 | // (pixels) - pre-accumulation spatial reuse pass blur radius (0 = disabled, must be used in case of probabilistic sampling) <- we're using probabilistic sampling 27 | settings.diffusePrepassBlurRadius = 0.0f; 28 | settings.specularPrepassBlurRadius = 20.0f; 29 | 30 | // diffuse 31 | settings.diffuseMaxFastAccumulatedFrameNum = 4; 32 | settings.diffusePhiLuminance = 0.5f; 33 | 34 | // specular 35 | settings.specularMaxFastAccumulatedFrameNum = 6; 36 | settings.specularPhiLuminance = 0.35f; 37 | settings.specularLobeAngleSlack = 0.15f; 38 | 39 | settings.confidenceDrivenLuminanceEdgeStoppingRelaxation = 0.5f; 40 | settings.roughnessEdgeStoppingRelaxation = 0.3f; 41 | settings.lobeAngleFraction = 0.93f; 42 | 43 | settings.atrousIterationNum = 5; 44 | 45 | settings.diffuseMaxAccumulatedFrameNum = 60; 46 | settings.specularMaxAccumulatedFrameNum = 60; 47 | 48 | settings.depthThreshold = 0.004f; 49 | 50 | return settings; 51 | } 52 | 53 | nrd::ReblurSettings GetDefaultREBLURSettings() 54 | { 55 | nrd::ReblurSettings settings; 56 | settings.enableAntiFirefly = true; 57 | settings.hitDistanceReconstructionMode = nrd::HitDistanceReconstructionMode::AREA_5X5; 58 | settings.maxAccumulatedFrameNum = 60; 59 | 60 | // (pixels) - pre-accumulation spatial reuse pass blur radius (0 = disabled, must be used in case of probabilistic sampling) <- we're using probabilistic sampling 61 | // settings.diffusePrepassBlurRadius = 0.0f; 62 | // settings.specularPrepassBlurRadius = 0.0f; 63 | 64 | return settings; 65 | } 66 | } // namespace NrdConfig -------------------------------------------------------------------------------- /Samples/Pathtracer/NrdConfig.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | namespace NrdConfig 16 | { 17 | enum class DenoiserMethod : uint32_t 18 | { 19 | REBLUR, 20 | RELAX, 21 | MaxCount 22 | }; 23 | 24 | nrd::RelaxSettings GetDefaultRELAXSettings(); 25 | nrd::ReblurSettings GetDefaultREBLURSettings(); 26 | } // namespace NrdConfig -------------------------------------------------------------------------------- /Samples/Pathtracer/NrdIntegration.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "NrdIntegration.h" 12 | 13 | static_assert(NRD_VERSION_MAJOR >= 4 && NRD_VERSION_MINOR >= 0, "Unsupported NRD version!"); 14 | 15 | #include "RenderTargets.h" 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | 23 | static nvrhi::Format GetNvrhiFormat(nrd::Format format) 24 | { 25 | switch (format) 26 | { 27 | case nrd::Format::R8_UNORM: 28 | return nvrhi::Format::R8_UNORM; 29 | case nrd::Format::R8_SNORM: 30 | return nvrhi::Format::R8_SNORM; 31 | case nrd::Format::R8_UINT: 32 | return nvrhi::Format::R8_UINT; 33 | case nrd::Format::R8_SINT: 34 | return nvrhi::Format::R8_SINT; 35 | case nrd::Format::RG8_UNORM: 36 | return nvrhi::Format::RG8_UNORM; 37 | case nrd::Format::RG8_SNORM: 38 | return nvrhi::Format::RG8_SNORM; 39 | case nrd::Format::RG8_UINT: 40 | return nvrhi::Format::RG8_UINT; 41 | case nrd::Format::RG8_SINT: 42 | return nvrhi::Format::RG8_SINT; 43 | case nrd::Format::RGBA8_UNORM: 44 | return nvrhi::Format::RGBA8_UNORM; 45 | case nrd::Format::RGBA8_SNORM: 46 | return nvrhi::Format::RGBA8_SNORM; 47 | case nrd::Format::RGBA8_UINT: 48 | return nvrhi::Format::RGBA8_UINT; 49 | case nrd::Format::RGBA8_SINT: 50 | return nvrhi::Format::RGBA8_SINT; 51 | case nrd::Format::RGBA8_SRGB: 52 | return nvrhi::Format::SRGBA8_UNORM; 53 | case nrd::Format::R16_UNORM: 54 | return nvrhi::Format::R16_UNORM; 55 | case nrd::Format::R16_SNORM: 56 | return nvrhi::Format::R16_SNORM; 57 | case nrd::Format::R16_UINT: 58 | return nvrhi::Format::R16_UINT; 59 | case nrd::Format::R16_SINT: 60 | return nvrhi::Format::R16_SINT; 61 | case nrd::Format::R16_SFLOAT: 62 | return nvrhi::Format::R16_FLOAT; 63 | case nrd::Format::RG16_UNORM: 64 | return nvrhi::Format::RG16_UNORM; 65 | case nrd::Format::RG16_SNORM: 66 | return nvrhi::Format::RG16_SNORM; 67 | case nrd::Format::RG16_UINT: 68 | return nvrhi::Format::RG16_UINT; 69 | case nrd::Format::RG16_SINT: 70 | return nvrhi::Format::RG16_SINT; 71 | case nrd::Format::RG16_SFLOAT: 72 | return nvrhi::Format::RG16_FLOAT; 73 | case nrd::Format::RGBA16_UNORM: 74 | return nvrhi::Format::RGBA16_UNORM; 75 | case nrd::Format::RGBA16_SNORM: 76 | return nvrhi::Format::RGBA16_SNORM; 77 | case nrd::Format::RGBA16_UINT: 78 | return nvrhi::Format::RGBA16_UINT; 79 | case nrd::Format::RGBA16_SINT: 80 | return nvrhi::Format::RGBA16_SINT; 81 | case nrd::Format::RGBA16_SFLOAT: 82 | return nvrhi::Format::RGBA16_FLOAT; 83 | case nrd::Format::R32_UINT: 84 | return nvrhi::Format::R32_UINT; 85 | case nrd::Format::R32_SINT: 86 | return nvrhi::Format::R32_SINT; 87 | case nrd::Format::R32_SFLOAT: 88 | return nvrhi::Format::R32_FLOAT; 89 | case nrd::Format::RG32_UINT: 90 | return nvrhi::Format::RG32_UINT; 91 | case nrd::Format::RG32_SINT: 92 | return nvrhi::Format::RG32_SINT; 93 | case nrd::Format::RG32_SFLOAT: 94 | return nvrhi::Format::RG32_FLOAT; 95 | case nrd::Format::RGB32_UINT: 96 | return nvrhi::Format::RGB32_UINT; 97 | case nrd::Format::RGB32_SINT: 98 | return nvrhi::Format::RGB32_SINT; 99 | case nrd::Format::RGB32_SFLOAT: 100 | return nvrhi::Format::RGB32_FLOAT; 101 | case nrd::Format::RGBA32_UINT: 102 | return nvrhi::Format::RGBA32_UINT; 103 | case nrd::Format::RGBA32_SINT: 104 | return nvrhi::Format::RGBA32_SINT; 105 | case nrd::Format::RGBA32_SFLOAT: 106 | return nvrhi::Format::RGBA32_FLOAT; 107 | case nrd::Format::R10_G10_B10_A2_UNORM: 108 | return nvrhi::Format::R10G10B10A2_UNORM; 109 | case nrd::Format::R10_G10_B10_A2_UINT: 110 | return nvrhi::Format::UNKNOWN; // not representable and not used 111 | case nrd::Format::R11_G11_B10_UFLOAT: 112 | return nvrhi::Format::R11G11B10_FLOAT; 113 | case nrd::Format::R9_G9_B9_E5_UFLOAT: 114 | return nvrhi::Format::UNKNOWN; // not representable and not used 115 | default: 116 | return nvrhi::Format::UNKNOWN; 117 | } 118 | } 119 | 120 | NrdIntegration::NrdIntegration(nvrhi::IDevice* device, nrd::Denoiser denoiser) 121 | : m_device(device), m_initialized(false), m_instance(nullptr), m_denoiser(denoiser), m_bindingCache(device), m_identifier(0) 122 | { 123 | } 124 | 125 | NrdIntegration::~NrdIntegration() 126 | { 127 | if (m_initialized) 128 | nrd::DestroyInstance(*m_instance); 129 | } 130 | 131 | bool NrdIntegration::Initialize(uint32_t width, uint32_t height, donut::engine::ShaderFactory& shaderFactory) 132 | { 133 | const nrd::LibraryDesc& libraryDesc = nrd::GetLibraryDesc(); 134 | 135 | const nrd::DenoiserDesc denoiserDescs[] = { { m_identifier, m_denoiser } }; 136 | 137 | nrd::InstanceCreationDesc instanceCreationDesc = {}; 138 | instanceCreationDesc.denoisers = denoiserDescs; 139 | instanceCreationDesc.denoisersNum = _countof(denoiserDescs); 140 | 141 | nrd::Result result = nrd::CreateInstance(instanceCreationDesc, m_instance); 142 | if (result != nrd::Result::SUCCESS) 143 | return false; 144 | 145 | const nrd::InstanceDesc& instanceDesc = nrd::GetInstanceDesc(*m_instance); 146 | 147 | const nvrhi::BufferDesc constantBufferDesc = 148 | nvrhi::utils::CreateVolatileConstantBufferDesc(instanceDesc.constantBufferMaxDataSize, "NrdConstantBuffer", instanceDesc.descriptorPoolDesc.setsMaxNum * 4); 149 | 150 | m_constantBuffer = m_device->createBuffer(constantBufferDesc); 151 | 152 | for (uint32_t samplerIndex = 0; samplerIndex < instanceDesc.samplersNum; samplerIndex++) 153 | { 154 | const nrd::Sampler& samplerMode = instanceDesc.samplers[samplerIndex]; 155 | 156 | nvrhi::SamplerAddressMode addressMode = nvrhi::SamplerAddressMode::Wrap; 157 | bool filter = false; 158 | 159 | switch (samplerMode) 160 | { 161 | case nrd::Sampler::NEAREST_CLAMP: 162 | addressMode = nvrhi::SamplerAddressMode::Clamp; 163 | filter = false; 164 | break; 165 | case nrd::Sampler::LINEAR_CLAMP: 166 | addressMode = nvrhi::SamplerAddressMode::Clamp; 167 | filter = true; 168 | break; 169 | default: 170 | assert(!"Unknown NRD sampler mode"); 171 | break; 172 | } 173 | 174 | auto samplerDesc = nvrhi::SamplerDesc().setAllAddressModes(addressMode).setAllFilters(filter); 175 | 176 | const nvrhi::SamplerHandle sampler = m_device->createSampler(samplerDesc); 177 | 178 | if (!sampler) 179 | { 180 | assert(!"Cannot create an NRD sampler"); 181 | return false; 182 | } 183 | 184 | m_samplers.push_back(sampler); 185 | } 186 | 187 | for (uint32_t pipelineIndex = 0; pipelineIndex < instanceDesc.pipelinesNum; pipelineIndex++) 188 | { 189 | const nrd::PipelineDesc& nrdPipelineDesc = instanceDesc.pipelines[pipelineIndex]; 190 | 191 | std::string fileName = std::string("nrd/RayTracingDenoiser/Shaders/Source/") + nrdPipelineDesc.shaderFileName; 192 | std::vector macros = { { "NRD_COMPILER_DXC", "1" }, { "NRD_NORMAL_ENCODING", "2" }, { "NRD_ROUGHNESS_ENCODING", "1" } }; 193 | 194 | NrdPipeline pipeline; 195 | pipeline.shader = shaderFactory.CreateShader(fileName.c_str(), "main", ¯os, nvrhi::ShaderType::Compute); 196 | 197 | if (!pipeline.shader) 198 | { 199 | assert(!"Cannot create an NRD shader"); 200 | return false; 201 | } 202 | 203 | nvrhi::BindingLayoutDesc layoutDesc; 204 | layoutDesc.visibility = nvrhi::ShaderType::Compute; 205 | 206 | nvrhi::BindingLayoutItem constantBufferItem = {}; 207 | constantBufferItem.type = nvrhi::ResourceType::VolatileConstantBuffer; 208 | constantBufferItem.slot = instanceDesc.constantBufferRegisterIndex; 209 | layoutDesc.bindings.push_back(constantBufferItem); 210 | 211 | assert(instanceDesc.samplersSpaceIndex == 0); 212 | for (uint32_t samplerIndex = 0; samplerIndex < instanceDesc.samplersNum; samplerIndex++) 213 | { 214 | // const nrd::StaticSamplerDesc& nrdStaticSampler = denoiserDesc.samplers[samplerIndex]; 215 | 216 | nvrhi::BindingLayoutItem samplerItem = {}; 217 | samplerItem.type = nvrhi::ResourceType::Sampler; 218 | samplerItem.slot = instanceDesc.samplersBaseRegisterIndex + samplerIndex; 219 | layoutDesc.bindings.push_back(samplerItem); 220 | } 221 | 222 | for (uint32_t descriptorRangeIndex = 0; descriptorRangeIndex < nrdPipelineDesc.resourceRangesNum; descriptorRangeIndex++) 223 | { 224 | const nrd::ResourceRangeDesc& nrdDescriptorRange = nrdPipelineDesc.resourceRanges[descriptorRangeIndex]; 225 | 226 | nvrhi::BindingLayoutItem resourceItem = {}; 227 | switch (nrdDescriptorRange.descriptorType) 228 | { 229 | case nrd::DescriptorType::TEXTURE: 230 | resourceItem.type = nvrhi::ResourceType::Texture_SRV; 231 | break; 232 | case nrd::DescriptorType::STORAGE_TEXTURE: 233 | resourceItem.type = nvrhi::ResourceType::Texture_UAV; 234 | break; 235 | default: 236 | assert(!"Unknown NRD descriptor type"); 237 | break; 238 | } 239 | 240 | for (uint32_t descriptorOffset = 0; descriptorOffset < nrdDescriptorRange.descriptorsNum; descriptorOffset++) 241 | { 242 | resourceItem.slot = nrdDescriptorRange.baseRegisterIndex + descriptorOffset; 243 | layoutDesc.bindings.push_back(resourceItem); 244 | } 245 | } 246 | 247 | pipeline.bindingLayout = m_device->createBindingLayout(layoutDesc); 248 | 249 | if (!pipeline.bindingLayout) 250 | { 251 | assert(!"Cannot create an NRD binding layout"); 252 | return false; 253 | } 254 | 255 | nvrhi::ComputePipelineDesc pipelineDesc; 256 | pipelineDesc.bindingLayouts = { pipeline.bindingLayout }; 257 | pipelineDesc.CS = pipeline.shader; 258 | pipeline.pipeline = m_device->createComputePipeline(pipelineDesc); 259 | 260 | if (!pipeline.pipeline) 261 | { 262 | assert(!"Cannot create an NRD pipeline"); 263 | return false; 264 | } 265 | 266 | m_pipelines.push_back(pipeline); 267 | } 268 | 269 | const uint32_t poolSize = instanceDesc.permanentPoolSize + instanceDesc.transientPoolSize; 270 | 271 | for (uint32_t i = 0; i < poolSize; i++) 272 | { 273 | const bool isPermanent = (i < instanceDesc.permanentPoolSize); 274 | 275 | const nrd::TextureDesc& nrdTextureDesc = isPermanent ? instanceDesc.permanentPool[i] : instanceDesc.transientPool[i - instanceDesc.permanentPoolSize]; 276 | 277 | const nvrhi::Format format = GetNvrhiFormat(nrdTextureDesc.format); 278 | if (format == nvrhi::Format::UNKNOWN) 279 | { 280 | assert(!"Unknown or unsupported NRD format"); 281 | return false; 282 | } 283 | 284 | std::stringstream ss; 285 | ss << "NRD " << (isPermanent ? "Permanent" : "Transient") << "Texture [" << (isPermanent ? i : i - instanceDesc.permanentPoolSize) << "]"; 286 | 287 | nvrhi::TextureDesc textureDesc; 288 | textureDesc.width = width; 289 | textureDesc.height = height; 290 | textureDesc.format = format; 291 | textureDesc.dimension = nvrhi::TextureDimension::Texture2D; 292 | textureDesc.initialState = nvrhi::ResourceStates::ShaderResource; 293 | textureDesc.keepInitialState = true; 294 | textureDesc.isUAV = true; 295 | textureDesc.debugName = ss.str(); 296 | 297 | const nvrhi::TextureHandle texture = m_device->createTexture(textureDesc); 298 | 299 | if (!texture) 300 | { 301 | assert(!"Cannot create an NRD texture"); 302 | return false; 303 | } 304 | 305 | if (isPermanent) 306 | m_permanentTextures.push_back(texture); 307 | else 308 | m_transientTextures.push_back(texture); 309 | } 310 | 311 | m_initialized = true; 312 | 313 | return true; 314 | } 315 | 316 | bool NrdIntegration::IsAvailable() const 317 | { 318 | return m_initialized; 319 | } 320 | 321 | static inline void MatrixToNrd(float* dest, const dm::float4x4& m) 322 | { 323 | dm::float4x4 tm = dm::transpose(m); 324 | memcpy(dest, &m, sizeof(m)); 325 | } 326 | 327 | void NrdIntegration::RunDenoiserPasses(nvrhi::ICommandList* commandList, 328 | const RenderTargets& renderTargets, 329 | int pass, 330 | const donut::engine::PlanarView& view, 331 | const donut::engine::PlanarView& viewPrev, 332 | uint32_t frameIndex, 333 | float disocclusionThreshold, 334 | float disocclusionThresholdAlternate, 335 | bool useDisocclusionThresholdAlternateMix, 336 | bool enableValidation, 337 | const void* methodSettings, 338 | bool reset) 339 | { 340 | if (methodSettings) 341 | nrd::SetDenoiserSettings(*m_instance, m_identifier, methodSettings); 342 | 343 | nrd::CommonSettings commonSettings; 344 | MatrixToNrd(commonSettings.worldToViewMatrix, dm::affineToHomogeneous(view.GetViewMatrix())); // MatrixToNrd(commonSettings.worldToViewRotationMatrix, 345 | // dm::affineToHomogeneous(view.GetViewMatrix())); 346 | MatrixToNrd(commonSettings.worldToViewMatrixPrev, dm::affineToHomogeneous(viewPrev.GetViewMatrix())); // MatrixToNrd(commonSettings.worldToViewRotationMatrixPrev, 347 | // dm::affineToHomogeneous(viewPrev.GetViewMatrix())); 348 | MatrixToNrd(commonSettings.viewToClipMatrix, view.GetProjectionMatrix(false)); 349 | MatrixToNrd(commonSettings.viewToClipMatrixPrev, viewPrev.GetProjectionMatrix(false)); 350 | 351 | dm::float2 pixelOffset = view.GetPixelOffset(); 352 | dm::float2 prevPixelOffset = viewPrev.GetPixelOffset(); 353 | commonSettings.isMotionVectorInWorldSpace = false; 354 | commonSettings.motionVectorScale[0] = (commonSettings.isMotionVectorInWorldSpace) ? 1.0f : 1.0f / view.GetViewExtent().width(); 355 | commonSettings.motionVectorScale[1] = (commonSettings.isMotionVectorInWorldSpace) ? 1.0f : 1.0f / view.GetViewExtent().height(); 356 | commonSettings.motionVectorScale[2] = 1.0f; 357 | commonSettings.cameraJitter[0] = pixelOffset.x; 358 | commonSettings.cameraJitter[1] = pixelOffset.y; 359 | commonSettings.cameraJitterPrev[0] = prevPixelOffset.x; 360 | commonSettings.cameraJitterPrev[1] = prevPixelOffset.y; 361 | commonSettings.resourceSize[0] = view.GetViewExtent().width(); 362 | commonSettings.resourceSize[1] = view.GetViewExtent().height(); 363 | commonSettings.resourceSizePrev[0] = viewPrev.GetViewExtent().width(); 364 | commonSettings.resourceSizePrev[1] = viewPrev.GetViewExtent().height(); 365 | commonSettings.rectSize[0] = view.GetViewExtent().width(); 366 | commonSettings.rectSize[1] = view.GetViewExtent().height(); 367 | commonSettings.rectSizePrev[0] = viewPrev.GetViewExtent().width(); 368 | commonSettings.rectSizePrev[1] = viewPrev.GetViewExtent().height(); 369 | commonSettings.frameIndex = frameIndex; 370 | commonSettings.enableValidation = enableValidation; 371 | commonSettings.disocclusionThreshold = disocclusionThreshold; 372 | commonSettings.disocclusionThresholdAlternate = disocclusionThresholdAlternate; 373 | commonSettings.isDisocclusionThresholdMixAvailable = useDisocclusionThresholdAlternateMix; 374 | commonSettings.accumulationMode = reset ? nrd::AccumulationMode::RESTART : nrd::AccumulationMode::CONTINUE; 375 | 376 | nrd::SetCommonSettings(*m_instance, commonSettings); 377 | 378 | const nrd::DispatchDesc* dispatchDescs = nullptr; 379 | uint32_t dispatchDescNum = 0; 380 | nrd::GetComputeDispatches(*m_instance, &m_identifier, 1, dispatchDescs, dispatchDescNum); 381 | 382 | const nrd::InstanceDesc& instanceDesc = nrd::GetInstanceDesc(*m_instance); 383 | 384 | for (uint32_t dispatchIndex = 0; dispatchIndex < dispatchDescNum; dispatchIndex++) 385 | { 386 | const nrd::DispatchDesc& dispatchDesc = dispatchDescs[dispatchIndex]; 387 | 388 | if (dispatchDesc.name) 389 | commandList->beginMarker(dispatchDesc.name); 390 | 391 | assert(m_constantBuffer); 392 | commandList->writeBuffer(m_constantBuffer, dispatchDesc.constantBufferData, dispatchDesc.constantBufferDataSize); 393 | 394 | nvrhi::BindingSetDesc setDesc; 395 | setDesc.bindings.push_back(nvrhi::BindingSetItem::ConstantBuffer(instanceDesc.constantBufferRegisterIndex, m_constantBuffer)); 396 | 397 | for (uint32_t samplerIndex = 0; samplerIndex < instanceDesc.samplersNum; samplerIndex++) 398 | { 399 | assert(m_samplers[samplerIndex]); 400 | setDesc.bindings.push_back(nvrhi::BindingSetItem::Sampler(instanceDesc.samplersBaseRegisterIndex + samplerIndex, m_samplers[samplerIndex])); 401 | } 402 | 403 | const nrd::PipelineDesc& nrdPipelineDesc = instanceDesc.pipelines[dispatchDesc.pipelineIndex]; 404 | uint32_t resourceIndex = 0; 405 | 406 | for (uint32_t descriptorRangeIndex = 0; descriptorRangeIndex < nrdPipelineDesc.resourceRangesNum; descriptorRangeIndex++) 407 | { 408 | const nrd::ResourceRangeDesc& nrdDescriptorRange = nrdPipelineDesc.resourceRanges[descriptorRangeIndex]; 409 | 410 | for (uint32_t descriptorOffset = 0; descriptorOffset < nrdDescriptorRange.descriptorsNum; descriptorOffset++) 411 | { 412 | assert(resourceIndex < dispatchDesc.resourcesNum); 413 | const nrd::ResourceDesc& resource = dispatchDesc.resources[resourceIndex]; 414 | 415 | nvrhi::TextureHandle texture; 416 | switch (resource.type) 417 | { 418 | case nrd::ResourceType::IN_MV: 419 | texture = renderTargets.denoiserMotionVectors; 420 | break; 421 | case nrd::ResourceType::IN_NORMAL_ROUGHNESS: 422 | texture = renderTargets.denoiserNormalRoughness; 423 | break; 424 | case nrd::ResourceType::IN_VIEWZ: 425 | texture = renderTargets.denoiserViewSpaceZ; 426 | break; 427 | case nrd::ResourceType::IN_SPEC_RADIANCE_HITDIST: 428 | texture = renderTargets.denoiserInSpecRadianceHitDist; 429 | break; 430 | case nrd::ResourceType::IN_DIFF_RADIANCE_HITDIST: 431 | texture = renderTargets.denoiserInDiffRadianceHitDist; 432 | break; 433 | case nrd::ResourceType::OUT_SPEC_RADIANCE_HITDIST: 434 | texture = renderTargets.denoiserOutSpecRadianceHitDist; 435 | break; 436 | case nrd::ResourceType::OUT_DIFF_RADIANCE_HITDIST: 437 | texture = renderTargets.denoiserOutDiffRadianceHitDist; 438 | break; 439 | case nrd::ResourceType::TRANSIENT_POOL: 440 | texture = m_transientTextures[resource.indexInPool]; 441 | break; 442 | case nrd::ResourceType::PERMANENT_POOL: 443 | texture = m_permanentTextures[resource.indexInPool]; 444 | break; 445 | default: 446 | assert(!"Unavailable resource type"); 447 | break; 448 | } 449 | 450 | assert(texture); 451 | 452 | nvrhi::TextureSubresourceSet subresources = nvrhi::AllSubresources; 453 | nvrhi::BindingSetItem setItem = nvrhi::BindingSetItem::None(); 454 | setItem.resourceHandle = texture; 455 | setItem.slot = nrdDescriptorRange.baseRegisterIndex + descriptorOffset; 456 | setItem.subresources = subresources; 457 | setItem.type = (nrdDescriptorRange.descriptorType == nrd::DescriptorType::TEXTURE) ? nvrhi::ResourceType::Texture_SRV : nvrhi::ResourceType::Texture_UAV; 458 | 459 | setDesc.bindings.push_back(setItem); 460 | 461 | resourceIndex++; 462 | } 463 | } 464 | 465 | assert(resourceIndex == dispatchDesc.resourcesNum); 466 | 467 | const NrdPipeline& pipeline = m_pipelines[dispatchDesc.pipelineIndex]; 468 | 469 | nvrhi::BindingSetHandle bindingSet = m_bindingCache.GetOrCreateBindingSet(setDesc, pipeline.bindingLayout); 470 | 471 | nvrhi::ComputeState state; 472 | state.bindings = { bindingSet }; 473 | state.pipeline = pipeline.pipeline; 474 | commandList->setComputeState(state); 475 | 476 | commandList->dispatch(dispatchDesc.gridWidth, dispatchDesc.gridHeight); 477 | 478 | if (dispatchDesc.name) 479 | commandList->endMarker(); 480 | } 481 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/NrdIntegration.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | 17 | class RenderTargets; 18 | 19 | namespace donut::engine 20 | { 21 | class PlanarView; 22 | class ShaderFactory; 23 | } // namespace donut::engine 24 | 25 | class NrdIntegration 26 | { 27 | public: 28 | NrdIntegration(nvrhi::IDevice* device, nrd::Denoiser method); 29 | ~NrdIntegration(); 30 | 31 | bool Initialize(uint32_t width, uint32_t height, donut::engine::ShaderFactory& shaderFactory); 32 | bool IsAvailable() const; 33 | 34 | void RunDenoiserPasses(nvrhi::ICommandList* commandList, 35 | const RenderTargets& renderTargets, 36 | int pass, 37 | const donut::engine::PlanarView& view, 38 | const donut::engine::PlanarView& viewPrev, 39 | uint32_t frameIndex, 40 | float disocclusionThreshold, 41 | float disocclusionThresholdAlternate, 42 | bool useDisocclusionThresholdAlternateMix, 43 | bool enableValidation, 44 | const void* methodSettings, 45 | bool reset); 46 | 47 | const nrd::Denoiser GetDenoiser() const 48 | { 49 | return m_denoiser; 50 | } 51 | 52 | private: 53 | nvrhi::DeviceHandle m_device; 54 | bool m_initialized; 55 | nrd::Instance* m_instance; 56 | nrd::Denoiser m_denoiser; 57 | nrd::Identifier m_identifier; 58 | 59 | struct NrdPipeline 60 | { 61 | nvrhi::ShaderHandle shader; 62 | nvrhi::BindingLayoutHandle bindingLayout; 63 | nvrhi::ComputePipelineHandle pipeline; 64 | }; 65 | 66 | nvrhi::BufferHandle m_constantBuffer; 67 | std::vector m_pipelines; 68 | std::vector m_samplers; 69 | std::vector m_permanentTextures; 70 | std::vector m_transientTextures; 71 | donut::engine::BindingCache m_bindingCache; 72 | }; -------------------------------------------------------------------------------- /Samples/Pathtracer/Pathtracer.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "PathtracerUi.h" 22 | 23 | // Unified Binding 24 | struct DescriptorSetIDs 25 | { 26 | enum 27 | { 28 | Globals, 29 | Denoiser, 30 | Nrc, 31 | Sharc, 32 | Bindless, 33 | COUNT 34 | }; 35 | }; 36 | 37 | #if ENABLE_NRD 38 | #include "RenderTargets.h" 39 | #include "NrdIntegration.h" 40 | #endif // ENABLE_NRD 41 | 42 | class ScopedMarker 43 | { 44 | public: 45 | ScopedMarker(nvrhi::ICommandList* commandList, const char* name) : m_commandList(commandList) 46 | { 47 | m_commandList->beginMarker(name); 48 | } 49 | ~ScopedMarker() 50 | { 51 | m_commandList->endMarker(); 52 | } 53 | 54 | private: 55 | nvrhi::ICommandList* m_commandList; 56 | }; 57 | 58 | class Pathtracer : public donut::app::ApplicationBase 59 | { 60 | public: 61 | using ApplicationBase::ApplicationBase; 62 | 63 | struct PipelinePermutation 64 | { 65 | nvrhi::ShaderLibraryHandle shaderLibrary; 66 | nvrhi::rt::PipelineHandle pipeline; 67 | nvrhi::rt::ShaderTableHandle shaderTable; 68 | }; 69 | 70 | Pathtracer(donut::app::DeviceManager* deviceManager, UIData& ui, nvrhi::GraphicsAPI api); 71 | virtual ~Pathtracer(); 72 | 73 | bool Init(int argc, const char* const* argv); 74 | 75 | virtual bool LoadScene(std::shared_ptr fs, const std::filesystem::path& sceneFileName) override; 76 | virtual void SceneUnloading() override; 77 | virtual void SceneLoaded() override; 78 | std::vector const& GetAvailableScenes() const; 79 | std::shared_ptr GetScene() const; 80 | 81 | std::string GetCurrentSceneName() const; 82 | void SetPreferredSceneName(const std::string& sceneName); 83 | void SetCurrentSceneName(const std::string& sceneName); 84 | 85 | void CopyActiveCameraToFirstPerson(); 86 | 87 | void EnableAnimations(); 88 | void DisableAnimations(); 89 | void Animate(float fElapsedTimeSeconds) override; 90 | 91 | bool KeyboardUpdate(int key, int scancode, int action, int mods) override; 92 | 93 | bool MousePosUpdate(double xpos, double ypos) override; 94 | bool MouseButtonUpdate(int button, int action, int mods) override; 95 | bool MouseScrollUpdate(double xoffset, double yoffset) override; 96 | 97 | bool CreateRayTracingPipeline(donut::engine::ShaderFactory& shaderFactory, PipelinePermutation& pipelinePermutation, std::vector& pipelineMacros); 98 | bool CreateRayTracingPipelines(); 99 | 100 | #if ENABLE_NRC 101 | NrcIntegration* GetNrcInstance() const; 102 | #endif 103 | 104 | void GetMeshBlasDesc(donut::engine::MeshInfo& mesh, nvrhi::rt::AccelStructDesc& blasDesc, bool skipTransmissiveMaterials) const; 105 | void CreateAccelStructs(nvrhi::ICommandList* commandList); 106 | void BuildTLAS(nvrhi::ICommandList* commandList, uint32_t frameIndex) const; 107 | 108 | void BackBufferResizing() override; 109 | 110 | void Render(nvrhi::IFramebuffer* framebuffer) override; 111 | 112 | std::shared_ptr GetShaderFactory(); 113 | std::shared_ptr GetRootFS() const; 114 | 115 | std::shared_ptr GetTextureCache(); 116 | 117 | void RebuildAccelerationStructure(); 118 | void ResetAccumulation(); 119 | 120 | donut::app::FirstPersonCamera* GetCamera(); 121 | 122 | std::string GetResolutionInfo(); 123 | 124 | private: 125 | std::shared_ptr m_rootFileSystem; 126 | std::shared_ptr m_nativeFileSystem; 127 | 128 | enum PipelineType 129 | { 130 | DefaultPathTracing, 131 | #if ENABLE_NRC 132 | NRC_Update, 133 | NRC_Query, 134 | #endif // ENABLE_NRC 135 | #if ENABLE_SHARC 136 | Sharc_Update, 137 | Sharc_Query, 138 | #endif // ENABLE_SHARC 139 | Count 140 | }; 141 | 142 | std::vector m_pipelineMacros[PipelineType::Count]; 143 | PipelinePermutation m_pipelinePermutations[PipelineType::Count]; 144 | 145 | nvrhi::CommandListHandle m_commandList; 146 | nvrhi::BindingLayoutHandle m_globalBindingLayout; 147 | nvrhi::BindingSetHandle m_globalBindingSet; 148 | nvrhi::BindingLayoutHandle m_bindlessLayout; 149 | 150 | nvrhi::GraphicsPipelineHandle m_tonemappingPSO; 151 | nvrhi::BindingLayoutHandle m_tonemappingBindingLayout; 152 | nvrhi::BindingSetHandle m_tonemappingBindingSet; 153 | nvrhi::ShaderHandle m_tonemappingPS; 154 | 155 | nvrhi::rt::AccelStructHandle m_topLevelAS; 156 | bool m_rebuildAS = true; 157 | int m_cameraIndex = -1; 158 | 159 | nvrhi::BufferHandle m_constantBuffer; 160 | nvrhi::BufferHandle m_debugBuffer; 161 | 162 | std::shared_ptr m_shaderFactory; 163 | std::shared_ptr m_descriptorTable; 164 | 165 | std::vector m_sceneFilesAvailable; 166 | std::string m_currentSceneName; 167 | std::shared_ptr m_scene; 168 | 169 | nvrhi::TextureHandle m_accumulationBuffer; 170 | nvrhi::TextureHandle m_pathTracerOutputBuffer; 171 | 172 | donut::app::FirstPersonCamera m_camera; 173 | donut::engine::PlanarView m_view; 174 | donut::engine::PlanarView m_viewPrevious; 175 | 176 | std::shared_ptr m_sunLight; 177 | std::shared_ptr m_headLight; 178 | 179 | std::unique_ptr m_bindingCache; 180 | 181 | bool m_enableAnimations = false; 182 | float m_wallclockTime = 0.0f; 183 | int m_frameIndex = 0; 184 | 185 | UIData& m_ui; 186 | 187 | dm::affine3 m_prevViewMatrix; 188 | bool m_resetAccumulation; 189 | bool m_sceneReloaded; 190 | uint32_t m_accumulatedFrameCount; 191 | 192 | nvrhi::GraphicsAPI m_api; 193 | 194 | #if ENABLE_NRC 195 | std::unique_ptr m_nrc; 196 | nrc::ContextSettings m_nrcContextSettings; 197 | int m_nrcUsedTrainingWidth = 0; 198 | int m_nrcUsedTrainingHeight = 0; 199 | nrc::BuffersAllocationInfo m_nrcBuffersAllocation; 200 | nvrhi::BindingLayoutHandle m_nrcBindingLayout; 201 | nvrhi::BindingSetHandle m_nrcBindingSet; 202 | #endif // ENABLE_NRC 203 | 204 | #if ENABLE_SHARC 205 | static const uint32_t m_sharcInvalidEntry = 0; 206 | uint32_t m_sharcEntriesNum = 0; 207 | nvrhi::BufferHandle m_sharcHashEntriesBuffer; 208 | nvrhi::BufferHandle m_sharcCopyOffsetBuffer; 209 | nvrhi::BufferHandle m_sharcVoxelDataBuffer; 210 | nvrhi::BufferHandle m_sharcVoxelDataBufferPrev; 211 | 212 | nvrhi::BindingLayoutHandle m_sharcBindingLayout; 213 | nvrhi::BindingSetHandle m_sharcBindingSet; 214 | nvrhi::BindingSetHandle m_sharcBindingSetSwapped; 215 | nvrhi::ShaderHandle m_sharcResolveCS; 216 | nvrhi::ComputePipelineHandle m_sharcResolvePSO; 217 | nvrhi::ShaderHandle m_sharcHashCopyCS; 218 | nvrhi::ComputePipelineHandle m_sharcHashCopyPSO; 219 | #endif // ENABLE_SHARC 220 | 221 | #if ENABLE_NRD 222 | nvrhi::BindingLayoutHandle m_denoiserBindingLayout; 223 | nvrhi::BindingSetHandle m_denoiserBindingSet; 224 | nvrhi::BindingSetHandle m_denoiserOutBindingSet; 225 | nvrhi::ShaderHandle m_denoiserReblurPackCS; 226 | nvrhi::ComputePipelineHandle m_denoiserReblurPackPSO; 227 | nvrhi::ShaderHandle m_denoiserReblurPack_NRC_CS; 228 | nvrhi::ComputePipelineHandle m_denoiserReblurPack_NRC_PSO; 229 | nvrhi::ShaderHandle m_denoiserResolveCS; 230 | nvrhi::ComputePipelineHandle m_denoiserResolvePSO; 231 | std::unique_ptr m_renderTargets; 232 | std::unique_ptr m_nrd; 233 | #endif // ENABLE_NRD 234 | 235 | // Unified Binding 236 | nvrhi::BindingLayoutHandle m_dummyLayouts[DescriptorSetIDs::COUNT]; 237 | nvrhi::BindingSetHandle m_dummyBindingSets[DescriptorSetIDs::COUNT]; 238 | }; -------------------------------------------------------------------------------- /Samples/Pathtracer/PathtracerUi.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "Pathtracer.h" 18 | 19 | #if ENABLE_NRC 20 | #include "NrcUtils.h" 21 | #include "NrcStructures.h" 22 | #endif 23 | 24 | using namespace donut::app; 25 | using namespace donut::engine; 26 | using namespace donut::math; 27 | 28 | // Conversion between sRGB to linear color space 29 | // Required here because of a known bug with ImGui and sRGB framebuffer 30 | float srgb_to_linear(float value) 31 | { 32 | if (value <= 0.04045f) 33 | return value / 12.92f; 34 | else 35 | return powf((value + 0.055f) / 1.055f, 2.4f); 36 | } 37 | 38 | void srgb_to_linear(ImVec4& color) 39 | { 40 | color.x = srgb_to_linear(color.x); 41 | color.y = srgb_to_linear(color.y); 42 | color.z = srgb_to_linear(color.z); 43 | } 44 | 45 | PathtracerUI::PathtracerUI(DeviceManager* deviceManager, Pathtracer& app, UIData& ui) : ImGui_Renderer(deviceManager), m_app(app), m_ui(ui) 46 | { 47 | m_commandList = GetDevice()->createCommandList(); 48 | 49 | std::shared_ptr nativeFS = std::make_shared(); 50 | m_fontDroidMono = this->LoadFont(*nativeFS, GetDirectoryWithExecutable().parent_path() / "Assets" / "Media/Fonts/DroidSans/DroidSans-Mono.ttf", 16.0f); 51 | 52 | ImGui_Console::Options options; 53 | options.font = m_fontDroidMono; 54 | 55 | ImGui::GetIO().IniFilename = nullptr; 56 | } 57 | 58 | PathtracerUI::~PathtracerUI() 59 | { 60 | } 61 | 62 | void PathtracerUI::buildUI(void) 63 | { 64 | if (!m_ui.showUI) 65 | return; 66 | 67 | const auto& io = ImGui::GetIO(); 68 | 69 | int width, height; 70 | GetDeviceManager()->GetWindowDimensions(width, height); 71 | 72 | if (m_app.IsSceneLoading()) 73 | { 74 | BeginFullScreenWindow(); 75 | 76 | char messageBuffer[256]; 77 | const auto& stats = Scene::GetLoadingStats(); 78 | snprintf(messageBuffer, std::size(messageBuffer), "Loading scene %s, please wait...\nObjects: %d/%d, Textures: %d/%d", m_app.GetCurrentSceneName().c_str(), 79 | stats.ObjectsLoaded.load(), stats.ObjectsTotal.load(), m_app.GetTextureCache()->GetNumberOfLoadedTextures(), 80 | m_app.GetTextureCache()->GetNumberOfRequestedTextures()); 81 | 82 | DrawScreenCenteredText(messageBuffer); 83 | 84 | EndFullScreenWindow(); 85 | 86 | return; 87 | } 88 | 89 | bool updateAccum = false; 90 | bool updateAccelerationStructure = false; 91 | 92 | ImGui::Begin("Settings", 0, ImGuiWindowFlags_AlwaysAutoResize); 93 | ImGui::SetWindowPos(ImVec2(1.0f, 1.0f)); 94 | ImGui::StyleColorsDark(); 95 | ImGuiStyle* style = &ImGui::GetStyle(); 96 | ImVec4* colors = style->Colors; 97 | for (int i = 0; i < (int)ImGuiCol_COUNT; ++i) 98 | srgb_to_linear(colors[i]); 99 | 100 | ImGui::Text("%s, %s", GetDeviceManager()->GetRendererString(), m_app.GetResolutionInfo().c_str()); 101 | double frameTime = GetDeviceManager()->GetAverageFrameTimeSeconds(); 102 | if (frameTime > 0.0) 103 | ImGui::Text("%.3f ms/frame (%.1f FPS)", frameTime * 1e3, 1.0 / frameTime); 104 | 105 | ImGui::Separator(); 106 | if (ImGui::CollapsingHeader("Generic:", ImGuiTreeNodeFlags_DefaultOpen)) 107 | { 108 | ImGui::Indent(12.0f); 109 | { 110 | #ifdef _DEBUG 111 | float3 cameraPosition = m_app.GetCamera()->GetPosition(); 112 | ImGui::Text("Camera (%0.2f, %0.2f, %0.2f)", cameraPosition.x, cameraPosition.y, cameraPosition.z); 113 | #endif 114 | 115 | const std::string currentScene = m_app.GetCurrentSceneName(); 116 | if (ImGui::BeginCombo("Scene", currentScene.c_str())) 117 | { 118 | const std::vector& scenes = m_app.GetAvailableScenes(); 119 | for (const std::string& scene : scenes) 120 | { 121 | bool is_selected = scene == currentScene; 122 | if (ImGui::Selectable(scene.c_str(), is_selected)) 123 | m_app.SetCurrentSceneName(scene); 124 | 125 | if (is_selected) 126 | ImGui::SetItemDefaultFocus(); 127 | } 128 | ImGui::EndCombo(); 129 | } 130 | 131 | updateAccum |= ImGui::Checkbox("Jitter", &m_ui.enableJitter); 132 | ImGui::SameLine(); 133 | updateAccum |= ImGui::Checkbox("Russian Roulette", &m_ui.enableRussianRoulette); 134 | ImGui::SameLine(); 135 | updateAccum |= ImGui::Checkbox("Transmission", &m_ui.enableTransmission); 136 | ImGui::SameLine(); 137 | 138 | if (ImGui::Checkbox("Animations", &m_ui.enableAnimations)) 139 | { 140 | if (m_ui.enableAnimations) 141 | m_app.EnableAnimations(); 142 | else 143 | m_app.DisableAnimations(); 144 | } 145 | } 146 | ImGui::Indent(-12.0f); 147 | } 148 | 149 | ImGui::Separator(); 150 | if (ImGui::CollapsingHeader("Path Tracing:", ImGuiTreeNodeFlags_DefaultOpen)) 151 | { 152 | ImGui::Indent(12.0f); 153 | { 154 | ImGui::Text("Mode:"); 155 | ImGui::SameLine(); 156 | if (ImGui::RadioButton("Reference", (m_ui.techSelection == TechSelection::None))) 157 | { 158 | m_ui.techSelection = TechSelection::None; 159 | updateAccum = true; 160 | } 161 | 162 | #if ENABLE_NRC 163 | ImGui::SameLine(); 164 | ImGui::BeginDisabled(!m_app.GetNrcInstance()->IsInitialized()); 165 | if (ImGui::RadioButton("NRC", m_ui.techSelection == TechSelection::Nrc)) 166 | { 167 | m_ui.techSelection = TechSelection::Nrc; 168 | updateAccum = true; 169 | } 170 | ImGui::EndDisabled(); 171 | #endif 172 | 173 | #if ENABLE_SHARC 174 | ImGui::SameLine(); 175 | if (ImGui::RadioButton("SHaRC", m_ui.techSelection == TechSelection::Sharc)) 176 | { 177 | m_ui.techSelection = TechSelection::Sharc; 178 | updateAccum = true; 179 | } 180 | #endif 181 | 182 | ImGui::Text("Denoiser:"); 183 | ImGui::SameLine(); 184 | if (ImGui::RadioButton("None", m_ui.denoiserSelection == DenoiserSelection::None)) 185 | { 186 | m_ui.denoiserSelection = DenoiserSelection::None; 187 | updateAccum = true; 188 | } 189 | ImGui::SameLine(); 190 | if (ImGui::RadioButton("Accumulation", m_ui.denoiserSelection == DenoiserSelection::Accumulation)) 191 | { 192 | m_ui.denoiserSelection = DenoiserSelection::Accumulation; 193 | // m_ui.enableAccumulation = true; 194 | updateAccum = true; 195 | } 196 | #if ENABLE_NRD 197 | ImGui::SameLine(); 198 | if (ImGui::RadioButton("NRD", m_ui.denoiserSelection == DenoiserSelection::Nrd)) 199 | { 200 | m_ui.denoiserSelection = DenoiserSelection::Nrd; 201 | updateAccum = true; 202 | } 203 | #endif 204 | 205 | updateAccum |= ImGui::SliderInt("Bounces", &m_ui.bouncesMax, 1, 24); 206 | updateAccum |= ImGui::SliderInt("Samples Per Pixel", &m_ui.samplesPerPixel, 1, 16); 207 | updateAccum |= ImGui::SliderFloat("Exposure Adjustment", &m_ui.exposureAdjustment, -8.f, 8.0f); 208 | updateAccum |= ImGui::SliderFloat("Roughness Min", &m_ui.roughnessMin, 0.0f, 1.0f); 209 | updateAccum |= ImGui::SliderFloat("Roughness Max", &m_ui.roughnessMax, 0.0f, 1.0f); 210 | updateAccum |= ImGui::SliderFloat("Metalness Min", &m_ui.metalnessMin, 0.0f, 1.0f); 211 | updateAccum |= ImGui::SliderFloat("Metalness Max", &m_ui.metalnessMax, 0.0f, 1.0f); 212 | 213 | // Debug views 214 | updateAccum |= ImGui::Combo("Debug Output", (int*)&m_ui.ptDebugOutput, m_ui.ptDebugOutputTypeStrings); 215 | 216 | updateAccum |= updateAccelerationStructure; 217 | } 218 | ImGui::Indent(-12.0f); 219 | } 220 | 221 | #if ENABLE_NRC 222 | ImGui::Separator(); 223 | if (ImGui::CollapsingHeader((m_ui.techSelection == TechSelection::Nrc) ? "NRC:" : "Select NRC PT Mode to see NRC options", ImGuiTreeNodeFlags_DefaultOpen)) 224 | { 225 | ImGui::Indent(12.0f); 226 | if (m_ui.techSelection == TechSelection::Nrc) 227 | { 228 | updateAccum |= ImGui::Checkbox("Train The Cache", &m_ui.nrcTrainCache); 229 | updateAccum |= ImGui::SliderInt("Training Bounces", &m_ui.nrcMaxTrainingBounces, 1, 8); 230 | updateAccum |= ImGui::Checkbox("Learn Irradiance", &m_ui.nrcLearnIrradiance); 231 | updateAccum |= ImGui::Checkbox("Include Direct Illumination", &m_ui.nrcIncludeDirectIllumination); 232 | updateAccum |= ImGui::Checkbox("Skip delta vertices", &m_ui.nrcSkipDeltaVertices); 233 | updateAccum |= ImGui::SliderFloat("Self-Training Attenuation", &m_ui.nrcSelfTrainingAttenuation, 0.0f, 1.0f, "%.3f"); 234 | updateAccum |= ImGui::SliderFloat("Heuristic Threshold", &m_ui.nrcTerminationHeuristicThreshold, 0.0f, 0.25f, "%.3f"); 235 | updateAccum |= ImGui::SliderInt("Num Training Iterations", &m_ui.nrcNumTrainingIterations, 1, 4); 236 | updateAccum |= ImGui::SliderFloat("Primary segments to train on", &m_ui.nrcProportionPrimarySegmentsToTrainOn, 0.0f, 1.0f, "%.2f"); 237 | updateAccum |= ImGui::SliderFloat("Tertiary+ segments to train on", &m_ui.nrcProportionTertiaryPlusSegmentsToTrainOn, 0.0f, 1.0f, "%.2f"); 238 | updateAccum |= ImGui::SliderFloat("Proportion unbiased", &m_ui.nrcProportionUnbiased, 0.0f, 1.0f, "%.2f"); 239 | updateAccum |= ImGui::SliderFloat("Unbiased self-training", &m_ui.nrcProportionUnbiasedToSelfTrain, 0.0f, 1.0f, "%.2f"); 240 | updateAccum |= ImGui::SliderFloat("Max Average Radiance Value", &m_ui.nrcMaxAverageRadiance, 0.001f, 1000.0f); 241 | updateAccum |= ImGui::Combo("Resolve Mode", (int*)&m_ui.nrcResolveMode, nrc::GetImGuiResolveModeComboString()); 242 | } 243 | ImGui::Indent(-12.0f); 244 | } 245 | #endif // ENABLE_NRC 246 | 247 | #if ENABLE_SHARC 248 | ImGui::Separator(); 249 | if (ImGui::CollapsingHeader((m_ui.techSelection == TechSelection::Sharc) ? "SHaRC:" : "Select SHaRC PT Mode to see SHaRC options", ImGuiTreeNodeFlags_DefaultOpen)) 250 | { 251 | ImGui::Indent(12.0f); 252 | if (m_ui.techSelection == TechSelection::Sharc) 253 | { 254 | updateAccum |= ImGui::Checkbox("Enable Clear", &m_ui.sharcEnableClear); 255 | updateAccum |= ImGui::Checkbox("Enable Update", &m_ui.sharcEnableUpdate); 256 | updateAccum |= ImGui::Checkbox("Enable Resolve", &m_ui.sharcEnableResolve); 257 | updateAccum |= ImGui::Checkbox("Enable Anti Firefly", &m_ui.sharcEnableAntiFireflyFilter); 258 | updateAccum |= ImGui::Checkbox("Update View Camera", &m_ui.sharcUpdateViewCamera); 259 | updateAccum |= ImGui::Checkbox("Enable Debug", &m_ui.sharcEnableDebug); 260 | updateAccum |= ImGui::SliderInt("Accumulation Frame Number", &m_ui.sharcAccumulationFrameNum, 1, 30); 261 | updateAccum |= ImGui::SliderInt("Stale Frame Number", &m_ui.sharcStaleFrameFrameNum, 1, 128); 262 | updateAccum |= ImGui::SliderInt("Downscale Factor", &m_ui.sharcDownscaleFactor, 1, 10); 263 | updateAccum |= ImGui::SliderFloat("Scene Scale", &m_ui.sharcSceneScale, 5.0f, 100.0f); 264 | updateAccum |= ImGui::SliderFloat("Rougness Threshold", &m_ui.sharcRoughnessThreshold, 0.0f, 1.0f); 265 | } 266 | ImGui::Indent(-12.0f); 267 | } 268 | #endif // ENABLE_SHARC 269 | 270 | ImGui::Separator(); 271 | if (ImGui::CollapsingHeader("Lighting:", ImGuiTreeNodeFlags_DefaultOpen)) 272 | { 273 | ImGui::Indent(12.0f); 274 | updateAccum |= ImGui::Checkbox("Enable Sky", &m_ui.enableSky); 275 | updateAccum |= ImGui::ColorEdit4("Sky Color", m_ui.skyColor, ImGuiColorEditFlags_NoAlpha | ImGuiColorEditFlags_Float); 276 | updateAccum |= ImGui::SliderFloat("Sky Intensity", &m_ui.skyIntensity, 0.f, 10.f); 277 | updateAccum |= ImGui::Checkbox("Enable Emissives", &m_ui.enableEmissives); 278 | updateAccum |= ImGui::Checkbox("Enable Direct Lighting", &m_ui.enableLighting); 279 | 280 | const auto& lights = m_app.GetScene()->GetSceneGraph()->GetLights(); 281 | 282 | if (!lights.empty() && ImGui::CollapsingHeader("Lights")) 283 | { 284 | if (ImGui::BeginCombo("Select Light", m_selectedLight ? m_selectedLight->GetName().c_str() : "(None)")) 285 | { 286 | int lightIndex = 0; 287 | for (const auto& light : lights) 288 | { 289 | bool selected = m_selectedLight == light; 290 | ImGui::Selectable(light->GetName().c_str(), &selected); 291 | if (selected) 292 | { 293 | m_selectedLight = light; 294 | m_selectedLightIndex = lightIndex; 295 | ImGui::SetItemDefaultFocus(); 296 | } 297 | lightIndex++; 298 | } 299 | ImGui::EndCombo(); 300 | } 301 | 302 | if (m_selectedLight) 303 | { 304 | bool target = (m_ui.targetLight == m_selectedLightIndex); 305 | updateAccum |= ImGui::Checkbox("Target this light?", &target); 306 | if (target) 307 | m_ui.targetLight = m_selectedLightIndex; 308 | else 309 | m_ui.targetLight = -1; 310 | 311 | updateAccum |= donut::app::LightEditor(*m_selectedLight); 312 | } 313 | } 314 | ImGui::Indent(-12.0f); 315 | } 316 | 317 | // Post-process 318 | { 319 | ImGui::Separator(); 320 | if (ImGui::CollapsingHeader("Tone mapping:", ImGuiTreeNodeFlags_DefaultOpen)) 321 | { 322 | ImGui::Indent(12.0f); 323 | 324 | updateAccum |= ImGui::Combo("Operator", (int*)&m_ui.toneMappingOperator, m_ui.toneMappingOperatorStrings); 325 | ImGui::Checkbox("Clamp", &m_ui.toneMappingClamp); 326 | 327 | ImGui::Indent(-12.0f); 328 | } 329 | } 330 | ImGui::End(); 331 | 332 | if (updateAccum) 333 | m_app.ResetAccumulation(); 334 | 335 | if (updateAccelerationStructure) 336 | m_app.RebuildAccelerationStructure(); 337 | }; -------------------------------------------------------------------------------- /Samples/Pathtracer/PathtracerUi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #define ENABLE_NRC 1 20 | #define ENABLE_SHARC 1 21 | #define ENABLE_NRD 1 22 | 23 | #if ENABLE_NRC 24 | #include "NrcIntegration.h" 25 | #endif // ENABLE_NRC 26 | 27 | enum class PTDebugOutputType : uint32_t 28 | { 29 | None = 0, 30 | DiffuseReflectance = 1, 31 | WorldSpaceNormals = 2, 32 | WorldSpacePosition = 3, 33 | Barycentrics = 4, 34 | HitT = 5, 35 | InstanceID = 6, 36 | Emissives = 7, 37 | BounceHeatmap = 8, 38 | }; 39 | 40 | enum class TechSelection : uint32_t 41 | { 42 | None = 0, 43 | #if ENABLE_NRC 44 | Nrc = 1, 45 | #endif 46 | #if ENABLE_SHARC 47 | Sharc = 2, 48 | #endif 49 | }; 50 | 51 | 52 | enum class DenoiserSelection : uint32_t 53 | { 54 | None = 0, 55 | Accumulation = 1, 56 | #if ENABLE_NRD 57 | Nrd = 2, 58 | #endif 59 | }; 60 | 61 | enum class ToneMappingOperator : uint32_t 62 | { 63 | Linear = 0, 64 | Reinhard = 1, 65 | }; 66 | 67 | struct UIData 68 | { 69 | bool showUI = true; 70 | bool enableAnimations = false; 71 | bool enableJitter = true; 72 | bool enableTransmission = false; 73 | bool enableBackFaceCull = true; 74 | int bouncesMax = 8; 75 | int accumulatedFrames = 1; 76 | int accumulatedFramesMax = 128; 77 | float exposureAdjustment = 0.0f; 78 | float roughnessMin = 0.0f; 79 | float roughnessMax = 1.0f; 80 | float metalnessMin = 0.0f; 81 | float metalnessMax = 1.0f; 82 | bool enableSky = true; 83 | bool enableEmissives = true; 84 | bool enableLighting = true; 85 | bool enableAbsorbtion = true; 86 | bool enableTransparentShadows = true; 87 | bool enableSoftShadows = true; 88 | float throughputThreshold = 0.01f; 89 | bool enableRussianRoulette = true; 90 | dm::float3 skyColor = dm::float3(0.5f, 0.75f, 1.0f); 91 | float skyIntensity = 8.0f; 92 | int samplesPerPixel = 1; 93 | int targetLight = 0; 94 | bool enableTonemapping = true; 95 | 96 | TechSelection techSelection = TechSelection::None; 97 | DenoiserSelection denoiserSelection = DenoiserSelection::Accumulation; 98 | bool enableDenoiser = false; 99 | 100 | ToneMappingOperator toneMappingOperator = ToneMappingOperator::Reinhard; 101 | bool toneMappingClamp = true; 102 | const char* toneMappingOperatorStrings = "Linear\0Reinhard\0"; 103 | 104 | #if ENABLE_NRC 105 | bool nrcLearnIrradiance = true; 106 | bool nrcIncludeDirectIllumination = true; 107 | bool nrcTrainCache = true; 108 | int nrcMaxTrainingBounces = 8; 109 | bool nrcCalculateTrainingLoss = false; 110 | float nrcMaxAverageRadiance = 1.0f; 111 | NrcResolveMode nrcResolveMode = NrcResolveMode::AddQueryResultToOutput; 112 | // TODO: Following settings will not be exposed 113 | float nrcProportionPrimarySegmentsToTrainOn = 0.02f; 114 | float nrcProportionTertiaryPlusSegmentsToTrainOn = 1.0f; 115 | float nrcProportionUnbiasedToSelfTrain = 1.0f; 116 | float nrcProportionUnbiased = 1.0f / 16.0f; 117 | float nrcSelfTrainingAttenuation = 1.0f; 118 | 119 | uint32_t nrcTrainingWidth = 0; 120 | uint32_t nrcTrainingHeight = 0; 121 | 122 | // Settings used by the path tracer 123 | bool nrcSkipDeltaVertices = false; 124 | float nrcTerminationHeuristicThreshold = 0.01f; 125 | int nrcNumTrainingIterations = 1; 126 | #endif // ENABLE_NRC 127 | 128 | #if ENABLE_SHARC 129 | bool sharcEnableClear = false; 130 | bool sharcEnableUpdate = true; 131 | bool sharcEnableResolve = true; 132 | bool sharcEnableAntiFireflyFilter = true; 133 | bool sharcUpdateViewCamera = true; 134 | bool sharcEnableDebug = false; 135 | int sharcDownscaleFactor = 5; 136 | float sharcSceneScale = 50.0f; 137 | int sharcAccumulationFrameNum = 10; 138 | int sharcStaleFrameFrameNum = 64; 139 | float sharcRoughnessThreshold = 0.4f; 140 | #endif // ENABLE_SHARC 141 | 142 | std::shared_ptr selectedMaterial = nullptr; 143 | std::shared_ptr activeSceneCamera = nullptr; 144 | 145 | PTDebugOutputType ptDebugOutput = PTDebugOutputType::None; 146 | const char* ptDebugOutputTypeStrings = "None\0Diffuse Reflectance\0Worldspace Normals\0Worldspace Position\0Barycentrics\0HitT\0InstanceID\0Emissives\0Bounce Heatmap\0"; 147 | }; 148 | 149 | class PathtracerUI : public donut::app::ImGui_Renderer 150 | { 151 | public: 152 | PathtracerUI(donut::app::DeviceManager* deviceManager, class Pathtracer& app, UIData& ui); 153 | virtual ~PathtracerUI(); 154 | 155 | protected: 156 | virtual void buildUI(void) override; 157 | 158 | private: 159 | class Pathtracer& m_app; 160 | UIData& m_ui; 161 | 162 | ImFont* m_fontOpenSans = nullptr; 163 | ImFont* m_fontDroidMono = nullptr; 164 | 165 | std::shared_ptr m_selectedLight; 166 | int m_selectedLightIndex = 0; 167 | 168 | nvrhi::CommandListHandle m_commandList; 169 | }; -------------------------------------------------------------------------------- /Samples/Pathtracer/PathtracerUtils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #define M_PI 3.141592653589f 12 | 13 | #define FLT_MAX 3.402823466e+38f 14 | 15 | // Jenkins's "one at a time" hash function 16 | uint JenkinsHash(uint x) 17 | { 18 | x += x << 10; 19 | x ^= x >> 6; 20 | x += x << 3; 21 | x ^= x >> 11; 22 | x += x << 15; 23 | return x; 24 | } 25 | 26 | // Maps integers to colors using the hash function (generates pseudo-random colors) 27 | float3 HashAndColor(int i) 28 | { 29 | uint hash = JenkinsHash(i); 30 | float r = ((hash >> 0) & 0xFF) / 255.0f; 31 | float g = ((hash >> 8) & 0xFF) / 255.0f; 32 | float b = ((hash >> 16) & 0xFF) / 255.0f; 33 | return float3(r, g, b); 34 | } 35 | 36 | uint InitRNG(uint2 pixel, uint2 resolution, uint frame) 37 | { 38 | uint rngState = dot(pixel, uint2(1, resolution.x)) ^ JenkinsHash(frame); 39 | return JenkinsHash(rngState); 40 | } 41 | 42 | float UintToFloat(uint x) 43 | { 44 | return asfloat(0x3f800000 | (x >> 9)) - 1.f; 45 | } 46 | 47 | uint XorShift(inout uint rngState) 48 | { 49 | rngState ^= rngState << 13; 50 | rngState ^= rngState >> 17; 51 | rngState ^= rngState << 5; 52 | return rngState; 53 | } 54 | 55 | float Rand(inout uint rngState) 56 | { 57 | return UintToFloat(XorShift(rngState)); 58 | } 59 | 60 | float3 GetPerpendicularVector(float3 u) 61 | { 62 | float3 a = abs(u); 63 | uint xm = ((a.x - a.y) < 0 && (a.x - a.z) < 0) ? 1 : 0; 64 | uint ym = (a.y - a.z) < 0 ? (1 ^ xm) : 0; 65 | uint zm = 1 ^ (xm | ym); 66 | return cross(u, float3(xm, ym, zm)); 67 | } 68 | 69 | // Clever offset_ray function from Ray Tracing Gems chapter 6 70 | // Offsets the ray origin from current position p, along normal n (which must be geometric normal) 71 | // so that no self-intersection can occur. 72 | float3 OffsetRay(const float3 p, const float3 n) 73 | { 74 | static const float origin = 1.0f / 32.0f; 75 | static const float float_scale = 1.0f / 65536.0f; 76 | static const float int_scale = 256.0f; 77 | 78 | int3 of_i = int3(int_scale * n.x, int_scale * n.y, int_scale * n.z); 79 | 80 | float3 p_i = 81 | float3(asfloat(asint(p.x) + ((p.x < 0) ? -of_i.x : of_i.x)), asfloat(asint(p.y) + ((p.y < 0) ? -of_i.y : of_i.y)), asfloat(asint(p.z) + ((p.z < 0) ? -of_i.z : of_i.z))); 82 | 83 | return float3(abs(p.x) < origin ? p.x + float_scale * n.x : p_i.x, abs(p.y) < origin ? p.y + float_scale * n.y : p_i.y, abs(p.z) < origin ? p.z + float_scale * n.z : p_i.z); 84 | } 85 | 86 | // Bounce heatmap visualization: https://developer.nvidia.com/blog/profiling-dxr-shaders-with-timer-instrumentation/ 87 | inline float3 Temperature(float t) 88 | { 89 | const float3 c[10] = { float3(0.0f / 255.0f, 2.0f / 255.0f, 91.0f / 255.0f), float3(0.0f / 255.0f, 108.0f / 255.0f, 251.0f / 255.0f), 90 | float3(0.0f / 255.0f, 221.0f / 255.0f, 221.0f / 255.0f), float3(51.0f / 255.0f, 221.0f / 255.0f, 0.0f / 255.0f), 91 | float3(255.0f / 255.0f, 252.0f / 255.0f, 0.0f / 255.0f), float3(255.0f / 255.0f, 180.0f / 255.0f, 0.0f / 255.0f), 92 | float3(255.0f / 255.0f, 104.0f / 255.0f, 0.0f / 255.0f), float3(226.0f / 255.0f, 22.0f / 255.0f, 0.0f / 255.0f), 93 | float3(191.0f / 255.0f, 0.0f / 255.0f, 83.0f / 255.0f), float3(145.0f / 255.0f, 0.0f / 255.0f, 65.0f / 255.0f) }; 94 | 95 | const float s = t * 10.0f; 96 | 97 | const int cur = int(s) <= 9 ? int(s) : 9; 98 | const int prv = cur >= 1 ? cur - 1 : 0; 99 | const int nxt = cur < 9 ? cur + 1 : 9; 100 | 101 | const float blur = 0.8f; 102 | 103 | const float wc = smoothstep(float(cur) - blur, float(cur) + blur, s) * (1.0f - smoothstep(float(cur + 1) - blur, float(cur + 1) + blur, s)); 104 | const float wp = 1.0f - smoothstep(float(cur) - blur, float(cur) + blur, s); 105 | const float wn = smoothstep(float(cur + 1) - blur, float(cur + 1) + blur, s); 106 | 107 | const float3 r = wc * c[cur] + wp * c[prv] + wn * c[nxt]; 108 | return float3(clamp(r.x, 0.0f, 1.0f), clamp(r.y, 0.0f, 1.0f), clamp(r.z, 0.0f, 1.0f)); 109 | } 110 | 111 | inline float3 BounceHeatmap(uint bounce) 112 | { 113 | switch (bounce) 114 | { 115 | case 0: 116 | return float3(0.0f, 0.0f, 1.0f); 117 | case 1: 118 | return float3(0.0f, 1.0f, 0.0f); 119 | default: 120 | return float3(1.0f, 0.0f, 0.0f); 121 | } 122 | } 123 | 124 | enum GeometryAttributes 125 | { 126 | GeomAttr_Position = 0x01, 127 | GeomAttr_TexCoord = 0x02, 128 | GeomAttr_Normal = 0x04, 129 | GeomAttr_Tangents = 0x08, 130 | GeomAttr_All = 0x0F 131 | }; 132 | 133 | struct GeometrySample 134 | { 135 | InstanceData instance; 136 | GeometryData geometry; 137 | MaterialConstants material; 138 | 139 | float3 vertexPositions[3]; 140 | float2 vertexTexcoords[3]; 141 | 142 | float3 objectSpacePosition; 143 | float2 texcoord; 144 | float3 flatNormal; 145 | float3 geometryNormal; 146 | float4 tangent; 147 | }; 148 | 149 | GeometrySample GetGeometryFromHit(uint instanceIndex, 150 | uint triangleIndex, 151 | uint geometryIndex, 152 | bool isFrontFacing, 153 | float2 rayBarycentrics, 154 | GeometryAttributes attributes, 155 | StructuredBuffer instanceBuffer, 156 | StructuredBuffer geometryBuffer, 157 | StructuredBuffer materialBuffer, 158 | ByteAddressBuffer bindlessBuffers[]) 159 | { 160 | GeometrySample gs = (GeometrySample)0; 161 | 162 | gs.instance = instanceBuffer[instanceIndex]; 163 | gs.geometry = geometryBuffer[gs.instance.firstGeometryIndex + geometryIndex]; 164 | gs.material = materialBuffer[gs.geometry.materialIndex]; 165 | 166 | ByteAddressBuffer indexBuffer = bindlessBuffers[NonUniformResourceIndex(gs.geometry.indexBufferIndex)]; 167 | ByteAddressBuffer vertexBuffer = bindlessBuffers[NonUniformResourceIndex(gs.geometry.vertexBufferIndex)]; 168 | 169 | float3 barycentrics; 170 | barycentrics.yz = rayBarycentrics; 171 | barycentrics.x = 1.0f - (barycentrics.y + barycentrics.z); 172 | 173 | uint3 indices = indexBuffer.Load3(gs.geometry.indexOffset + triangleIndex * c_SizeOfTriangleIndices); 174 | 175 | if (attributes & GeomAttr_Position) 176 | { 177 | gs.vertexPositions[0] = asfloat(vertexBuffer.Load3(gs.geometry.positionOffset + indices[0] * c_SizeOfPosition)); 178 | gs.vertexPositions[1] = asfloat(vertexBuffer.Load3(gs.geometry.positionOffset + indices[1] * c_SizeOfPosition)); 179 | gs.vertexPositions[2] = asfloat(vertexBuffer.Load3(gs.geometry.positionOffset + indices[2] * c_SizeOfPosition)); 180 | gs.objectSpacePosition = interpolate(gs.vertexPositions, barycentrics); 181 | } 182 | 183 | if ((attributes & GeomAttr_TexCoord) && gs.geometry.texCoord1Offset != ~0u) 184 | { 185 | gs.vertexTexcoords[0] = asfloat(vertexBuffer.Load2(gs.geometry.texCoord1Offset + indices[0] * c_SizeOfTexcoord)); 186 | gs.vertexTexcoords[1] = asfloat(vertexBuffer.Load2(gs.geometry.texCoord1Offset + indices[1] * c_SizeOfTexcoord)); 187 | gs.vertexTexcoords[2] = asfloat(vertexBuffer.Load2(gs.geometry.texCoord1Offset + indices[2] * c_SizeOfTexcoord)); 188 | gs.texcoord = interpolate(gs.vertexTexcoords, barycentrics); 189 | } 190 | 191 | if ((attributes & GeomAttr_Normal) && gs.geometry.normalOffset != ~0u) 192 | { 193 | float3 normals[3]; 194 | normals[0] = Unpack_RGB8_SNORM(vertexBuffer.Load(gs.geometry.normalOffset + indices[0] * c_SizeOfNormal)); 195 | normals[1] = Unpack_RGB8_SNORM(vertexBuffer.Load(gs.geometry.normalOffset + indices[1] * c_SizeOfNormal)); 196 | normals[2] = Unpack_RGB8_SNORM(vertexBuffer.Load(gs.geometry.normalOffset + indices[2] * c_SizeOfNormal)); 197 | gs.geometryNormal = interpolate(normals, barycentrics); 198 | gs.geometryNormal = mul(gs.instance.transform, float4(gs.geometryNormal, 0.0f)).xyz; 199 | gs.geometryNormal = normalize(gs.geometryNormal); 200 | 201 | gs.geometryNormal *= isFrontFacing ? 1.0f : -1.0f; 202 | } 203 | 204 | if ((attributes & GeomAttr_Tangents) && gs.geometry.tangentOffset != ~0u) 205 | { 206 | float4 tangents[3]; 207 | tangents[0] = Unpack_RGBA8_SNORM(vertexBuffer.Load(gs.geometry.tangentOffset + indices[0] * c_SizeOfNormal)); 208 | tangents[1] = Unpack_RGBA8_SNORM(vertexBuffer.Load(gs.geometry.tangentOffset + indices[1] * c_SizeOfNormal)); 209 | tangents[2] = Unpack_RGBA8_SNORM(vertexBuffer.Load(gs.geometry.tangentOffset + indices[2] * c_SizeOfNormal)); 210 | gs.tangent.xyz = interpolate(tangents, barycentrics).xyz; 211 | gs.tangent.xyz = mul(gs.instance.transform, float4(gs.tangent.xyz, 0.0f)).xyz; 212 | gs.tangent.xyz = normalize(gs.tangent.xyz); 213 | gs.tangent.w = tangents[0].w; 214 | } 215 | 216 | float3 objectSpaceFlatNormal = normalize(cross(gs.vertexPositions[1] - gs.vertexPositions[0], gs.vertexPositions[2] - gs.vertexPositions[0])); 217 | 218 | gs.flatNormal = normalize(mul(gs.instance.transform, float4(objectSpaceFlatNormal, 0.0f)).xyz); 219 | gs.flatNormal *= isFrontFacing ? 1.0f : -1.0f; 220 | 221 | return gs; 222 | } 223 | 224 | enum MaterialAttributes 225 | { 226 | MatAttr_BaseColor = 0x01, 227 | MatAttr_Emissive = 0x02, 228 | MatAttr_Normal = 0x04, 229 | MatAttr_MetalRough = 0x08, 230 | MatAttr_Transmission = 0x10, 231 | 232 | MatAttr_All = 0x1F 233 | }; 234 | 235 | MaterialSample SampleGeometryMaterial(GeometrySample gs, 236 | float2 texGrad_x, 237 | float2 texGrad_y, 238 | float mipLevel, // <-- Use a compile time constant for mipLevel, < 0 for aniso filtering 239 | MaterialAttributes attributes, 240 | SamplerState materialSampler, 241 | Texture2D bindlessTextures[]) 242 | { 243 | MaterialTextureSample textures = DefaultMaterialTextures(); 244 | 245 | if ((attributes & MatAttr_BaseColor) && (gs.material.baseOrDiffuseTextureIndex >= 0) && (gs.material.flags & MaterialFlags_UseBaseOrDiffuseTexture) != 0) 246 | { 247 | Texture2D diffuseTexture = bindlessTextures[NonUniformResourceIndex(gs.material.baseOrDiffuseTextureIndex)]; 248 | 249 | if (mipLevel >= 0) 250 | textures.baseOrDiffuse = diffuseTexture.SampleLevel(materialSampler, gs.texcoord, mipLevel); 251 | else 252 | textures.baseOrDiffuse = diffuseTexture.SampleGrad(materialSampler, gs.texcoord, texGrad_x, texGrad_y); 253 | } 254 | 255 | if ((attributes & MatAttr_Emissive) && (gs.material.emissiveTextureIndex >= 0) && (gs.material.flags & MaterialFlags_UseEmissiveTexture) != 0) 256 | { 257 | Texture2D emissiveTexture = bindlessTextures[NonUniformResourceIndex(gs.material.emissiveTextureIndex)]; 258 | 259 | if (mipLevel >= 0) 260 | textures.emissive = emissiveTexture.SampleLevel(materialSampler, gs.texcoord, mipLevel); 261 | else 262 | textures.emissive = emissiveTexture.SampleGrad(materialSampler, gs.texcoord, texGrad_x, texGrad_y); 263 | } 264 | 265 | if ((attributes & MatAttr_Normal) && (gs.material.normalTextureIndex >= 0) && (gs.material.flags & MaterialFlags_UseNormalTexture) != 0) 266 | { 267 | Texture2D normalsTexture = bindlessTextures[NonUniformResourceIndex(gs.material.normalTextureIndex)]; 268 | 269 | if (mipLevel >= 0) 270 | textures.normal = normalsTexture.SampleLevel(materialSampler, gs.texcoord, mipLevel); 271 | else 272 | textures.normal = normalsTexture.SampleGrad(materialSampler, gs.texcoord, texGrad_x, texGrad_y); 273 | } 274 | 275 | if ((attributes & MatAttr_MetalRough) && (gs.material.metalRoughOrSpecularTextureIndex >= 0) && (gs.material.flags & MaterialFlags_UseMetalRoughOrSpecularTexture) != 0) 276 | { 277 | Texture2D specularTexture = bindlessTextures[NonUniformResourceIndex(gs.material.metalRoughOrSpecularTextureIndex)]; 278 | 279 | if (mipLevel >= 0) 280 | textures.metalRoughOrSpecular = specularTexture.SampleLevel(materialSampler, gs.texcoord, mipLevel); 281 | else 282 | textures.metalRoughOrSpecular = specularTexture.SampleGrad(materialSampler, gs.texcoord, texGrad_x, texGrad_y); 283 | } 284 | 285 | if ((attributes & MatAttr_Transmission) && (gs.material.transmissionTextureIndex >= 0) && (gs.material.flags & MaterialFlags_UseTransmissionTexture) != 0) 286 | { 287 | Texture2D transmissionTexture = bindlessTextures[NonUniformResourceIndex(gs.material.transmissionTextureIndex)]; 288 | 289 | if (mipLevel >= 0) 290 | textures.transmission = transmissionTexture.SampleLevel(materialSampler, gs.texcoord, mipLevel); 291 | else 292 | textures.transmission = transmissionTexture.SampleGrad(materialSampler, gs.texcoord, texGrad_x, texGrad_y); 293 | } 294 | 295 | return EvaluateSceneMaterial(gs.geometryNormal, gs.tangent, gs.material, textures); 296 | } 297 | 298 | // Decodes light vector and distance from Light structure based on the light type 299 | void GetLightData(LightConstants light, float3 surfacePos, float2 rand2, bool enableSoftShadows, out float3 incidentVector, out float lightDistance, out float irradiance) 300 | { 301 | incidentVector = 0; 302 | float halfAngularSize = 0; 303 | irradiance = 0; 304 | lightDistance = 0; 305 | 306 | if (light.lightType == LightType_Directional) 307 | { 308 | if (enableSoftShadows) 309 | { 310 | float3 bitangent = normalize(GetPerpendicularVector(light.direction)); 311 | float3 tangent = cross(bitangent, light.direction); 312 | 313 | float angle = rand2.x * 2.0f * M_PI; 314 | float distance = sqrt(rand2.y); 315 | 316 | incidentVector = light.direction + (bitangent * sin(angle) + tangent * cos(angle)) * tan(light.angularSizeOrInvRange * 0.5f) * distance; 317 | incidentVector = normalize(incidentVector); 318 | } 319 | else 320 | incidentVector = light.direction; 321 | 322 | lightDistance = FLT_MAX; 323 | halfAngularSize = light.angularSizeOrInvRange * 0.5f; 324 | irradiance = light.intensity; 325 | } 326 | else if (light.lightType == LightType_Spot || light.lightType == LightType_Point) 327 | { 328 | float3 lightToSurface = surfacePos - light.position; 329 | float distance = sqrt(dot(lightToSurface, lightToSurface)); 330 | float rDistance = 1.0f / distance; 331 | incidentVector = lightToSurface * rDistance; 332 | lightDistance = length(lightToSurface); 333 | 334 | float attenuation = 1.0f; 335 | if (light.angularSizeOrInvRange > 0) 336 | { 337 | attenuation = square(saturate(1.0f - square(square(distance * light.angularSizeOrInvRange)))); 338 | 339 | if (attenuation == 0) 340 | return; 341 | } 342 | 343 | float spotlight = 1.0f; 344 | if (light.lightType == LightType_Spot) 345 | { 346 | float LdotD = dot(incidentVector, light.direction); 347 | float directionAngle = acos(LdotD); 348 | spotlight = 1.0f - smoothstep(light.innerAngle, light.outerAngle, directionAngle); 349 | if (spotlight == 0) 350 | return; 351 | } 352 | 353 | if (light.radius > 0) 354 | { 355 | halfAngularSize = atan(min(light.radius * rDistance, 1.0f)); 356 | 357 | // A good enough approximation for 2 * (1 - cos(halfAngularSize)), numerically more accurate for small angular sizes 358 | float solidAngleOverPi = square(halfAngularSize); 359 | float radianceTimesPi = light.intensity / square(light.radius); 360 | 361 | irradiance = radianceTimesPi * solidAngleOverPi; 362 | } 363 | else 364 | irradiance = light.intensity * square(rDistance); 365 | 366 | irradiance *= spotlight * attenuation; 367 | } 368 | else 369 | return; 370 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/RenderTargets.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "RenderTargets.h" 12 | 13 | RenderTargets::RenderTargets(nvrhi::IDevice* device, uint32_t width, uint32_t height) 14 | { 15 | auto CreateCommonTexture = [device, width, height](nvrhi::Format format, const char* debugName, nvrhi::TextureHandle& texture) { 16 | nvrhi::TextureDesc desc; 17 | desc.width = width; 18 | desc.height = height; 19 | desc.format = format; 20 | desc.debugName = debugName; 21 | desc.isVirtual = false; 22 | desc.initialState = nvrhi::ResourceStates::UnorderedAccess; 23 | desc.isRenderTarget = false; 24 | desc.isUAV = true; 25 | desc.dimension = nvrhi::TextureDimension::Texture2D; 26 | desc.keepInitialState = true; 27 | desc.isTypeless = false; 28 | 29 | texture = device->createTexture(desc); 30 | }; 31 | 32 | CreateCommonTexture(nvrhi::Format::R32_FLOAT, "denoiserViewspaceZ", denoiserViewSpaceZ); 33 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserMotionVectors", denoiserMotionVectors); 34 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserNormalRoughness", denoiserNormalRoughness); 35 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserEmissive", denoiserEmissive); 36 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserDiffuseAbedo", denoiserDiffuseAlbedo); 37 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserSpecularAbedo", denoiserSpecularAlbedo); 38 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserInDiffRadianceHitDist", denoiserInDiffRadianceHitDist); 39 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserInSpecRadianceHitDist", denoiserInSpecRadianceHitDist); 40 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserOutDiffRadianceHitDist", denoiserOutDiffRadianceHitDist); 41 | CreateCommonTexture(nvrhi::Format::RGBA16_FLOAT, "denoiserOutSpecRadianceHitDist", denoiserOutSpecRadianceHitDist); 42 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/RenderTargets.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma once 12 | 13 | #include 14 | 15 | class RenderTargets 16 | { 17 | public: 18 | RenderTargets(nvrhi::IDevice* device, uint32_t width, uint32_t height); 19 | 20 | nvrhi::TextureHandle denoiserViewSpaceZ; 21 | nvrhi::TextureHandle denoiserNormalRoughness; 22 | nvrhi::TextureHandle denoiserMotionVectors; 23 | nvrhi::TextureHandle denoiserEmissive; 24 | nvrhi::TextureHandle denoiserDiffuseAlbedo; 25 | nvrhi::TextureHandle denoiserSpecularAlbedo; 26 | 27 | nvrhi::TextureHandle denoiserInDiffRadianceHitDist; 28 | nvrhi::TextureHandle denoiserInSpecRadianceHitDist; 29 | 30 | nvrhi::TextureHandle denoiserOutDiffRadianceHitDist; 31 | nvrhi::TextureHandle denoiserOutSpecRadianceHitDist; 32 | }; -------------------------------------------------------------------------------- /Samples/Pathtracer/SharcResolve.hlsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #include "GlobalCb.h" 12 | #include "LightingCb.h" 13 | 14 | #include "SharcCommon.h" 15 | 16 | #define LINEAR_BLOCK_SIZE 256 17 | 18 | ConstantBuffer g_Lighting : register(b0, space0); 19 | ConstantBuffer g_Global : register(b1, space0); 20 | 21 | RWStructuredBuffer u_SharcHashEntriesBuffer : register(u0, space3); 22 | RWStructuredBuffer u_HashCopyOffsetBuffer : register(u1, space3); 23 | RWStructuredBuffer u_SharcVoxelDataBuffer : register(u2, space3); 24 | RWStructuredBuffer u_SharcVoxelDataBufferPrev : register(u3, space3); 25 | 26 | [numthreads(LINEAR_BLOCK_SIZE, 1, 1)] 27 | void sharcCompaction(in uint2 did : SV_DispatchThreadID) 28 | { 29 | HashMapData hashMapData; 30 | hashMapData.capacity = g_Lighting.sharcEntriesNum; 31 | hashMapData.hashEntriesBuffer = u_SharcHashEntriesBuffer; 32 | 33 | SharcCopyHashEntry(did.x, hashMapData, u_HashCopyOffsetBuffer); 34 | } 35 | 36 | [numthreads(LINEAR_BLOCK_SIZE, 1, 1)] 37 | void sharcResolve(in uint2 did : SV_DispatchThreadID) 38 | { 39 | SharcParameters sharcParameters; 40 | 41 | sharcParameters.gridParameters.cameraPosition = g_Lighting.sharcCameraPosition.xyz; 42 | sharcParameters.gridParameters.sceneScale = g_Lighting.sharcSceneScale; 43 | sharcParameters.gridParameters.logarithmBase = SHARC_GRID_LOGARITHM_BASE; 44 | sharcParameters.gridParameters.levelBias = SHARC_GRID_LEVEL_BIAS; 45 | 46 | sharcParameters.hashMapData.capacity = g_Lighting.sharcEntriesNum; 47 | sharcParameters.hashMapData.hashEntriesBuffer = u_SharcHashEntriesBuffer; 48 | 49 | sharcParameters.voxelDataBuffer = u_SharcVoxelDataBuffer; 50 | sharcParameters.voxelDataBufferPrev = u_SharcVoxelDataBufferPrev; 51 | 52 | SharcResolveParameters resolveParameters; 53 | resolveParameters.accumulationFrameNum = g_Lighting.sharcAccumulationFrameNum; 54 | resolveParameters.staleFrameNumMax = g_Lighting.sharcStaleFrameNum; 55 | resolveParameters.cameraPositionPrev = g_Lighting.sharcCameraPositionPrev.xyz; 56 | resolveParameters.enableAntiFireflyFilter = g_Lighting.sharcEnableAntifirefly; 57 | 58 | SharcResolveEntry(did.x, sharcParameters, resolveParameters 59 | #if SHARC_DEFERRED_HASH_COMPACTION 60 | , u_HashCopyOffsetBuffer 61 | #endif // SHARC_DEFERRED_HASH_COMPACTION 62 | ); 63 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/Tonemapping.hlsl: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved. 3 | * 4 | * NVIDIA CORPORATION and its licensors retain all intellectual property 5 | * and proprietary rights in and to this software, related documentation 6 | * and any modifications thereto. Any use, reproduction, disclosure or 7 | * distribution of this software and related documentation without an express 8 | * license agreement from NVIDIA CORPORATION is strictly prohibited. 9 | */ 10 | 11 | #pragma pack_matrix(row_major) 12 | 13 | #include "GlobalCb.h" 14 | 15 | ConstantBuffer g_Global : register(b0); 16 | RWTexture2D pathTracingOutputBuffer : register(u0); 17 | RWTexture2D accumulationBuffer : register(u1); 18 | 19 | float CalculateLuminance(float3 color) 20 | { 21 | return dot(color, float3(0.299f, 0.587f, 0.114f)); 22 | } 23 | 24 | float3 ToneMapLinear(float3 color) 25 | { 26 | return color; 27 | } 28 | 29 | float3 ToneMapReinhard(float3 color) 30 | { 31 | float luminance = CalculateLuminance(color); 32 | float reinhard = luminance / (luminance + 1.0f); 33 | 34 | return color * (reinhard / luminance); 35 | } 36 | 37 | float3 ToneMap(float3 color) 38 | { 39 | float3 outColor = color * g_Global.exposureScale; 40 | 41 | if (g_Global.toneMappingOperator == 0 /* Linear */) 42 | outColor = ToneMapLinear(outColor); 43 | else if (g_Global.toneMappingOperator == 1 /* Reinhard */) 44 | outColor = ToneMapReinhard(outColor); 45 | 46 | return outColor; 47 | } 48 | 49 | void main_ps(in float4 pos: SV_Position, in float2 uv: UV, out float4 output: SV_Target) 50 | { 51 | float4 color = pathTracingOutputBuffer[pos.xy]; 52 | if (g_Global.enableAccumulation) 53 | { 54 | if (g_Global.recipAccumulatedFrames < 1.0f) 55 | { 56 | float4 accumulatedColor = accumulationBuffer[pos.xy]; 57 | color = lerp(accumulatedColor, color, g_Global.recipAccumulatedFrames); 58 | } 59 | accumulationBuffer[pos.xy] = color; 60 | } 61 | 62 | color.xyz = ToneMap(color.xyz); 63 | 64 | if (g_Global.clamp) 65 | color = saturate(color); 66 | 67 | output = float4(color); 68 | } -------------------------------------------------------------------------------- /Samples/Pathtracer/shaders.cfg: -------------------------------------------------------------------------------- 1 | Pathtracer.hlsl -T lib -D REFERENCE=1 -D ENABLE_NRD={0,1} 2 | Pathtracer.hlsl -T lib -D NRC_UPDATE=1 3 | Pathtracer.hlsl -T lib -D NRC_QUERY=1 -D ENABLE_NRD={0,1} 4 | Pathtracer.hlsl -T lib -D SHARC_UPDATE=1 5 | Pathtracer.hlsl -T lib -D SHARC_QUERY=1 -D ENABLE_NRD={0,1} 6 | SharcResolve.hlsl -T cs -E sharcResolve 7 | SharcResolve.hlsl -T cs -E sharcCompaction 8 | Tonemapping.hlsl -T ps -E main_ps 9 | Denoiser.hlsl -T cs -E reblurPackData -D NRD_NORMAL_ENCODING=2 -D NRD_ROUGHNESS_ENCODING=1 10 | Denoiser.hlsl -T cs -E reblurPackData -D NRD_NORMAL_ENCODING=2 -D NRD_ROUGHNESS_ENCODING=1 -D ENABLE_NRC=1 11 | Denoiser.hlsl -T cs -E resolve -D NRD_NORMAL_ENCODING=2 -D NRD_ROUGHNESS_ENCODING=1 --------------------------------------------------------------------------------