Skip to content

AMD FSR™ Ray Regeneration 1.2.1

Sample output from AMD FSR™ Ray Regeneration

Figure 1: Sample output from AMD FSR™ Ray Regeneration.

AMD 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

1. 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. AMD 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.

1.1. Decoupled denoising vs. joint denoising

AMD 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 AMD FSR™ 4 adds an extra layer of smoothing when used alongside AMD FSR™ Ray Regeneration. For optimal visual results, AMD FSR™ Ray Regeneration should be paired with AMD FSR™ 4.

1.2. Signals

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

Signal flagsInput/output signals:
FFX_DENOISER_SIGNAL_AMBIENT_OCCLUSIONAmbient occlusion (in [0, 1]/[0, 1])
FFX_DENOISER_SIGNAL_DIRECT_DIFFUSEDirect diffuse
FFX_DENOISER_SIGNAL_DIRECT_SPECULARDirect specular
FFX_DENOISER_SIGNAL_DOMINANT_LIGHT_VISIBILITYDominant light visibility (in [0, FP16_MAX]/[0, 1])
FFX_DENOISER_SIGNAL_INDIRECT_DIFFUSEIndirect diffuse
FFX_DENOISER_SIGNAL_INDIRECT_SPECULARIndirect specular
FFX_DENOISER_SIGNAL_SPECULAR_OCCLUSIONSpecular occlusion (in [0, 1]/[0, 1])

The selected signals are specified via the signalFlags member of the ffxCreateContextDescDenoiser context creation description.

2. Getting started

2.1. Setting up your project

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

  1. Add the AMD FSR™ API include headers to your project’s include directories.
  2. Add the AMD FSR™ Ray Regeneration include 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.

2.2. Setting up logging

By default, all messages (FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_VERBOSE) are output as debug strings. Logging can be explicitly set up before (or after) context creation for the denoiser effect by passing a ffxConfigureDescGlobalDebug configure description to ffxConfigure.

// Example: configuring the logging callback for the denoiser effect.
ffx::ConfigureDescGlobalDebug configureDesc = {};
configureDesc.effectId = FFX_API_EFFECT_ID_DENOISER;
// If set to nullptr, messages will be output as debug strings.
configureDesc.fpMessage = [](uint32_t type, const wchar_t* message)
{
switch (type)
{
case FFX_API_MESSAGE_TYPE_ERROR:
{
LogError(message);
break;
}
case FFX_API_MESSAGE_TYPE_WARNING:
{
LogWarning(message);
break;
}
default:
{
break;
}
}
};
configureDesc.debugLevel = FFX_API_CONFIGURE_GLOBALDEBUG_LEVEL_VERBOSE;
ffx::ReturnCode result = ffx::Configure(configureDesc);
assert(result == ffx::ReturnCode::Ok);

2.3. Querying support

Before context creation, you can query supported versions by passing a ffxQueryDescGetVersions query description to ffxQuery. If the query returns no contexts, support for AMD FSR™ Ray Regeneration is not available.

// Example: querying all supported AMD FSR™ Ray Regeneration versions.
uint64_t supportedVersionCount = 0;
ffx::QueryDescGetVersions queryDesc = {};
queryDesc.createDescType = FFX_API_EFFECT_ID_DENOISER;
queryDesc.device = GetDevice()->GetImpl()->DX12Device();
queryDesc.outputCount = &supportedVersionCount;
ffx::ReturnCode result = ffx::Query(queryDesc);
assert(result == ffx::ReturnCode::Ok);
m_DenoiserVersionIds.resize(supportedVersionCount);
m_DenoiserVersionStrings.resize(supportedVersionCount);
queryDesc.versionIds = m_DenoiserVersionIds.data();
queryDesc.versionNames = m_DenoiserVersionStrings.data();
result = ffx::Query(queryDesc);
assert(result == ffx::ReturnCode::Ok);
m_DenoiserAvailable = not versionIds.empty();

2.4. Creating the context

To start using AMD FSR™ Ray Regeneration, you need to create a denoiser context by passing a ffxCreateContextDescDenoiser context creation description along with a backend description 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 AMD FSR™ Ray Regeneration, ffxCreateContext will return an error and the context pointer will remain unset.

