Skip to content

FSR™ Ray Regeneration 1.1.0

Sample output from FSR™ Ray Regeneration

Figure 1: Sample output from FSR™ Ray Regeneration.

FSR™ Ray Regeneration is a machine learning-based denoiser for ray-traced workloads. It improves visual quality by reducing noise in rendered frames while preserving detail.

Table of contents

Introduction

Since the advent of hardware-accelerated ray tracing, real-time game and rendering engines have increasingly adopted ray tracing techniques. From realistic reflections to finely detailed shadows, ray tracing has significantly elevated visual fidelity in modern games. As GPU ray tracing capabilities continue to improve, developers are pushing the boundaries of global illumination and exploring real-time path tracing. However, raw ray-traced outputs are inherently noisy, making effective denoising essential for producing clean, high-quality images.

Denoising transforms noisy ray- or path-traced outputs into coherent, visually accurate images, often leveraging both spatial and temporal information. FSR™ Ray Regeneration performs this task with a key advantage: it uses machine learning to dynamically determine optimal filter weights, delivering superior results compared to purely analytical methods.

Decoupled denoising vs. joint denoising

FSR™ Ray Regeneration performs denoising independently of upscaling, making it a decoupled denoiser. In contrast, denoising and upscaling can also be combined into a single, joint operation. Both approaches are valid, but decoupling denoising into its own dispatch call simplifies adding additional rendered content afterward.

Although upscaling is not strictly required for denoising, rendering expensive ray-traced effects, especially path tracing, at native resolution can be very demanding on hardware. With significant advancements in upscaling technology, incorporating upscaling into the rendering pipeline is strongly recommended. Additionally, the temporal anti-aliasing provided by upscalers such as FSR™ 4 adds an extra layer of smoothing when used alongside FSR™ Ray Regeneration. For optimal visual results, FSR™ Ray Regeneration should be paired with FSR™ 4.

Denoising modes

To maximize compatibility and efficiency when denoising your game’s specific workloads, FSR™ Ray Regeneration provides multiple input/output configurations:

ConfigurationInputs
FFX_DENOISER_MODE_4_SIGNALSDirect specular, direct diffuse, indirect specular, indirect diffuse
FFX_DENOISER_MODE_2_SIGNALSFused specular and diffuse
FFX_DENOISER_MODE_1_SIGNALFused specular and diffuse into one signal

The selected mode is specified via the mode member of the ffxCreateContextDescDenoiser context creation description.

Additionally, you can denoise the dominant light visibility as a separate signal by adding the FFX_DENOISER_ENABLE_DOMINANT_LIGHT to the flags member of the ffxCreateContextDescDenoiser context creation description.

Getting started

Setting up your project

FSR™ Ray Regeneration is part of the FSR™ SDK. Before getting started, make sure to read the Getting Started with the FSR™ API guide. Once familiar with the API, follow these steps to prepare your project for FSR™ Ray Regeneration:

  1. Add the FSR™ API include headers to your project’s include directories.
  2. Add the FSR™ Ray Regeneration headers to your project’s include directories.
  3. Link your project against amd_fidelityfx_loader_dx12.lib, located in FidelityFX/signedbin.
  4. Copy amd_fidelityfx_loader_dx12.dll and amd_fidelityfx_denoiser_dx12.dll from FidelityFX/signedbin into your project’s executable directory.

Querying support

Before creating a context, you can check for support by using the ffxQuery API to enumerate the available contexts. If the query returns no contexts, support for FSR™ Ray Regeneration is not available.

The FSR™ Ray Regeneration sample demonstrates how to query and handle the available contexts:

uint64_t DenoiserVersionCount = 0;
ffx::QueryDescGetVersions queryDesc = {};
queryDesc.createDescType = FFX_API_EFFECT_ID_DENOISER;
queryDesc.device = m_device;
queryDesc.outputCount = &DenoiserVersionCount;
ffx::Query(queryDesc);
m_DenoiserVersionIds.resize(DenoiserVersionCount);
m_DenoiserVersionStrings.resize(DenoiserVersionCount);
queryDesc.versionIds = m_DenoiserVersionIds.data();
queryDesc.versionNames = m_DenoiserVersionStrings.data();
ffx::Query(queryDesc);
m_DenoiserAvailable = !m_DenoiserVersionIds.empty();

