forked from NVIDIA-RTX/RTXDI
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
85d9a2a
commit 85c7845
Showing
54 changed files
with
3,199 additions
and
549 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
# ReSTIR GI | ||
|
||
## What is ReSTIR GI | ||
|
||
ReSTIR GI is a spatio-temporal resampling algorithm that is applied to samples of surfaces that produce indirect lighting. For comparison, regular ReSTIR "DI" operates on samples of lights that affect primary surfaces; ReSTIR GI converts every surface found by tracing BRDF-sampled rays from the primary surface into a light sample. | ||
|
||
 | ||
|
||
## Integration model | ||
|
||
Our implementation of ReSTIR GI follows the same integration model as ReSTIR "DI", with the resampling math encapsulated in a number of high-level functions that call various functions in the bridge for application interoperability. The application is expected to call these high-level functions from its own shaders. | ||
|
||
The functions and structures necessary for ReSTIR GI implementation are defined in two header files: [`GIResamplingFunctions.hlsli`](../rtxdi-sdk/include/RTXDI/GIResamplingFunctions.hlsli) and [`GIReservoir.hlsli`](../rtxdi-sdk/include/RTXDI/GIReservoir.hlsli). See the [API reference](ShaderAPI-RestirGI.md) for more information on the ReSTIR GI functions and structures. | ||
|
||
The application needs to implement the various bridge functions necessary for ReSTIR GI in order to compile the shaders. ReSTIR GI and DI share the same bridge interface, and the sets of functions necessary for them intersect. The following shared bridge structures and functions are needed for ReSTIR GI and must be implemented: | ||
|
||
RAB_EmptySurface() | ||
RAB_GetGBufferSurface(...) | ||
RAB_GetNextRandom(...) | ||
RAB_GetSurfaceLinearDepth(...) | ||
RAB_GetSurfaceNormal(...) | ||
RAB_GetSurfaceWorldPos(...) | ||
RAB_IsSurfaceValid(...) | ||
RAB_RandomSamplerState { ... } | ||
RAB_Surface { ... } | ||
|
||
The following bridge functions are ReSTIR GI specific: | ||
|
||
RAB_GetGISampleTargetPdfForSurface(...) | ||
RAB_ValidateGISampleWithJacobian(...) | ||
RAB_GetConservativeVisibility(surface, samplePosition) | ||
RAB_GetTemporalConservativeVisibility(currentSurface, previousSurface, samplePosition) | ||
|
||
ReSTIR GI only needs two GPU resources to operate: | ||
|
||
- *Reservoir buffer*: a structured buffer with `RTXDI_PackedGIReservoir` structures. The number of these structures can be calculated using the `rtxdi::Context::GetReservoirBufferElementCount()` function, multiplied by the number of frame-sized buffers that you need to implement the desired pipeline (typically 1 or 2). | ||
|
||
- *Neighbor offset buffer*: a buffer with offsets used for the spatial and spatio-temporal resampling passes. The same buffer is used for ReSTIR DI, and it can be filled using the `rtxdi::Context::FillNeighborOffsetBuffer(...)` function at creation time. | ||
|
||
## Implementation basics | ||
|
||
Before an implementation of ReSTIR GI in a renderer can be started, a regular forward path tracer needs to exist. It can be limited in some ways like path length or types of BRDF lobes or effects supported, but it needs to produce three key parameters that are used as ReSTIR GI input: | ||
|
||
- Position and orientation of the secondary surface | ||
- Solid angle PDF of the ray that was used to find the secondary surface | ||
- Reflected and/or emitted radiance of the secondary surface or a path starting from it | ||
|
||
Note that the radiance of secondary surfaces is non-directional, which means that ReSTIR GI converts all secondary surfaces into Lambertian reflectors or emitters. It is possible to extend the implementation with some directionality information, such as spherical harmonics or even storing some information about the secondary BRDF and the light(s) used to shade it, but that would make the implementation much more expensive, while the difference will be limited to effects like caustics, and normally ReSTIR GI isn't powerful enough to resolve caustics. For the latter reason, the BRDF of secondary surfaces should be limited to avoid producing sharp caustics, such as by clamping the roughness to higher values. | ||
|
||
Once the secondary surfaces or paths have been traced and shaded, the application should create a ReSTIR GI reservoir from it using the `RTXDI_MakeGIReservoir` function. That reservoir can be stored in a reservoir buffer for further resampling, or passed directly into a temporal or a spatio-temporal resampling function. See [ShadeSecondarySurfaces.hlsl](../shaders/LightingPasses/ShadeSecondarySurfaces.hlsl) for an example. | ||
|
||
GI reservoirs created after path tracing can be immediately used for shading, without any resampling. This is a useful mode to verify that the math is correct, that no throughput or visibility term is left behind when ReSTIR GI is inserted into the pipeline. In the basic version (no MIS), the application should take the final GI reservoir for the current pixel and shade the primary surface using that reservoir as a regular emissive surface sample, multiplying the results with `weightSum` from the reservoir. The shading result replaces the original radiance produced by the path tracer. See [GIFinalShading.hlsl](../shaders/LightingPasses/GIFinalShading.hlsl) for an example. | ||
|
||
LightVector = normalize(reservoir.position - primarySurface.position) | ||
|
||
PrimaryReflectedIndirectRadiance = primarySurface.BRDF(LightVector, ViewVector) * reservoir.radiance * reservoir.weightSum | ||
|
||
Once the GI reservoir creation and shading are working correctly, resampling passes can be inserted between these two stages. It can be a combination of temporal and spatial passes, or a fused spatio-temporal pass. In the former case, the [temporal pass](../shaders/LightingPasses/GITemporalResampling.hlsl) can be performed in the same shader that does path tracing and/or initial shading, and the [spatial pass](../shaders/LightingPasses/GISpatialResampling.hlsl) can be performed in the same shader that does final shading - or they can be separate shaders, whichever works faster. The sample application uses the separate approach. In the case of fused [spatio-temporal resampling](../shaders/LightingPasses/GIFusedResampling.hlsl), all passes can be done in the same shader, which is probably the easiest way to integrate ReSTIR GI into a path tracer, but it's likely not great for performance. | ||
|
||
In the final shading pass, multiple importance sampling (MIS) can be applied to recover some image quality lost on shiny surfaces. ReSTIR doesn't work well on specular surfaces with low roughness: the results may look like a more stable, but sparser noise pattern than the original signal, and that's bad for denoising. It's better to combine the ReSTIR GI output with its input in a MIS fashion and use the input signal on surfaces with low roughness. An example of such MIS scheme can be found in the sample application, in the [GIFinalShading.hlsl](../shaders/LightingPasses/GIFinalShading.hlsl) file - see the `GetMISWeight` function and its uses. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,220 @@ | ||
# RTXDI Shader API for ReSTIR GI | ||
|
||
This document lists the macros, structures and functions that are specific to the ReSTIR GI subsystem of RTXDI. | ||
|
||
## User-Defined Macros | ||
|
||
### `RTXDI_WITH_RESTIR_GI` | ||
|
||
When defined, includes the [ReSTIR GI](RestirGI.md) implementation, with all of its functions and structures - both on the shader side and the CPU side. | ||
|
||
### `RTXDI_ENABLE_STORE_RESERVOIR` | ||
|
||
Define this macro to `0` in order to remove the `RTXDI_GI_StoreReservoir` function. This is useful in shaders that have read-only access to the light reservoir buffer, e.g. for debugging purposes. | ||
|
||
### `RTXDI_GI_ALLOWED_BIAS_CORRECTION` | ||
|
||
Define this macro to one of the `RTXDI_BIAS_CORRECTION_...` constants to limit the most complex bias correction algorithm that is included in the shaders, to reduce code bloat. | ||
|
||
### `RTXDI_GI_RESERVOIR_BUFFER` | ||
|
||
Define this macro to a resource name for the ReSTIR GI reservoir buffer, which should have HLSL type `RWStructuredBuffer<RTXDI_PackedGIReservoir>`. | ||
|
||
### `RTXDI_NEIGHBOR_OFFSETS_BUFFER` | ||
|
||
Define this macro to a resource name for the neighbor offset buffer, which should have HLSL type `Buffer<float2>`. | ||
|
||
|
||
## Structures | ||
|
||
### `RTXDI_ResamplingRuntimeParameters` | ||
|
||
See [Shader API](ShaderAPI.md) | ||
|
||
### `RTXDI_PackedGIReservoir` | ||
|
||
A compact representation of a single GI reservoir that should be stored in a structured buffer. | ||
|
||
### `RTXDI_GIReservoir` | ||
|
||
This structure represents a single surface reservoir that stores the surface position, orientation, radiance, and statistical parameters. It can be serialized into `RTXDI_PackedGIReservoir` for storage using the `RTXDI_PackGIReservoir` function, and deserialized from that representation using the `RTXDI_UnpackGIReservoir` function. | ||
|
||
|
||
## Reservoir Functions | ||
|
||
### `RTXDI_EmptyGIReservoir` | ||
|
||
RTXDI_GIReservoir RTXDI_EmptyGIReservoir() | ||
|
||
Returns an empty reservoir object. | ||
|
||
### `RTXDI_IsValidReservoir` | ||
|
||
bool RTXDI_IsValidGIReservoir(const RTXDI_GIReservoir reservoir) | ||
|
||
Returns `true` if the provided reservoir contains a valid GI sample. | ||
|
||
### `RTXDI_LoadGIReservoir` | ||
|
||
RTXDI_GIReservoir RTXDI_LoadGIReservoir( | ||
RTXDI_ResamplingRuntimeParameters params, | ||
uint2 reservoirPosition, | ||
uint reservoirArrayIndex) | ||
|
||
RTXDI_GIReservoir RTXDI_LoadGIReservoir( | ||
RTXDI_ResamplingRuntimeParameters params, | ||
uint2 reservoirPosition, | ||
uint reservoirArrayIndex, | ||
out uint miscFlags) | ||
|
||
Loads and unpacks a GI reservoir from the provided reservoir storage buffer. The buffer normally contains multiple 2D arrays of reservoirs, corresponding to screen pixels, so the function takes the reservoir position and array index and translates those to the buffer index. The optional `miscFlags` output parameter returns a custom 16-bit field that applications can use for their needs. | ||
|
||
### `RTXDI_StoreGIReservoir` | ||
|
||
void RTXDI_StoreGIReservoir( | ||
const RTXDI_GIReservoir reservoir, | ||
RTXDI_ResamplingRuntimeParameters params, | ||
uint2 reservoirPosition, | ||
uint reservoirArrayIndex) | ||
|
||
void RTXDI_StoreGIReservoir( | ||
const RTXDI_GIReservoir reservoir, | ||
const uint miscFlags, | ||
RTXDI_ResamplingRuntimeParameters params, | ||
uint2 reservoirPosition, | ||
uint reservoirArrayIndex) | ||
|
||
Packs and stores the GI reservoir into the provided reservoir storage buffer. Buffer addressing works similar to `RTXDI_LoadGIReservoir`. | ||
|
||
|
||
## Basic Resampling Functions | ||
|
||
### `RTXDI_CombineGIReservoirs` | ||
|
||
bool RTXDI_CombineGIReservoirs( | ||
inout RTXDI_GIReservoir reservoir, | ||
const RTXDI_GIReservoir newReservoir, | ||
float random, | ||
float targetPdf) | ||
|
||
Adds a reservoir with one sample into this reservoir. Returns `true` if the new reservoir's sample was selected, `false` if not. | ||
|
||
### `RTXDI_FinalizeGIResampling` | ||
|
||
void RTXDI_FinalizeGIResampling( | ||
inout RTXDI_GIReservoir reservoir, | ||
float normalizationNumerator, | ||
float normalizationDenominator) | ||
|
||
Performs normalization of the reservoir after streaming. After this function is called, the reservoir's `weightSum` field becomes its inverse PDF that can be used for shading or for further reservoir combinations. | ||
|
||
The `normalizationNumerator` and `normalizationDenominator` parameters specify the normalization scale for bias correction. Basic applications like streaming of initial light samples will set the numerator to 1.0 and the denominator to M (the number of samples in the reservoir). Spatio-temporal resampling will normally compute the numerator and denominator by weighing the final selected sample against the original surfaces used in resampling. | ||
|
||
**Note:** unlike with direct lighting reservoirs, the GI reservoirs do not store the target PDF. In order for `RTXDI_FinalizeGIResampling` to work correctly, the denominator must include the target PDF of the selected sample! | ||
|
||
### `RTXDI_MakeGIReservoir` | ||
|
||
RTXDI_GIReservoir RTXDI_MakeGIReservoir( | ||
const float3 samplePos, | ||
const float3 sampleNormal, | ||
const float3 sampleRadiance, | ||
const float samplePdf) | ||
|
||
Creates a GI reservoir from a raw light sample. | ||
|
||
**Note**: the original sample PDF can be embedded into `sampleRadiance`, in which case the `samplePdf` parameter should be set to 1.0. | ||
|
||
|
||
## High-Level Sampling and Resampling Functions | ||
|
||
### `RTXDI_GITemporalResampling` | ||
|
||
struct RTXDI_GITemporalResamplingParameters | ||
{ | ||
float3 screenSpaceMotion; | ||
uint sourceBufferIndex; | ||
uint maxHistoryLength; | ||
uint biasCorrectionMode; | ||
float depthThreshold; | ||
float normalThreshold; | ||
uint maxReservoirAge; | ||
bool enablePermutationSampling; | ||
bool enableFallbackSampling; | ||
}; | ||
GI_GIReservoir RTXDI_GITemporalResampling( | ||
const uint2 pixelPosition, | ||
const RAB_Surface surface, | ||
const RTXDI_GIReservoir inputReservoir, | ||
inout RAB_RandomSamplerState rng, | ||
const RTXDI_GITemporalResamplingParameters tparams, | ||
const RTXDI_ResamplingRuntimeParameters params) | ||
|
||
Implements the core functionality of the temporal resampling pass. Takes the previous G-buffer, motion vectors, and two GI reservoir buffers - current and previous - as inputs. Tries to match the surfaces in the current frame to surfaces in the previous frame. If a match is found for a given pixel, the current and previous reservoirs are combined. | ||
|
||
An optional visibility ray may be cast if enabled with the `tparams.biasCorrectionMode` setting, to reduce the resampling bias. That visibility ray should ideally be traced through the previous frame BVH, but can also use the current frame BVH if the previous is not available - that will produce more bias. | ||
|
||
For more information on the members of the `RTXDI_GITemporalResamplingParameters` structure, see the comments in the source code. | ||
|
||
### `RTXDI_SpatialResampling` | ||
|
||
struct RTXDI_GISpatialResamplingParameters | ||
{ | ||
uint sourceBufferIndex; | ||
float depthThreshold; | ||
float normalThreshold; | ||
uint numSamples; | ||
float samplingRadius; | ||
uint biasCorrectionMode; | ||
}; | ||
RTXDI_GIReservoir RTXDI_GISpatialResampling( | ||
const uint2 pixelPosition, | ||
const RAB_Surface surface, | ||
const RTXDI_GIReservoir inputReservoir, | ||
inout RAB_RandomSamplerState rng, | ||
const RTXDI_GISpatialResamplingParameters sparams, | ||
const RTXDI_ResamplingRuntimeParameters params) | ||
|
||
Implements the core functionality of the spatial resampling pass. Operates on the current frame G-buffer and its reservoirs. For each pixel, considers a number of its neighbors and, if their surfaces are similar enough to the current pixel, combines their reservoirs. | ||
|
||
Optionally, one visibility ray is traced for each neighbor being considered, to reduce bias, if enabled with the `sparams.biasCorrectionMode` setting. | ||
|
||
For more information on the members of the `RTXDI_GISpatialResamplingParameters` structure, see the comments in the source code. | ||
|
||
|
||
### `RTXDI_SpatioTemporalResampling` | ||
|
||
|
||
struct RTXDI_GISpatioTemporalResamplingParameters | ||
{ | ||
float3 screenSpaceMotion; | ||
uint sourceBufferIndex; | ||
uint maxHistoryLength; | ||
float depthThreshold; | ||
float normalThreshold; | ||
uint maxReservoirAge; | ||
uint numSpatialSamples; | ||
float samplingRadius; | ||
uint biasCorrectionMode; | ||
bool enablePermutationSampling; | ||
bool enableFallbackSampling; | ||
}; | ||
RTXDI_GIReservoir RTXDI_GISpatioTemporalResampling( | ||
const uint2 pixelPosition, | ||
const RAB_Surface surface, | ||
RTXDI_GIReservoir inputReservoir, | ||
inout RAB_RandomSamplerState rng, | ||
const RTXDI_GISpatioTemporalResamplingParameters stparams, | ||
const RTXDI_ResamplingRuntimeParameters params) | ||
|
||
Implements the core functionality of a combined spatio-temporal resampling pass. This is similar to a sequence of `RTXDI_GITemporalResampling` and `RTXDI_GISpatialResampling`, with the exception that the input reservoirs are all taken from the previous frame. This function is useful for implementing a lighting solution in a single shader, which generates the initial samples, applies spatio-temporal resampling, and shades the final samples. | ||
|
||
### `RTXDI_GIBoilingFilter` | ||
|
||
void RTXDI_GIBoilingFilter( | ||
uint2 LocalIndex, | ||
float filterStrength, | ||
RTXDI_ResamplingRuntimeParameters params, | ||
inout RTXDI_GIReservoir reservoir) | ||
|
||
Applies a boiling filter over all threads in the compute shader thread group. This filter attempts to reduce boiling by removing reservoirs whose weighted radiance is significantly higher than the weighted radiances of their neighbors. | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Oops, something went wrong.