// Example: creating a denoiser context with the desired signal flags.
ffx::CreateBackendDX12Desc backendDesc = {};
backendDesc.device = GetDevice()->GetImpl()->DX12Device();
ffx::CreateContextDescDenoiser createDesc = {};
createDesc.version = FFX_DENOISER_VERSION;
createDesc.maxRenderSize = { resInfo.UpscaleWidth, resInfo.UpscaleHeight };
// Signals
createDesc.signalFlags = FFX_DENOISER_SIGNAL_NONE;
if (m_EnableDirectDiffuse)
{
// Flag indicating that direct diffuse needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE;
}
if (m_EnableDirectSpecular)
{
// Flag indicating that direct specular needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_DIRECT_SPECULAR;
}
if (m_EnableIndirectDiffuse)
{
// Flag indicating that indirect diffuse needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_INDIRECT_DIFFUSE;
}
if (m_EnableIndirectSpecular)
{
// Flag indicating that indirect specular needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_INDIRECT_SPECULAR;
}
if (m_EnableDominantLightVisibility)
{
// Flag indicating that dominant light visibility needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_DOMINANT_LIGHT_VISIBILITY;
}
if (m_EnableAmbientOcclusion)
{
// Flag indicating that ambient occlusion needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_AMBIENT_OCCLUSION;
}
if (m_EnableSpecularOcclusion)
{
// Flag indicating that specular occlusion needs to be denoised.
denoiserContextDesc.signalFlags |= FFX_DENOISER_SIGNAL_SPECULAR_OCCLUSION;
}
// Checkerboard reconstruction (optional, must be a subset of signalFlags)
createDesc.checkerboardSignalFlags = FFX_DENOISER_SIGNAL_NONE;
// Flags
denoiserContextDesc.flags = {};
if (m_EnableDebugging)
{
// Flag indicating that debug features may be enabled. Memory usage may increase.
denoiserContextDesc.flags |= FFX_DENOISER_ENABLE_DEBUGGING;
}
if (m_EnableValidation)
{
// Flag indicating that exhaustive validation should be enabled. Performance may decrease.
denoiserContextDesc.flags |= FFX_DENOISER_ENABLE_VALIDATION;
}
// Create the denoiser context
ffx::ReturnCode result = ffx::CreateContext(m_pDenoiserContext, nullptr, createDesc, backendDesc);
assert(result == ffx::ReturnCode::Ok);

2.4.1. ffxCreateContextDescDenoiser

ParameterDescription
versionThe version of the API the application was built against. Must be set to FFX_DENOISER_VERSION.
maxRenderSizeMaximum size that rendering will be performed at.
signalFlagsSignal flags corresponding to a combination of values from FfxApiDenoiserSignalFlags.
checkerboardSignalFlagsSignal flags corresponding to a combination of values from FfxApiDenoiserSignalFlags for signals that use checkerboard reconstruction. Must be a subset of signalFlags.
flagsCreate flags corresponding to zero or a combination of values from FfxApiCreateContextDenoiserFlags.

2.4.2. FfxApiCreateContextDenoiserFlags

FlagDescription
FFX_DENOISER_ENABLE_DEBUGGINGFlag indicating that debug features may be enabled. Memory usage may increase.
FFX_DENOISER_ENABLE_VALIDATIONFlag indicating that exhaustive validation should be enabled. Performance may decrease.

For use cases that require a specific version, you can override the implicit default version by passing a ffxOverrideVersion description to ffxCreateContext too.

// Example: creating a denoiser context pinned to a specific version.
ffx::CreateContextDescOverrideVersion versionOverride = {};
versionOverride.versionId = m_DenoiserVersionIds[m_SelectedDenoiserVersion];
// Create the denoiser context
ffx::ReturnCode result = ffx::CreateContext(m_pDenoiserContext, nullptr, createDesc, backendDesc, versionOverride);
assert(result == ffx::ReturnCode::Ok);

2.5. Dispatching

Denoising is performed on the GPU by passing the below dispatch descriptions to ffxDispatch. Dispatch descriptions can be passed if and only if the corresponding dispatch conditions are met by the context. Furthermore, the majority of dispatch descriptions are non-optional and must be passed if and only if the corresponding dispatch conditions are met by the context.

Inputs and outputs of an AMD FSR™ Ray Regeneration dispatch

Figure 2: Inputs and outputs of an example AMD FSR™ Ray Regeneration dispatch for denoising direct diffuse, direct specular, indirect diffuse and indirect specular signals. The common input resources of ffxDispatchDescDenoiser are shown above, and the noisy input and denoised output signal resource of each dispatch description on either side.

AMD FSR™ Ray Regeneration uses a linked-list pattern to pass multiple dispatch descriptions to a single ffxDispatch call. Each description header contains a pNext pointer that chains the next description in sequence. The ffxDispatchDescDenoiser description is always the head of the chain; all signal and debug-view descriptions are linked after it.

All descriptions passed to a single ffxDispatch call must remain alive (i.e., not go out of scope) for the duration of that call.