Creating the context

To start using FSR™ Ray Regeneration, you need to create a denoiser context. This requires filling out an ffxCreateContextDescDenoiser struct along with a backend description. Then, pass these structs to ffxCreateContext. The parameters you provide determine the internal resource allocations and pipeline state compilations. Creating contexts in time-critical paths is not recommended due to their initialization cost. If the hardware does not support FSR™ Ray Regeneration, ffxCreateContext will return an error and the context pointer will remain unset.

ffx::CreateBackendDX12Desc backendDesc = {};
backendDesc.device = GetDevice()->GetImpl()->DX12Device();
ffx::CreateContextDescDenoiser createDesc = {};
createDesc.version = FFX_DENOISER_VERSION;
createDesc.maxRenderSize = { resInfo.UpscaleWidth, resInfo.UpscaleHeight };
createDesc.mode = m_DenoiserMode;
// Optional flags
if (m_EnableDebugging)
createDesc.flags |= FFX_DENOISER_ENABLE_DEBUGGING;
if (m_EnableDominantLightVisibilityDenoising)
createDesc.flags |= FFX_DENOISER_ENABLE_DOMINANT_LIGHT;
// Create the denoiser context
ffx::CreateContext(m_pDenoiserContext, nullptr, createDesc, backendDesc);

For use cases that require a specific context version, you can override it by passing an optional ffxOverrideVersion description to ffxCreateContext.

ffx::CreateContextDescOverrideVersion versionOverride = {};
versionOverride.versionId = m_DenoiserVersionIds[m_SelectedDenoiserVersion];
// Create the denoiser context
ffx::CreateContext(m_pDenoiserContext, nullptr, createDesc, backendDesc, versionOverride);

Dispatching

Denoising is performed on the GPU using ffxDispatch. This function takes a ffxDispatchDescDenoiser struct along with input/output structs for the signals. The templated ffx_api.hpp allows chaining multiple inputs together as variable arguments. This enables concise calls for the different signal configurations.

Example: 4 signals + dominant light

// Denoise 4 signals + dominant light
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput4Signals dispatchDenoiserInputs = {};
ffx::DispatchDescDenoiserInputDominantLight dispatchDenoiserInputsDominantLight = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs, dispatchDenoiserInputsDominantLight);

Example: 4 signals

// Denoise 4 signals
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput4Signals dispatchDenoiserInputs = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs);

Example: 2 signals + dominant light

// Denoise 2 signals + dominant light
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput2Signals dispatchDenoiserInputs = {};
ffx::DispatchDescDenoiserInputDominantLight dispatchDenoiserInputsDominantLight = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs, dispatchDenoiserInputsDominantLight);

Example: 2 signals

// Denoise 2 signals + dominant light
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput2Signals dispatchDenoiserInputs = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs);

Example: 1 fused signal + dominant light

// Denoise just 1 fused signal + dominant light
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput1Signal dispatchDenoiserInputs = {};
ffx::DispatchDescDenoiserInputDominantLight dispatchDenoiserInputsDominantLight = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs, dispatchDenoiserInputsDominantLight);

Example: 1 fused signal

// Denoise just 1 fused signal
ffx::DispatchDescDenoiser dispatchDesc = {};
ffx::DispatchDescDenoiserInput1Signal dispatchDenoiserInputs = {};
// Fill in the structs with the appropriate data:
// ...
ffx::Dispatch(m_pDenoiserContext, dispatchDesc, dispatchDenoiserInputs);

The dispatch call requires additional parameters that provide per-frame information and resources. These are mandatory for accurate temporal and spatial denoising.

ParameterDescription
linearDepthResource containing absolute linear depth values for the current frame.

Preferred format: R32_FLOAT
R: linear depth (abs(CurrentLinearDepth))
motionVectorsResource containing motion vectors for the current frame.

Preferred format: RGBA16_FLOAT
RG: pixel movement in 2D (PreviousUV - CurrentUV)
B: absolute linear depth delta (abs(PreviousLinearDepth) - abs(CurrentLinearDepth))
normalsResource containing normals and roughness for the current frame.

Preferred format: RGB10A2_UNORM
RG: octahedrally encoded normals (see Encoding normals)
B: linear roughness
A: material type (see Encoding material type)
specularAlbedoResource containing specular albedo for the current frame (see Generating specular albedo).
If FFX_DENOISER_DISPATCH_NON_GAMMA_ALBEDO is not added to flags, all channels are expected to be encoded using sqrt.

Preferred format: RGBA8_UNORM
RGB: specular albedo
diffuseAlbedoResource containing diffuse albedo for the current frame.
If FFX_DENOISER_DISPATCH_NON_GAMMA_ALBEDO is not added to flags, all channels are expected to be encoded using sqrt.

Preferred format: RGBA8_UNORM
RGB: diffuse albedo (e.g., BaseColor * (1 - Metalness))
motionVectorScaleRG: The scale factor for transforming the 2D motion vectors into UV space.
For 2D motion vectors computed as PreviousUV - CurrentUV, use { .x = +1.0f, .y = +1.0f }.
For 2D motion vectors computed as PreviousNDC - CurrentNDC, use { .x = +0.5f, .y = -0.5f }
B: The scale factor for transforming the linear depth delta.
For linear depth deltas computed as abs(PreviousLinearDepth) - abs(CurrentLinearDepth), use { .z = +1.0f }.
jitterOffsetsSubpixel jitter offsets applied to the camera projection, expressed in screen pixels.
cameraPositionDeltaCamera movement since last frame (PreviousPosition - CurrentPosition).
cameraRightRight (left) vector of the camera if using a right-handed (left-handed) coordinate system, expressed in world space.
cameraUpUp vector of the camera, expressed in world space.
cameraForwardForward direction of the camera, expressed in world space (i.e., the direction the camera is looking).
cameraAspectRatioAspect ratio of the camera.
cameraNearDistance to the near plane of the camera.
cameraFarDistance to the far plane of the camera.
cameraFovAngleVerticalVertical field of view of the camera, expressed in radians.
renderSizeResolution used for rendering the current frame input resources.
deltaTimeTime elapsed since the last frame, expressed in milliseconds.
frameIndexIndex of the current frame.
flagsZero or a combination of values from FfxApiDispatchDenoiserFlags.

Depth values and depth deltas are computed using absolute linear depth.

  • Absolute means the depth value is independent of whether the camera is facing forward or backward in view space.
  • Linear means the depth is expressed in view space, rather than NDC space. The dispatch parameters below are closely related and must remain consistent to ensure correct reprojection.
ParameterDescription
linearDepthabs(CurrentLinearDepth)
motionVectorsabs(PreviousLinearDepth) - abs(CurrentLinearDepth)
cameraForwardForward direction of the camera, expressed in world space (i.e., the direction the camera is looking).

cameraRight, cameraUp and cameraForward correspond to the view-to-world basis vectors, potentially permuted and/or sign-inverted to account for axis mappings and flips introduced by the view-to-clip (view-to-projection) transform.

Configuring settings

To achieve the best results with FSR™ Ray Regeneration, some settings may need to be adjusted. The default values are already set to sensible presets and are not simply zeros. A recommended best practice when modifying settings is to first query the current defaults via the API. This ensures that you start from a valid baseline.

Example code for querying the default value of the stability bias setting:

float stabilityBias;
ffx::QueryDescDenoiserGetDefaultKeyValue queryDesc = {};
queryDesc.key = FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS;
queryDesc.count = 1u;
queryDesc.data = &stabilityBias;
ffx::Query(m_pDenoiserContext, queryDesc);

New denoiser settings can be committed at any time before calling ffxDispatch and do not need to be updated every frame.

To apply the settings, use ffxConfigure.

Example code for configuring the stability bias setting with a custom value:

float stabilityBias = ...;
ffx::ConfigureDescDenoiserKeyValue configureDesc = {};
configureDesc.key = FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS;
configureDesc.count = 1u;
configureDesc.data = &stabilityBias;
ffx::Configure(m_pDenoiserContext, configureDesc);

Querying the currently used version

The denoiser context in FSR™ Ray Regeneration follows the same design pattern as FSR™ Upscaling and FSR™ Frame Generation. This means a driver override may be present, so the actual context version might differ from the version defined in the header as FFX_DENOISER_VERSION.

To determine the actual version of the context, use the following ffxQuery function:

uint32_t versionMajor = 0;
uint32_t versionMinor = 0;
uint32_t versionPatch = 0;
ffx::QueryDescDenoiserGetVersion queryDesc = {};
queryDesc.device = m_device;
queryDesc.major = &versionMajor;
queryDesc.minor = &versionMinor;
queryDesc.patch = &versionPatch;
ffx::Query(m_pDenoiserContext, queryDesc);

Cleaning up

The application is responsible for cleaning up any created denoiser context. This is done by calling ffxDestroyContext with the context to be destroyed.

Performance

FSR™ Ray Regeneration performance may vary depending on your target hardware and configuration.

Denoising 4 signals + dominant light signal:

Target Render ResolutionAverage [ms]
960x5401.13
1920x10804.09
2560x14407.72

Denoising 4 signals:

Target Render ResolutionAverage [ms]
960x5401.09
1920x10803.93
2560x14407.37

Denoising 2 signals + dominant light signal:

Target Render ResolutionAverage [ms]
960x5400.94
1920x10803.33
2560x14406.34

Denoising 2 signals:

Target Render ResolutionAverage [ms]
960x5400.87
1920x10803.05
2560x14405.84

Denoising 1 signal + dominant light signal:

Target Render ResolutionAverage [ms]
960x5400.85
1920x10802.85
2560x14405.51

Denoising 1 signal:

Target Render ResolutionAverage [ms]
960x5400.78
1920x10802.63
2560x14405.18

Performance figures are accurate at the time of writing for an AMD Radeon™ RX 9070 XT and are subject to change.

Memory requirements

FSR™ Ray Regeneration requires additional GPU local memory for use by the GPU. When using the API, this memory is allocated during context creation via the series of callbacks that make up the backend interface. This memory is used to store intermediate surfaces computed by the algorithm, as well as surfaces that persist across multiple frames of the application.

The tables below summarize the memory usage of FSR™ Ray Regeneration under various operating conditions.

Denoising 4 signals + dominant light signal:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x5401095752
1920x1080386196190
2560x1440656350306

Denoising 4 signals:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x5401045252
1920x1080369179190
2560x1440626320306

Denoising 2 signals + dominant light signal:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540913952
1920x1080322132190
2560x1440542236306

Denoising 2 signals:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540873552
1920x1080306116190
2560x1440512206306

Denoising 1 signal + dominant light signal:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540823052
1920x1080288100188
2560x1440480177303

Denoising 1 signal:

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540782652
1920x108027484190
2560x1440454148306

Memory figures are accurate at the time of writing for an AMD Radeon™ RX 9070 XT and are subject to change.

An application can query the amount of GPU local memory required by FSR™ Ray Regeneration before creating a denoiser context. See code examples for calling ffxQuery:

FfxApiEffectMemoryUsage memory = {};
ffx::QueryDescDenoiserGetGPUMemoryUsage queryDesc = {};
queryDesc.device = m_device;
queryDesc.maxRenderSize = denoiserContextDesc.maxRenderSize;
queryDesc.mode = denoiserContextDesc.mode;
queryDesc.flags = denoiserContextDesc.flags;
queryDesc.gpuMemoryUsage = &memory;
ffx::Query(queryDesc, versionOverride);
LOG(L"Denoiser GPU Memory Usage totalUsageInBytes %.3f MB aliasableUsageInBytes %.3f MB",
memory.totalUsageInBytes / 1048576.f,
memory.aliasableUsageInBytes / 1048576.f);

Debugging

Debug view

When the FSR™ Ray Regeneration context is created with FFX_DENOISER_ENABLE_DEBUGGING, integrations can provide an optional ffxDispatchDescDenoiserDebugView description to the dispatch call. This will enable an extra output pass to run internally and output relevant debug information into an app-provided render target.

FSR™ Ray Regeneration debug view in overview mode

Figure 2: FSR™ Ray Regeneration debug view in overview mode.