// Example: dispatching all active signals using manual pNext chaining.
// Needs to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiser dispatchDesc = {};
// ... assign description members ...
ffxDispatchDescHeader* prevHeader = &dispatchDesc.header;
const auto link = [&prevHeader](auto& desc)
{
ffxDispatchDescHeader* const nextHeader = &desc.header;
prevHeader->pNext = nextHeader;
prevHeader = nextHeader;
};
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserAmbientOcclusion dispatchDescAmbientOcclusion = {};
if (m_EnableAmbientOcclusion)
{
// ... assign description members ...
link(dispatchDescAmbientOcclusion);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserDirectDiffuse dispatchDescDirectDiffuse = {};
if (m_EnableDirectDiffuse)
{
// ... assign description members ...
link(dispatchDescDirectDiffuse);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserDirectSpecular dispatchDescDirectSpecular = {};
if (m_EnableDirectSpecular)
{
// ... assign description members ...
link(dispatchDescDirectSpecular);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserDominantLight dispatchDescDominantLight = {};
if (m_EnableDominantLightVisibility)
{
// ... assign description members ...
link(dispatchDescDominantLight);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserIndirectDiffuse dispatchDescIndirectDiffuse = {};
if (m_EnableIndirectDiffuse)
{
// ... assign description members ...
link(dispatchDescIndirectDiffuse);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserIndirectSpecular dispatchDescIndirectSpecular = {};
if (m_EnableIndirectSpecular)
{
// ... assign description members ...
link(dispatchDescIndirectSpecular);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserSpecularOcclusion dispatchDescSpecularOcclusion = {};
if (m_EnableSpecularOcclusion)
{
// ... assign description members ...
link(dispatchDescSpecularOcclusion);
}
// Need to persist in the ::ffxDispatch scope.
ffx::DispatchDescDenoiserDebugView dispatchDescDebugView = {};
if (m_EnableDebugging && m_ShowDebugView)
{
// ... assign description members ...
link(dispatchDescDebugView);
}
// ffx::Dispatch cannot be used because it will reset pNext.
ffxReturnCode_t result = ::ffxDispatch(&m_pDenoiserContext, &dispatchDesc.header);
assert(result == FFX_API_RETURN_OK);

Alternatively, the templated ffx_api.hpp allows linking multiple inputs together as variable arguments.

// Example: dispatching indirect diffuse and specular signals using the variadic ffx::Dispatch helper.
// Denoise indirect diffuse and specular
ffx::DispatchDescDenoiser dispatchDesc = {};
// ... assign description members ...
ffx::DispatchDescDenoiserIndirectDiffuse dispatchDescIndirectDiffuse = {};
// ... assign description members ...
ffx::DispatchDescDenoiserIndirectSpecular dispatchDescIndirectSpecular = {};
// ... assign description members ...
ffx::ReturnCode result = ffx::Dispatch(m_pDenoiserContext, dispatchDesc, DispatchDescDenoiserIndirectDiffuse, DispatchDescDenoiserIndirectSpecular);
assert(result == ffx::ReturnCode::Ok);

2.5.1. Checkerboard reconstruction

To reduce ray tracing cost, signals can be rendered at half horizontal resolution by tracing alternate pixels each frame in a checkerboard pattern. AMD FSR™ Ray Regeneration can reconstruct a full-resolution signal from these half-resolution inputs using depth- and normal-aware bilateral filtering.

To enable checkerboard reconstruction for a signal, include its flag in both signalFlags and checkerboardSignalFlags at context creation. checkerboardSignalFlags must always be a subset of signalFlags.

In the checkerboard pattern, only half the pixels are traced per frame, alternating as follows:

// Checkerboard pattern: 0 = inactive (not traced), 1 = active (traced).
x=0 x=1 x=2 x=3
y=0: 0 1 0 1
y=1: 1 0 1 0
y=2: 0 1 0 1

Where 0 indicates an inactive pixel (not traced this frame) and 1 indicates an active pixel (traced this frame). The origin of the pattern alternates each frame via checkerboardOrigin in the per-signal FfxApiDenoiserSignal:

  • checkerboardOrigin = 0: pixel (0,0) is active (traced).
  • checkerboardOrigin = 1: pixel (1,0) is active (traced).

The typical usage is checkerboardOrigin = frameIndex & 1.

Active pixels must be packed into a half-width texture (width = renderSize.width / 2) before being passed as the input of the signal. AMD FSR™ Ray Regeneration will unpack them and reconstruct the inactive pixels internally.

2.5.2. ffxDispatchDescDenoiser

ParameterDescription
commandListA pointer to the command list.

DX12: pointer to ID3D12GraphicsCommandList.
linearDepthThe signed linear depth.

Channels:
R: Signed linear depth values for the current frame. CurrentLinearDepth
motionVectorsThe motion vectors.

Channels:
RG: 2D motion vectors. PreviousUV - CurrentUV
B: Signed linear depth delta. PreviousLinearDepth - CurrentLinearDepth
normalsThe normals, roughness and material type.

Channels:
RG: Octahedrally encoded normals
B: Linear Roughness
A: Material Type - See docs for more info.
specularAlbedoThe specular albedo.

Channels:
RGB: Specular albedo. The denoiser expects a software encoding chosen by the application: sqrt(SpecularAlbedo) by default, or the raw SpecularAlbedo (identity function) when FFX_DENOISER_DISPATCH_NON_ENCODED_ALBEDO is provided. This software encoding is a convention between the application and the denoiser and is orthogonal to the resource format: if a hardware sRGB format is used, the sampled value is already sRGB-decoded to linear by the hardware, and the software encoding above is then applied on top of that linear value.

The default sqrt encoding is a perceptual transfer function: it allocates more precision to darker values, matching human perception and reducing banding when the albedo is stored in a low-bit-depth format. For this reason, apply only one transfer function, not both: either use a hardware sRGB format together with FFX_DENOISER_DISPATCH_NON_ENCODED_ALBEDO (so the software step is the identity function), or store into a linear format and use the default software sqrt encoding. Combining a hardware sRGB format with the software sqrt encoding applies two perceptual transfer functions, which defeats the purpose: the precision redistribution is applied twice and warps the values.

Can be zero-initialized if and only if FFX_DENOISER_SIGNAL_DIRECT_SPECULAR, FFX_DENOISER_SIGNAL_INDIRECT_SPECULAR, and FFX_DENOISER_SIGNAL_DOMINANT_LIGHT_VISIBILITY are not set in the context creation signal flags.
diffuseAlbedoThe diffuse albedo.

Channels:
RGB: Diffuse albedo (e.g., BaseColor * (1 - Metalness)). The denoiser expects a software encoding chosen by the application: sqrt(DiffuseAlbedo) by default, or the raw DiffuseAlbedo (identity function) when FFX_DENOISER_DISPATCH_NON_ENCODED_ALBEDO is provided. This software encoding is a convention between the application and the denoiser and is orthogonal to the resource format: if a hardware sRGB format is used, the sampled value is already sRGB-decoded to linear by the hardware, and the software encoding above is then applied on top of that linear value.

The default sqrt encoding is a perceptual transfer function: it allocates more precision to darker values, matching human perception and reducing banding when the albedo is stored in a low-bit-depth format. For this reason, apply only one transfer function, not both: either use a hardware sRGB format together with FFX_DENOISER_DISPATCH_NON_ENCODED_ALBEDO (so the software step is the identity function), or store into a linear format and use the default software sqrt encoding. Combining a hardware sRGB format with the software sqrt encoding applies two perceptual transfer functions, which defeats the purpose: the precision redistribution is applied twice and warps the values.

Can be zero-initialized if and only if FFX_DENOISER_SIGNAL_DIRECT_DIFFUSE, FFX_DENOISER_SIGNAL_INDIRECT_DIFFUSE, and FFX_DENOISER_SIGNAL_DOMINANT_LIGHT_VISIBILITY are not set in the context creation signal flags.
motionVectorScaleThe motion vector scale factor.

Channels:
RG: 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 signed linear depth delta
    For linear depth deltas computed as PreviousLinearDepth - CurrentLinearDepth, use { .z = +1.0f }.
jitterOffsetsThe subpixel jitter offset applied to the camera projection. (Expressed in screen pixels)
cameraPositionDeltaThe position delta of the camera since last frame. PreviousPosition - CurrentPosition (Expressed in world space)
viewThe world-to-view transformation matrix for the current frame.

Matrix convention:
Storage: row-major (rows stored contiguously in memory)
Multiplication: row vectors v' = vM

Compatibility:
Column-major with column vectors (e.g., OpenGL®, Cauldron): direct copy (memcpy) - no transpose needed
Column-major with row vectors: requires transpose
Row-major with row vectors (e.g., DirectXMath): direct copy (memcpy)
Row-major with column vectors: requires transpose

Memory Layout Example (translation by (Tx, Ty, Tz)):
[m00 m01 m02 m03]   [1  0  0  0]
[m10 m11 m12 m13] = [0  1  0  0]
[m20 m21 m22 m23]   [0  0  1  0]
[m30 m31 m32 m33]   [Tx Ty Tz 1]
projectionThe (unjittered) view-to-projection transformation matrix for the current frame.

Matrix convention:
Storage: row-major (rows stored contiguously in memory)
Multiplication: row vectors v' = vM

Compatibility:
Column-major with column vectors (e.g., OpenGL®, Cauldron): direct copy (memcpy) - no transpose needed
Column-major with row vectors: requires transpose
Row-major with row vectors (e.g., DirectXMath): direct copy (memcpy)
Row-major with column vectors: requires transpose
linearDepthBoundsThe absolute linear depth bounds considered for denoising.

Passthrough is enabled for signal texels where the corresponding absolute linear depth values are outside [linearDepthBounds.min,linearDepthBounds.max].
renderSizeThe resolution that was used for rendering the input resources.
frameIndexThe index of the current frame. Must be previousFrameIndex + 1 for normal operation.

Any other value (jump or reset) is interpreted as a scene discontinuity and implicitly triggers FFX_DENOISER_DISPATCH_RESET, discarding temporal history. For intentional discontinuities such as camera cuts, set FFX_DENOISER_DISPATCH_RESET explicitly instead.
flagsDispatch flags corresponding to zero or a combination of values from FfxApiDispatchDenoiserFlags.

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

  • Signed means the depth value is dependent on whether the camera is facing forward or backward in view space.
  • Linear means the depth is expressed in view space (z component), rather than NDC space.

2.5.3. FfxApiDispatchDenoiserFlags

FlagDescription
FFX_DENOISER_DISPATCH_RESETFlag indicating that the history accumulation needs to be reset.
FFX_DENOISER_DISPATCH_NON_ENCODED_ALBEDOFlag controlling the software encode/decode transfer function applied to the (diffuse and specular) albedo textures. This is a software convention agreed between the application and the denoiser; it is independent of any hardware sRGB texture format.

When set: the application is expected to encode the albedo in software with the identity function; the denoiser decodes with the identity function.
When clear: the application is expected to encode the albedo in software with the sqrt function; the denoiser decodes with the square function.

2.5.4. FfxApiDenoiserSignal

Each per-signal dispatch description holds a signal member of type FfxApiDenoiserSignal, which carries the input resource, the output resource, and the checkerboard origin for that signal.

ParameterDescription
inputInput signal to be denoised.
outputResulting denoised signal.
checkerboardOriginThe X-coordinate of the first traced pixel at Y=0 in a checkerboard pattern.

In a checkerboard pattern, only half the pixels are traced per frame, alternating as follows:

     x=0  x=1  x=2  x=3
y=0:  0    1    0    1
y=1:  1    0    1    0
y=2:  0    1    0    1

0: pixel (0, 0) was traced.
1: pixel (1, 0) was traced.

checkerboardOrigin = frameIndex & 1

Must be zero-initialized if this signal is not set in checkerboardSignalFlags.
Only meaningful if this signal is set in checkerboardSignalFlags.

2.5.5. ffxDispatchDescDenoiserAmbientOcclusion

ParameterDescription
signalChannels:
R:
    input: Ambient occlusion (in [0,1] where 0 is fully occluded and 1 is fully exposed) to denoise
    output: Denoised ambient occlusion (in [0,1] where 0 is fully occluded and 1 is fully exposed) (or input if passthrough)

2.5.6. ffxDispatchDescDenoiserDirectDiffuse

ParameterDescription
signalChannels:
RGB:
    input: Direct diffuse radiance to denoise
    output: Denoised direct diffuse radiance (or input if passthrough)
A:
    input: Undefined
    output: Preserved (or input if passthrough)

2.5.7. ffxDispatchDescDenoiserDirectSpecular

ParameterDescription
signalChannels:
RGB:
    input: Direct specular radiance to denoise
    output: Denoised direct specular radiance (or input if passthrough)
A:
    input: Undefined
    output: Preserved (or input if passthrough)

2.5.8. ffxDispatchDescDenoiserDominantLight

ParameterDescription
signalChannels:
R:
    input: Dominant light visibility (ray hit distance [0,FP16_MAX] where FP16_MAX is fully exposed) to denoise
    output: Denoised dominant light visibility (in [0,1] where 0 is fully occluded and 1 is fully exposed) (or input ray hit distance converted to visibility if passthrough)
directionDominant light direction. (from light source to target)
emissionDominant light emission. (i.e. LightColor * LightIntensity)
angularRadiusDominant light angular radius, i.e. the half-angle of the cone the light subtends at the shaded point. (Expressed in radians)

Controls the width of the penumbra and must match the angular radius used when sampling the light to generate the noisy visibility signal.
// Example: sampling the dominant light within its angular cone to generate the noisy
// visibility signal. The same angularRadius is passed to ffxDispatchDescDenoiserDominantLight.
const float cosHalfAngle = cos(angularRadius);
const float3 sampleDir = UniformSampleCone(rngState, lightDirection, cosHalfAngle);
// Trace the hit-distance ray toward the sampled direction.
// The result is the noisy visibility encoded as ray hit distance in [0, FP16_MAX],
// where FP16_MAX denotes an unshadowed (fully exposed) sample.
Ray ray;
ray.origin = surfacePosition + surfaceNormal * rayEpsilon;
ray.direction = sampleDir;
const float hitDistance = TraceHitDistanceRay(rtas, ray);

2.5.9. ffxDispatchDescDenoiserIndirectDiffuse

ParameterDescription
signalChannels:
RGB:
    input: Indirect diffuse radiance to denoise
    output: Denoised indirect diffuse radiance (or input if passthrough)
A:
    input: Indirect diffuse ray hit distance
    output: Preserved (or input if passthrough)

2.5.10. ffxDispatchDescDenoiserIndirectSpecular

ParameterDescription
signalChannels:
RGB:
    input: Indirect specular radiance to denoise
    output: Denoised indirect specular radiance (or input if passthrough)
A:
    input: Indirect specular ray hit distance
    output: Preserved (or input if passthrough)

2.5.11. ffxDispatchDescDenoiserSpecularOcclusion

ParameterDescription
signalChannels:
R:
    input: Specular occlusion (in [0,1], where 0 is fully occluded and 1 is fully exposed) to denoise
    output: Denoised specular occlusion (in [0,1], where 0 is fully occluded and 1 is fully exposed) (or input if passthrough)

2.6. Configuring settings

To achieve the best results with AMD 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 by passing a ffxQueryDescDenoiserGetDefaultKeyValue to ffxQuery. This ensures that you start from a valid baseline.

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

// Example: querying the default value of FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS.
float stabilityBias;
ffx::QueryDescDenoiserGetDefaultKeyValue queryDesc = {};
queryDesc.key = FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS;
queryDesc.count = 1u;
queryDesc.data = &stabilityBias;
ffx::ReturnCode result = ffx::Query(m_pDenoiserContext, queryDesc);
assert(result == ffx::ReturnCode::Ok);

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, pass a ffxConfigureDescDenoiserKeyValue to ffxConfigure.

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

// Example: applying a custom value for FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS.
float stabilityBias = ...;
ffx::ConfigureDescDenoiserKeyValue configureDesc = {};
configureDesc.key = FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIAS;
configureDesc.count = 1u;
configureDesc.data = &stabilityBias;
ffx::ReturnCode result = ffx::Configure(m_pDenoiserContext, configureDesc);
assert(result == ffx::ReturnCode::Ok);

2.6.1. ffxConfigureDescDenoiserKeyValue

ParameterDescription
keyConfiguration key corresponding to an entry of FfxApiConfigureDenoiserKey.
countThe number of elements to configure.
dataPointer to an array with the configuration values for the elements to configure.

2.6.2. ffxQueryDescDenoiserGetDefaultKeyValue

ParameterDescription
keyConfiguration key corresponding to an entry of FfxApiConfigureDenoiserKey.
countThe number of elements to query.
dataPointer to an array for storing the queried elements.

2.6.3. FfxApiConfigureDenoiserKey

KeyDescription
FFX_API_CONFIGURE_DENOISER_KEY_CROSS_BILATERAL_NORMAL_STRENGTHOverride the strength of the cross bilateral normal term.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_STABILITY_BIASOverride the bias of the temporal accumulation to be more stable but less responsive.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_MAX_RADIANCEOverride the maximum radiance value.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_RADIANCE_CLIP_STD_KOverride the standard deviation K value used for radiance clipping.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_GAUSSIAN_KERNEL_RELAXATIONOverride the Gaussian kernel relaxation factor.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_DISOCCLUSION_THRESHOLDOverride the disocclusion threshold used for depth comparisons during temporal reprojection.

Format: A single float.
FFX_API_CONFIGURE_DENOISER_KEY_DEBUG_VIEW_LINEAR_DEPTH_BOUNDSOverride the absolute linear depth bounds used for normalizing the absolute linear depth debug view.

Format: A single FfxApiFloatBounds.

2.7. 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.

3. Performance

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

3.1. Timings

The tables below summarize the timings of AMD FSR™ Ray Regeneration under various operating conditions.

3.1.1. Denoising direct diffuse, direct specular, indirect diffuse and indirect specular signals

Target Render ResolutionAverage [ms]
960x5400.99
1920x10803.74
2560x14407.15

3.1.2. Denoising indirect diffuse and indirect specular signals

Target Render ResolutionAverage [ms]
960x5400.81
1920x10803.08
2560x14406.01

3.1.3. Denoising indirect specular signal

Target Render ResolutionAverage [ms]
960x5400.71
1920x10802.57
2560x14405.27

3.1.4. Denoising dominant light visibility signal

Target Render ResolutionAverage [ms]
960x5400.13
1920x10800.36
2560x14400.57

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

3.2. Memory requirements

AMD 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 AMD FSR™ Ray Regeneration under various operating conditions.

3.2.1. Denoising direct diffuse, direct specular, indirect diffuse and indirect specular signals

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x5401176255
1920x1080377178199
2560x1440634306328

3.2.2. Denoising indirect diffuse and indirect specular signals

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540994455
1920x1080313114199
2560x1440519191328

3.2.3. Denoising indirect specular signal

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x540903555
1920x108028283199
2560x1440462134328

3.2.4. Denoising ambient occlusion or dominant light visibility or specular occlusion signal

Render ResolutionTotal Working Set [MB]Persistent Working Set [MB]Aliasable Working Set [MB]
960x54032275
1920x1080725517
2560x14401188830

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

3.2.5. ffxQueryDescDenoiserGetGPUMemoryUsage

ParameterDescription
deviceA pointer to the device. For DX12: pointer to ID3D12Device.
maxRenderSizeMaximum size that rendering will be performed at.
signalFlagsSignal flags corresponding to a combination of values from FfxApiDenoiserSignalFlags.
checkerboardSignalFlagsSignal flags corresponding to a combination of values from FfxApiDenoiserSignalFlags for signals that use checkerboard reconstruction.

Must be a subset of signalFlags.
flagsCreate flags corresponding to zero or a combination of values from FfxApiCreateContextDenoiserFlags.
gpuMemoryUsageA pointer to a FfxApiEffectMemoryUsage structure that will hold the GPU memory usage.

The expected memory usage can be queried before context creation by passing a ffxQueryDescDenoiserGetGPUMemoryUsage to ffxQuery:

// Example: querying the expected GPU memory usage before context creation.
FfxApiEffectMemoryUsage memoryUsage = {};
ffx::QueryDescDenoiserGetGPUMemoryUsage queryDesc = {};
queryDesc.device = GetDevice()->GetImpl()->DX12Device();
queryDesc.maxRenderSize = denoiserContextDesc.maxRenderSize;
queryDesc.signalFlags = denoiserContextDesc.signalFlags;
queryDesc.checkerboardSignalFlags = denoiserContextDesc.checkerboardSignalFlags;
queryDesc.flags = denoiserContextDesc.flags;
queryDesc.gpuMemoryUsage = &memoryUsage;
ffx::ReturnCode result = ffx::Query(queryDesc, versionOverride);
assert(result == ffx::ReturnCode::Ok);
LOG(L"Denoiser GPU Memory Usage totalUsageInBytes %.3f MB aliasableUsageInBytes %.3f MB",
memoryUsage.totalUsageInBytes / 1048576.f,
memoryUsage.aliasableUsageInBytes / 1048576.f);

The actual memory usage can be queried after context creation by passing a ffxQueryDescDenoiserGetGPUMemoryUsage to ffxQuery:

// Example: querying the actual GPU memory usage after context creation.
FfxApiEffectMemoryUsage memoryUsage = {};
ffx::QueryDescDenoiserGetGPUMemoryUsage queryDesc = {};
queryDesc.device = GetDevice()->GetImpl()->DX12Device();
queryDesc.maxRenderSize = denoiserContextDesc.maxRenderSize;
queryDesc.signalFlags = denoiserContextDesc.signalFlags;
queryDesc.checkerboardSignalFlags = denoiserContextDesc.checkerboardSignalFlags;
queryDesc.flags = denoiserContextDesc.flags;
queryDesc.gpuMemoryUsage = &memoryUsage;
ffx::ReturnCode result = ffx::Query(m_pDenoiserContext, queryDesc);
assert(result == ffx::ReturnCode::Ok);
LOG(L"Denoiser GPU Memory Usage totalUsageInBytes %.3f MB aliasableUsageInBytes %.3f MB",
memoryUsage.totalUsageInBytes / 1048576.f,
memoryUsage.aliasableUsageInBytes / 1048576.f);

4. Debugging

4.1. Debug view

When the AMD FSR™ Ray Regeneration context is created with FFX_DENOISER_ENABLE_DEBUGGING, integrations can pass an optional ffxDispatchDescDenoiserDebugView dispatch description to ffxDispatch to output relevant debug information into an app-provided render target.

AMD FSR™ Ray Regeneration debug view in overview mode

Figure 3: AMD 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. signed linear depth delta), the Z-component of the motionVectors input.
Blue for negative deltas, Red for positive deltas.
Linear DepthAbsolute value of the linearDepth input.
Clamped to the bounds corresponding to FFX_API_CONFIGURE_DENOISER_KEY_DEBUG_VIEW_LINEAR_DEPTH_BOUNDS.
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.
UnusedUnused viewport, displayed as a black texture.
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.

Passing a ffxDispatchDescDenoiserDebugView dispatch description to ffxDispatch 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. AMD 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 contain 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.

// Example: populating the debug view dispatch description.
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;

4.1.1. ffxDispatchDescDenoiserDebugView

ParameterDescription
outputThe target output resource for debug visualization.

Channels:
RGB: Color information to display.
A: Indicates which pixels have been written to.
outputSizeThe resolution of the debug visualization.
modeThe debug view mode corresponding to an entry of FfxApiDenoiserDebugViewMode.
viewportIndexThe index of the viewport to visualize when FFX_API_DENOISER_DEBUG_VIEW_MODE_FULLSCREEN_VIEWPORT mode is active.
Clamped between 0 and FFX_API_DENOISER_DEBUG_VIEW_MAX_VIEWPORTS - 1.

5. Best practices

5.1. Generating noisy signals

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

5.1.1. Radiance inputs and albedo modulation

AMD FSR™ Ray Regeneration expects the first-bounce albedo modulation to be absent from the radiance signals. The first-bounce albedo is instead passed separately via the diffuseAlbedo and specularAlbedo members of ffxDispatchDescDenoiser. After denoising, the application is responsible for multiplying the denoised output by the first-bounce albedo before compositing.

The expected albedo modulation state per signal is:

SignalFirst-bounce albedoSubsequent-bounce albedo
Direct diffuseNot applied — passed separately via diffuseAlbedoN/A
Direct specularNot applied — passed separately via specularAlbedoN/A
Indirect diffuseNot applied — passed separately via diffuseAlbedoApplied
Indirect specularNot applied — passed separately via specularAlbedoApplied

In practice this means your ray tracer should output the first-bounce lighting contribution directly, without multiplying by the surface albedo. Subsequent bounces accumulate full radiance (albedo included) as usual. After the denoiser dispatch, multiply the denoised output by the albedo to produce the final composited result.

// Example: re-modulating the denoised output in the composition pass.
const float3 diffuseAlbedo = Square(g_DiffuseAlbedo[pixel]); // decode sqrt-encoded albedo
const float3 specularAlbedo = Square(g_SpecularAlbedo[pixel]);
const float3 diffuse = (directDiffuse + indirectDiffuse) * diffuseAlbedo;
const float3 specular = (directSpecular + indirectSpecular) * specularAlbedo;
g_Output[pixel] = float4(diffuse + specular, 0.0f);

5.1.2. 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 AMD 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 AMD FSR™ Ray Regeneration Denoised output from AMD FSR™ Ray Regeneration

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

5.1.3. 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 AMD 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 AMD 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.

5.1.4. 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 AMD 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.

5.1.5. Tweakable denoiser settings

Even with high-quality inputs and the recommended best practices applied, some artifacts may still remain. AMD 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 the full list of available keys and guidance on retrieving and applying these settings, see 2.6. Configuring settings.

5.2. Providing motion vectors

5.2.1. Space

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

AMD 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 signed linear view-space z delta.

2D motion vector

Figure 5: 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 AMD 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;
// Assuming camera looks along the +Z axis in view space: pView.z = pProjection.w
// Signed linear depth delta
motionVector.z = previousPosition.w - currentPosition.w;
// CPU: Matching AMD 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;

5.2.2. Coverage

AMD 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.

5.3. 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 AMD FSR™ Ray Regeneration are required to be stored using octahedral encoding.

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

5.3.1. Octahedral encoding of normals:

// Example: encoding a unit normal vector into a 2D octahedral UV.
float2 NormalToOctahedronUv(float3 N)
{
N.xy /= abs(N.x) + abs(N.y) + abs(N.z);
float2 k = sign(N.xy);
if (N.z < 0.0)
{
N.xy = (1.0 - abs(N.yx)) * k;
}
return N.xy * 0.5 + 0.5;
}

5.3.2. Octahedral decoding of normals:

// Example: decoding a 2D octahedral UV back into a unit normal vector.
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);
}

5.4. 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. AMD 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.

AMD FSR™ Ray Regeneration supports four material type values (0–3). Material type 0 is reserved as the default material, leaving three additional values available for custom use. Upon encoding the material type into the normals member of the ffxDispatchDescDenoiser dispatch description, the stored value should be a normalized fraction relative to the maximum number of supported materials.

// Example: encoding the material type into the alpha channel of the normals resource.
// ... ray tracing pass ...
uint materialType = hit.materialType;
float materialTypeFrac = materialType / 3.0;
outNormalsResource[pixel] = float4(normalOctahedralUv, roughness, materialTypeFrac);

5.5. 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.

5.5.1. 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 6: 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:

// Example: generating specular albedo from a pre-baked GGX BRDF look-up table.
// ... 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;
// ...

5.5.2. Example: BRDF Approximation

The following HLSL code snippets illustrate how to approximate the BRDF contribution 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);
// ...

6. Requirements

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

7. Version history

VersionDate
1.2.12026-10-28
1.2.02026-06-24
1.1.02026-03-18
1.0.02025-12-10

For more details, refer to the changelog.

8. See also