DataDescription
Motion VectorsPixel screen motion, the XY-components of the motionVectors input.
Motion Vectors ZPixel z motion (i.e. Linear Depth Delta), the Z-component of the motionVectors input.
Blue for negative deltas, Red for positive deltas.
Linear DepthValue of the linearDepth input.
Clamped to 1024 units.
NormalsDecoded normals of the normals input.
Reprojected ConfidenceIndicates the per pixel confidence of a history sample.
Black means the sample will not contribute in the accumulation.
Reprojected UVUV that will be used to sample history information.
View Centered PosReconstructed position centered around the camera.
This information is useful to validate that input camera parameters are correct.
Virtual Hit PosReconstructed virtual hit position used for low-roughness specular samples.
This information is useful to validate that input camera parameters and specular ray length are correct.
NN Input0First input channel to the neural network.
NN Input1Second input channel to the neural network.
NN Input2Third input channel to the neural network.
Composed LumaLuminance of the composited input signals.

Providing the ffxDispatchDescDenoiserDebugView to the dispatch call automatically enables the debug pass internally. Output information is written to an app-provided render target and it is therefore up to the app itself to implement how to composite the output information to the screen. FSR™ Ray Regeneration expects the output target to be in a 4-component format as all four data components are used. In the output target the RGB channels contains the color information to display and the A channel indicates which pixels have been written to. The mode parameter toggles between an overview mode or fullscreen mode of a single data viewport. The viewportIndex controls which viewport to display if FFX_API_DENOISER_DEBUG_VIEW_MODE_FULLSCREEN_VIEWPORT is selected as the active mode. The viewportIndex is clamped between 0 and FFX_API_DENOISER_DEBUG_VIEW_MAX_VIEWPORTS - 1.

ffx::DispatchDescDenoiserDebugView dispatchDenoiserDebugView = {};
dispatchDenoiserDebugView.output = SDKWrapper::ffxGetResourceApi(m_pDebugView->GetResource(), FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
dispatchDenoiserDebugView.outputSize = { m_pDebugView->GetDesc().Width, m_pDebugView->GetDesc().Height };
dispatchDenoiserDebugView.mode = m_DebugViewMode;
dispatchDenoiserDebugView.viewportIndex = (uint32_t)m_DebugViewport;
ParameterDescription
outputTarget output resource to write debug visualization data into.
outputSizeThe resolution of the output resource.
modeAn entry of FfxApiDenoiserDebugViewMode that selects the mode used for visualization.

FFX_API_DENOISER_DEBUG_VIEW_MODE_OVERVIEW
FFX_API_DENOISER_DEBUG_VIEW_MODE_FULLSCREEN_VIEWPORT
viewportIndexWhen mode is set to FFX_API_DENOISER_DEBUG_VIEW_MODE_FULLSCREEN_VIEWPORT, use this index to indicate which debug viewport to fullscreen.
The value is clamped between 0 and FFX_API_DENOISER_DEBUG_VIEW_MAX_VIEWPORTS - 1

Best practices

Generating noisy signals

FSR™ Ray Regeneration, depending on the selected mode, expects one or more low-sample input radiance signals for denoising. High-quality inputs are essential to achieve the best results, as the algorithm is not designed to “correct” artefacts present in the input data. Below are some guidelines for generating high-quality noisy inputs.

Stochastic sampling

In most physically-based path tracers, stochastic sampling is used to simulate realistic lighting behaviors such as shadows, reflections, and indirect lighting. Stochastic sampling introduces randomness into ray directions so that, over many samples, the result converges to a noise-free signal. At low sample counts, this randomness produces perceivable noise in the rendered image. This noise is exactly what FSR™ Ray Regeneration targets for denoising. Understanding and managing the quality of this noise is critical for achieving optimal denoising results.

Noisy Input Denoised Output
Noisy ray-traced input to FSR™ Ray Regeneration Denoised output from FSR™ Ray Regeneration

Figure 3: left: Noisy ray-traced input to FSR™ Ray Regeneration. right: Corresponding denoised output from FSR™ Ray Regeneration.

Noise

We recommend using white noise and high-quality hash functions with minimal correlations when driving stochastic sampling. Examples of suitable hash functions include xxhash32 and lowbias32.

A good practice is to ensure that your ray-traced image converges to a noise-free result when using many samples without denoising. If it does not, verify whether your chosen noise or hash function is introducing unwanted correlations or other artifacts that are difficult to denoise.

While blue noise often performs well in many scenarios, it can introduce correlation artifacts if its period is not sufficiently large. For this reason, white noise is generally preferred for FSR™ Ray Regeneration inputs.

Noise reduction techniques such as ReSTIR can help reduce noise in the input signals. However, these techniques often introduce cross-sample dependencies that FSR™ Ray Regeneration is not designed to handle. In such cases, it is important to disrupt correlation patterns using strategies like permutation sampling or randomization of temporal reuse.

Usage of radiance caching

Tracing paths recursively quickly becomes too expensive for real-time rendering. To manage this cost, many real-time path tracers apply radiance caching after only a few recursive bounces. Radiance caching provides a precomputed store of radiance that can be sampled by direction, enabling early ray termination and significantly reducing computational overhead.

Using a radiance cache generally results in a more complete signal, since additional lighting contributions, too expensive to compute per frame, are incorporated through cached samples. This is naturally beneficial for FSR™ Ray Regeneration, as more complete inputs typically yield more complete and stable outputs.

However, the caching process often trades accuracy for performance, which may introduce artifacts into the final image. Watch out for low-frequency noise or temporal inconsistencies, as these issues can be amplified by the denoiser if not mitigated.

See AMD GI-1.0 for examples and guidance on radiance caching.

Tweakable denoiser settings

Even with high-quality inputs and the recommended best practices applied, some artifacts may still remain. FSR™ Ray Regeneration exposes a number of tweakable settings, defined in FfxApiConfigureDenoiserKey, that allow fine-grained control over denoiser behavior. These parameters can help balance stability, sharpness, and noise reduction to best suit your application.

It is recommended to start from the default settings queried from the API and then adjust selectively according to what works best for your application. For guidance on retrieving and applying these settings, see Configuring settings.

Providing motion vectors

Space

A key part of any temporal algorithm is the provision of motion vectors.

FSR™ Ray Regeneration accepts motion vectors in a 2.5D format:

  • The XY components represent screen-space pixel motion, encoding the movement from a pixel in the current frame to the same pixel in the previous frame.
  • The Z component represents the linear view-space z delta.

2D motion vector

Figure 4: A 2D motion vector from a pixel in the current frame to the corresponding pixel in the previous frame.

If your application computes motion vectors in a space other than UV space (for example, NDC space), you can use the motionVectorScale member of the ffxDispatchDescDenoiser dispatch description to scale them appropriately for FSR™ Ray Regeneration.

Example HLSL and C++ code illustrating NDC-space motion vector scaling:

// GPU: Example of NDC motion vector computation
float3 motionVector;
// 2D motion expressed in NDC space
motionVector.xy = (previousPosition.xy / previousPosition.w) - (currentPosition.xy / currentPosition.w) - cancelJitter;
// Absolute linear depth delta
motionVector.z = abs(previousPosition.w) - abs(currentPosition.w);
// CPU: Matching FSR™ Ray Regeneration motionVectorScale configuration to convert motion vectors from NDC to UV space
dispatchDesc.motionVectorScale.x = +0.5f;
dispatchDesc.motionVectorScale.y = -0.5f;
dispatchDesc.motionVectorScale.z = +1.0f;

Coverage

FSR™ Ray Regeneration achieves higher-quality denoising when more objects provide valid motion vectors. It is therefore recommended that all opaque, alpha-tested, and alpha-blended objects write motion vectors for every pixel they cover. Additionally, if vertex shader effects are applied, such as scrolling UVs or procedural vertex animations, these transformations should also be incorporated into the motion vector computation to ensure optimal results.

Encoding normals

Each 3D normal vector represents a direction on the unit sphere. To store normals efficiently, we can project the unit sphere onto a 2D plane and remap each normal vector as a 2D UV coordinate. This process is called octahedral encoding. The normals passed to FSR™ Ray Regeneration are required to be stored using octahedral encoding.

Below are examples of how to encode and decode normals using this method.

Octahedral encoding of normals:

float2 NormalToOctahedronUv(float3 N)
{
N.xy /= abs(N.x) + abs(N.y) + abs(N.z);
float2 k = sign(N.xy);
float s = saturate(-N.z);
N.xy = lerp(N.xy, (1.0 - abs(N.yx)) * k, s);
return N.xy * 0.5 + 0.5;
}

Octahedral decoding of normals:

float3 OctahedronUvToNormal(float2 UV)
{
UV = UV * 2.0f - 1.0f;
float3 N = float3(UV, 1.0f - abs(UV.x) - abs(UV.y));
float t = saturate(-N.z);
float2 s = sign(N.xy);
N.xy += s * t;
return normalize(N);
}

Encoding material type

The alpha channel of the normals member of the ffxDispatchDescDenoiser dispatch description is used to encode the material type. The material type is a lightweight way to distinguish different surface materials from one another. FSR™ Ray Regeneration will reject mixing between two pixels if their material IDs do not match. This prevents cross-material blending and is an effective solution for avoiding ghosting artifacts in complex material setups.

FSR™ Ray Regeneration supports three material type variations. Material type 0 is reserved as the default material. Upon encoding the material type into the normals member of the ffxDispatchDescDenoiser input, the stored value should be a normalized fraction relative to the maximum number of supported materials.

// ... ray tracing pass ...
uint materialType = hit.materialType;
float materialTypeFrac = materialType / 3.0;
outNormalsResource[pixel] = float4(normalOctahedralUv, roughness, materialTypeFrac);

Generating specular albedo

Specular albedo can be generated using various methods. In most physically-based rendering pipelines, similar computations are already performed as part of the lighting computations. These computations can be reused to generate a standalone specular albedo feature map.

Below are two commonly used methods for generating specular albedo in real-time rendering applications.

Example: BRDF lookup table

A BRDF LUT is generated offline and stored in a floating-point texture. At runtime, this LUT is sampled to look up approximate BRDF characteristics, commonly used in the split-sum approximation for image-based lighting.

A typical GGX BRDF look-up table

Figure 5: A typical GGX BRDF look-up table.

The following HLSL code snippet illustrates how to sample the BRDF LUT and use the resulting scale and bias to generate specular albedo:

// ... ray tracing pass ...
float NoV = dot(normal, view);
float2 brdf = BrdfLUT.SampleLevel(LinearSampler, float2(NoV, hit.materialRoughness), 0);
const float3 MinReflectance = float3(0.04, 0.04, 0.04);
float3 F0 = lerp(MinReflectance, hit.materialAlbedo.rgb, hit.materialMetallic);
float3 specularAlbedo = F0 * brdf.x + brdf.y;
// ...

Example: BRDF Approximation

The following HLSL code snippets illustrate how to approximate the BRDF influence at runtime without the need for an offline baked LUT:

// [Ray Tracing Gems, Chapter 32]
float3 ApproximateBRDF(float3 F0, float alpha, float NoV)
{
NoV = abs(NoV);
float4 x = float4(1.0, NoV, NoV*NoV, NoV*NoV*NoV);
float4 y = float4(1.0, alpha, alpha*alpha, alpha*alpha*alpha);
float2x2 M1 = float2x2(0.99044, -1.28514,
1.29678, -0.755907);
float3x3 M2 = float3x3(1.0, 2.92338, 59.4188,
20.3225, -27.0302, 222.592,
121.563, 626.13, 316.627);
float2x2 M3 = float2x2(0.0365463, 3.32707,
9.0632, -9.04756);
float3x3 M4 = float3x3(1.0, 3.59685, -1.36772,
9.04401, -16.3174, 9.22949,
5.56589, 19.7886, -20.2123);
float bias = dot(mul(M1, x.xy), y.xy) * rcp(dot(mul(M2, x.xyw), y.xyw));
float scale = dot(mul(M3, x.xy), y.xy) * rcp(dot(mul(M4, x.xzw), y.xyw));
// Hack for specular reflectance of 0
bias *= saturate(F0.g * 50);
return mad(F0, max(0, scale), max(0, bias));
}
// ... ray tracing pass ...
const float3 MinReflectance = float3(0.04, 0.04, 0.04);
float3 F0 = lerp(MinReflectance, hit.materialAlbedo.rgb, hit.materialMetallic);
float alpha = hit.materialRoughness * hit.materialRoughness;
float NoV = dot(normal, view);
float3 specularAlbedo = ApproximateBRDF(F0, alpha, NoV);
// ...

Requirements

  • AMD FSR™ Ray Regeneration requires an AMD Radeon™ RX 9000 Series GPU or later.
  • DirectX® 12 + Shader Model 6.6
  • Windows® 11

Version history

VersionDate
1.1.0March 18, 2026
1.0.0December 10, 2025

For more details, refer to the changelog.

References

See also