PostProcessWorks
This page covers all rendering passes provided by uexPostProcessWorks: SMAA 1x, SMAA S2x, the Filmic Temporal Filter, Sharpen, Screen Space Indirect Lighting (SSIL), and Screen-Space Shadows (Bend Studio).
All passes run entirely on the compute pipeline with no rasterisation dependencies. Every pass is viewport-aware (ViewOffset + ViewSize) for correct split-screen and VR support. None of them require temporal jitter — the image is stable at native resolution from frame one.
SMAA 1x
SMAA 1x only works when r.AntiAliasingMethod=0 (no engine AA).
Overview
A fully compute-shader implementation of SMAA 1x for Unreal Engine 5, adapted from Jorge Jimenez's original SMAA algorithm. The pass runs three stages — edge detection, blending weight calculation, and neighbourhood blending — entirely on the compute pipeline. An optional fourth stage chains into the Filmic Temporal Filter when velocity and depth buffers are available.
Edge detection supports both colour-based (default, higher quality) and luma-based modes, with an HDR tonemap pre-pass so bright specular highlights don't produce false edges. A depth-predication system modulates the detection threshold per-pixel: lowering it at geometry silhouettes to catch subtle edges, and raising it on smooth surfaces to suppress texture noise. Detected edge pixels are written to an append buffer and dispatched indirectly into the blend weight pass, so only pixels that actually contain edges pay the cost of the full search and area-texture lookups.
The blend weight pass performs horizontal and vertical pattern matching using the standard SMAA area and search textures, with configurable search distance, diagonal detection, and corner rounding. Neighbourhood blending then reconstructs the final anti-aliased colour using the computed weights.
Console Variables
rEx.SMAA.Mode (int, default: 1)
Anti-aliasing / temporal filtering mode selector.
0— Disabled. Scene colour passes through unmodified.1— SMAA 1x. Spatial anti-aliasing only.2— SMAA 1x + Filmic Temporal Filter. SMAA runs first, then temporal filtering blends against reprojected history.3— SMAA S2x. Forward-rendering path for MSAA 2x; extracts both MSAA samples, applies SMAA per sample, then averages the result.
rEx.SMAA.MaxSearchSteps (float, default: 32, range: 4–112)
How far along an edge the algorithm searches to determine its shape. Longer searches improve AA on long straight edges but increase GPU cost. The search texture is 64 entries wide so values above 112 give no additional benefit. Presets: 4 (low), 8 (medium), 16 (high), 32 (ultra).
rEx.SMAA.MaxDiagSearchSteps (float, default: 16, range: 0–20)
Same as above but for diagonal edges. Ignored when DisableDiag is enabled. The diagonal area texture covers distances up to 20. Presets: 0 (off), 8 (medium), 16 (high), 20 (ultra).
rEx.SMAA.DisableDiag (int, default: 0)
Skips diagonal edge processing. Saves some ALU in the blend weight pass at the cost of worse AA on ~45° edges. 0 = diagonals enabled, 1 = disabled.
rEx.SMAA.CornerRounding (float, default: 0.25, range: 0.0–1.0)
Controls how sharp or rounded corners appear where two edges meet at a right angle. At 0.0, corners are fully sharp. At 1.0, corner detection is effectively disabled. The original SMAA default is 25%. Sweet spot: 0.2–0.3.
rEx.SMAA.Predication (int, default: 1)
Enables depth-based threshold modulation. Where depth discontinuities exist, the colour threshold is lowered to catch subtle edges. Where depth is smooth, the threshold is raised to suppress false positives. Requires a valid depth buffer. 0 = off, 1 = on.
rEx.SMAA.PredicationThreshold (float, default: 0.01, range: 0.001–0.1)
Minimum depth delta to count as a depth edge. If using SceneDepthTexture (hardware Z, 0–1 NDC), 0.01 is a reasonable start. Too low = everything is a depth edge. Too high = no depth edges detected. Tune visually — enable debug mode 1 and compare edge maps.
rEx.SMAA.PredicationScale (float, default: 2.0, range: 1.0–5.0)
Multiplier applied to the base threshold when predication is active. Raises the threshold globally, making SMAA less sensitive overall, with the local strength carving out exceptions at depth edges. Higher values reduce false positives on textures but risk missing edges without depth discontinuities. Start at 2.0.
rEx.SMAA.PredicationStrength (float, default: 0.4, range: 0.0–1.0)
Controls how strongly the local threshold is reduced at depth edges when predication is enabled. Higher values catch more subtle geometry edges but can increase false positives.
rEx.SMAA.HDREdgeDetection (int, default: 1)
Controls edge detection input. 0 = compare raw HDR colour deltas, 1 = tonemap before edge detection. Leave enabled unless you specifically want raw HDR edge sensitivity.
SMAA S2x
SMAA S2x only works in the forward renderer with MSAA 2x enabled. Use rEx.SMAA.Mode=3.
Overview
SMAA S2x combines MSAA 2x sample coverage with SMAA edge filtering. The pass extracts both MSAA samples, runs the SMAA pipeline per sample using S2x subsample indices, then averages the filtered samples back into the final scene colour.
This mode is intended for forward-rendered projects where MSAA is already available. It costs more than SMAA 1x because the SMAA work is effectively performed for two samples, but it handles subpixel and geometry edges better than pure post-process SMAA 1x.
Console Variables
rEx.SMAA.S2xSwapSamples (int, default: 1)
Controls the MSAA sample order used by SMAA S2x. 0 = default MSAA sample order, 1 = swapped sample order. The swapped order is the recommended default because it generally produces better S2x reconstruction.
rEx.SMAA.S2xDebug is available outside shipping builds. 0 = disabled, 1 = sample 0, 2 = sample 1, 3 = side-by-side, 4 = amplified difference.
Filmic Temporal Filter
Overview
The Filmic Temporal Filter is a compute-shader temporal stabilisation pass designed to work as a companion to SMAA 1x. Inspired by Jorge Jimenez's Filmic SMAA research at Activision, it blends the current frame with reprojected history using the engine's velocity buffer to reduce aliasing and temporal instability — without the per-pixel jitter that traditional TAA solutions require.
Key features:
- Adaptive history sampling — Catmull-Rom bicubic filtering for static or slow-moving pixels, bilinear for fast motion, with an optional SMAA-edge-aware path that skips the expensive filter where no edges are detected.
- FPS-aware temporal blending — Blend weights adjust automatically to frame time so the filter behaves consistently whether the game runs at 30 fps or 120 fps.
- Depth-based disocclusion handling — A 5-tap cross-pattern depth search recovers valid history for newly revealed pixels, reducing ghosting around moving objects.
- YCoCg variance clamping — Neighbourhood statistics in YCoCg colour space keep the blended result within a plausible range, preventing colour bleeding and lag on high-contrast edges.
The filter is enabled by setting rEx.SMAA.Mode to 2. There is no separate enable CVar — it activates automatically when mode 2 is selected and the required velocity and depth buffers are available.
Console Variables
rEx.FilmicFilter.BlendFactor (float, default: 0.1, range: 0.05–0.5)
Base temporal blend weight between the current frame and reprojected history. The actual per-pixel weight is computed adaptively from luminance contrast and frame time, but will never fall below this floor. Lower = stronger temporal smoothing but more ghosting on fast motion. Higher = more responsive but less filtering. Raise toward 0.2–0.3 if you notice trailing on fast-moving objects.
rEx.FilmicFilter.Sharpness (float, default: 75.0, range: 0–100)
Catmull-Rom bicubic tension for history sampling on static/slow pixels. Mapped internally to 0–1 (0 = very soft, 100 = maximum sharpness). At 75, the history preserves fine detail without ringing. Values above 90 may introduce slight haloing on high-contrast edges. Has no effect on fast-moving pixels (velocity > 2 px/frame), which always fall back to bilinear.
rEx.FilmicFilter.DepthSearchEnable (bool, default: 1)
Enables a 5-tap cross-pattern depth search around the reprojected UV when the initial depth test flags a disocclusion. Disabling saves a handful of texture fetches per disoccluded pixel but results in more ghosting along object silhouettes. In most cases the cost is negligible and the quality gain is worthwhile.
rEx.FilmicFilter.ConvergenceStrength (float, default: 0.82, range: 0.0–2.0)
Filmic sharpening strength applied during temporal convergence. At 0.0 convergence sharpening is disabled. At 1.0 the paper-default strength is used. Lower values significantly reduce ringing and haloes on high-contrast edges.
rEx.FilmicFilter.ClipSigma (float, default: 1.60, range: 0.5–3.0)
Neighbourhood clamping strength in YCoCg colour space. Lower = softer results with less ringing. Higher = more sharpness but potential haloes on high-contrast edges.
rEx.FilmicFilter.VelocityThreshold (float, default: 0.45, range: 0.0+)
Minimum velocity in pixels before the filmic convergence sharpening activates. Pixels slower than this skip the convergence pass entirely. Higher values reduce ringing artefacts on slow camera movement at the cost of less temporal sharpening on near-static content.
Sharpen — RCAS & NVIDIA Image Scaling
Overview
The sharpen pass provides two selectable sharpening algorithms — AMD FidelityFX Robust Contrast Adaptive Sharpening (RCAS) and NVIDIA Image Scaling (NIS) — exposed through a single unified entry point. It runs as a compute-shader post-process after anti-aliasing.
RCAS (Preset 0) is a lightweight 5-tap cross-pattern sharpener derived from AMD's FidelityFX CAS. It computes a variance-based sharpening kernel per pixel, adaptively limiting the enhancement where the local neighbourhood already has high contrast. An optional high-pass denoise filter detects and suppresses sharpening on film grain and sensor noise, preventing grain from becoming distractingly crunchy. RCAS operates in HDR and is the default preset.
NIS Sharpen (Preset 1) uses NVIDIA's directional sharpening algorithm from the NVIDIA Image Scaling SDK. Rather than a simple isotropic kernel, NIS analyses local edge direction and applies sharpening along the edge while limiting enhancement across it, which preserves thin lines and text more cleanly than symmetric filters. The shader runs in 32×32 tile blocks with 256 threads per group.
NIS Scaler (Preset 2) is the full NVIDIA Image Scaling upscaler combined with directional sharpening in a single pass. It renders at a reduced internal resolution and reconstructs the output at full resolution using 6-tap polyphase filtering with pre-computed coefficient textures, then applies the same directional sharpen on the upscaled result.
The NIS Upscaler (Preset 2) may not work in-editor due to editor having its own Scene Percentage View setting which can be changed from the editor viewport dropdown.
Console Variables
Shared
rEx.Sharpen.Enable (int, default: 1)
Toggles the sharpen pass on/off. 0 = disabled, 1 = enabled.
rEx.Sharpen.Intensity (float, default: 0.5, range: 0.0–1.0)
Sharpen strength. At 0.0 the image is effectively unsharpened; at 1.0 maximum sharpening is applied. For RCAS this is mapped through an exponential curve so the perceptual response is roughly linear. For NIS presets this feeds directly into the NVIDIA config as the sharpness slider. Start at 0.5; raise toward 0.7–0.8 if the image looks soft after temporal AA, lower toward 0.3 if you see haloing on high-contrast edges.
rEx.Sharpen.Preset (int, default: 0)
Selects the sharpening algorithm. 0 = RCAS (AMD FidelityFX, 5-tap variance-based, HDR-friendly), 1 = NIS Sharpen (NVIDIA directional sharpen only, no upscale), 2 = NIS Scaler (NVIDIA upscale + directional sharpen). RCAS is the cheapest option and a good default. If you are already rendering at 100% screen percentage, Preset 1 or 0 is more appropriate.
RCAS-Specific
rEx.RCas.DenoiseEnable (int, default: 1)
Enables a high-pass noise detection filter inside RCAS. When active, the shader identifies pixels where local variance is dominated by high-frequency noise (film grain, dithering) and limits sharpening on those pixels to prevent grain amplification. 0 = disabled (maximum sharpening on all frequencies), 1 = enabled (recommended). Disable only if your content has no film grain and you want the absolute strongest sharpening response.
NIS Scaler-Specific
rEx.NIS.UpscalerQuality (int, default: 1)
Quality preset for NIS Scaler mode (Preset 2). Controls the internal render resolution as a percentage of output. 0 = Performance (70%), 1 = Balanced (80%), 2 = Quality (90%). Has no effect when using Preset 0 or 1.
Screen Space Indirect Lighting (SSIL)
Overview
The SSIL pass approximates indirect bounce lighting using screen-space data. It samples the scene colour buffer around each pixel in view-space hemispheres oriented along the surface normal, estimating how much light nearby visible surfaces contribute as indirect illumination. The effect adds subtle colour bleeding and ambient fill that reacts to scene content in real time without requiring baked lightmaps or ray tracing hardware.
The pipeline runs in five compute-shader stages:
- Main IL sampling at half resolution. For each pixel, the shader reconstructs the view-space position and normal from depth and GBuffer A, then takes a configurable number of hemisphere samples to gather indirect colour from the scene colour buffer. Sample count is controlled by the quality preset (1, 2, 4, or 8 samples).
- Downsample from half to quarter resolution for the denoising pass.
- Two-pass Gaussian blur at quarter resolution with configurable spread. The low resolution makes the blur very cheap while covering a large spatial footprint.
- Bilateral upsample in two steps (quarter → half → full). The upsample is depth-aware and normal-aware via GBuffer A, preserving sharp boundaries at geometry edges while smoothly interpolating the IL signal across surfaces.
- Temporal accumulation and composite. The denoised IL is blended with reprojected history using velocity-based rejection and depth validation, then added to the scene colour. The temporal pass stabilises the noisy per-frame estimate into a clean, stable result.
Console Variables
rEx.SSIL.Enable (bool, default: 0)
Enables Screen Space Indirect Lighting. When disabled, the pass is skipped entirely with no GPU cost.
rEx.SSIL.Radius (float, default: 200.0, range: 0.1+)
View-space hemisphere sample radius in centimetres. Larger radius = wider bounce light spread but sparser samples.
rEx.SSIL.Intensity (float, default: 5.0, range: 0.0+)
Final IL multiplier applied to the indirect lighting contribution before compositing into scene colour.
rEx.SSIL.BlurSpread (float, default: 0.5, range: 0.25–1.0)
Gaussian blur spread at quarter resolution. Lower values preserve more high-frequency detail in the IL signal; higher values produce smoother, more diffuse results.
rEx.SSIL.TemporalWeight (float, default: 0.90, range: 0.5–0.97)
Temporal blend weight controlling how much history is retained each frame. Higher = more temporal stability but slower response to lighting changes. Lower = faster response but noisier.
rEx.SSIL.Quality (int, default: 2)
Quality preset controlling the number of hemisphere samples per pixel at half resolution. 1 = Fast (1 sample), 2 = Medium (2 samples), 3 = High (4 samples), 4 = Ultra (8 samples).
rEx.SSIL.TraceMethod (int, default: 0)
Selects the SSIL ray hit method. 0 = fixed-step marcher, 1 = Newton tangent-plane refinement with marcher fallback. Method 1 can improve hit refinement but should be validated per scene because it changes the tracing path.
rEx.SSIL.DebugMode (int, default: 0)
Debug visualisation. 0 = normal composite, 1 = raw IL amplified, 2 = velocity / motion vectors, 3 = quarter-res downsample, 4 = quarter-res blur output, 5 = half-res bilateral upsample, 6 = full-res denoised pre-temporal, 7 = post-temporal IL only.
Screen-Space Shadows (Bend Studio)
Overview
A compute-shader port of Bend Studio's screen-space shadow technique (Apache 2.0), adapted for the UE5 RDG pipeline. The pass projects each light's direction into screen space, then ray-marches through the depth buffer to determine whether each pixel is occluded by nearby geometry. The result is a per-pixel shadow mask composited into the scene colour as a multiply, producing fine contact shadows that are difficult or impossible to achieve with traditional shadow maps alone — thin object self-shadowing, small crevice darkening, and sharp contact lines at object intersections.
This is a post-process effect, not integrated into the engine's shadow rendering pipeline. Shadows are applied after lighting has been computed, meaning they darken the final image rather than attenuating individual light contributions during the lighting pass. The effect cannot distinguish which light each shadow should block and does not interact with the engine's shadow cascades, distance field shadows, or ray-traced shadows.
Because Bend SSS is applied as a post-process multiply, it cannot attenuate individual lights correctly, cannot participate in shadow-map composition, and can over-darken scenes where multiple lights contribute to the same pixel. It is also limited to visible screen-space depth, so off-screen occluders, hidden backfaces, and geometry outside the depth buffer cannot cast shadows.
Future improvements are expected.
The pass uses Bend Studio's quadrant-based dispatch builder to efficiently tile GPU work based on the light's screen-space position. An early-out optimisation skips pixels outside the active depth range (e.g. sky), which can be 2–3× faster in best-case scenarios.
Multi-light support is available: when enabled, the pass iterates over up to 4 lights provided by the Shader Works subsystem, generates a per-light shadow mask, and min-merges them into a single accumulator. The Shader Works subsystem automatically detects directional, point, and spot lights in the scene and sorts them by intensity, so the most significant lights are always shadowed first.
Excluding Objects From Bend SSS
Bend SSS exclusion uses Custom Stencil. Any object that writes the configured stencil bit can be skipped by the Bend SSS pass.
If Custom Depth-Stencil is disabled, the Bend SSS shader has no stencil data to read and exclusion will not work.
Steps:
- Open Project Settings.
- Go to Rendering -> Postprocessing.
- Set Custom Depth-Stencil Pass to Enabled with Stencil.
- Select the actor or mesh component in the level.
- Open the Details panel.
- Expand Rendering.
- Enable Render CustomDepth Pass.
- Set CustomDepth Stencil Value (1).
Console Variables
rEx.SSS.Enable (int, default: 1)
Enables Bend Studio screen-space shadows. 0 = disabled, 1 = enabled.
rEx.SSS.SurfaceThickness (float, default: 0.05, range: 0.001–0.1)
Assumed thickness of each pixel for shadow casting, as a fraction of the depth range. Controls how "solid" surfaces appear to the ray marcher. Too low and thin objects won't cast shadows; too high and shadows become overly thick with haloing. Start at 0.05 and scale up/down in multiples of 2.
rEx.SSS.BilinearThreshold (float, default: 0.02, range: 0.005–0.1)
Edge detection threshold for bilinear filtering during the ray march. Use rEx.SSS.DebugMode 1 to visualise detected edges.
rEx.SSS.ShadowContrast (float, default: 4.0, range: 1.0–8.0)
Contrast boost applied to the shadow transition. Higher values produce sharper, more defined shadow edges. Must be ≥ 1.
rEx.SSS.ShadowStrength (float, default: 0.5, range: 0.0–1.0)
How dark the screen-space shadows are. 0 = no visible effect, 1 = full black shadows.
rEx.SSS.SampleCount (int, default: 60, range: 16–120)
Number of shadow samples per pixel. Controls effective shadow length — more samples = longer shadow reach but higher GPU cost.
rEx.SSS.FadeRatio (float, default: 0.6, range: 0.0–0.95)
Fraction of MaxDistance at which the shadow begins to fade out. At 0.0 the shadow fades over its entire length; at 0.95 it stays nearly full strength until the very end.
rEx.SSS.MaxDistance (float, default: 5000.0, range: 500–50000)
World-space distance at which screen-space shadows fully fade out.
rEx.SSS.MinDepthDelta (float, default: 0.05, range: 0.0–0.5)
Minimum depth difference required to cast a shadow. Filters out micro-crevice shadows from surface detail and noise. Higher values restrict shadows to only large geometry depth discontinuities.
rEx.SSS.IgnoreEdgePixels (int, default: 0)
When enabled, detected edge pixels do not cast shadows. Helps reduce artefacts at grazing angles on flat surfaces, but may thin out foliage shadows. 0 = disabled, 1 = enabled.
rEx.SSS.UseEarlyOut (int, default: 1)
Enables early-out for pixels outside the active depth bounds (e.g. sky). Adds ~15% overhead in worst case but can be 2–3× faster in typical scenes with significant sky coverage.
rEx.SSS.MultiLight (int, default: 0)
Multi-light shadow mode. 0 = best single light only (cheapest). 1 = all significant lights up to 4 (higher cost). Light data is provided by the Shader Works subsystem.
rEx.SSS.DebugMode (int, default: 0)
Debug visualisation. 0 = normal operation, 1 = edge mask, 2 = thread index, 3 = wave index, 4 = shadow mask only (grayscale).
Comparison with UE5's Built-in Contact Shadows
UE5 ships two contact shadow implementations: a stochastic jittering path (the default) and its own port of Bend Studio's technique added more recently (enabled via r.ContactShadows.Standalone.Method=1). The uexPostProcessWorks adaptation differs from both in several meaningful ways.
Architecture
The most fundamental difference is where the shadow is applied. UE5's contact shadow passes run inside the lighting pipeline — they are dispatched once per light during shadow projection and attenuate that light's contribution individually. The uexPostProcessWorks pass is a post-process effect that runs after all lighting is computed and multiplies the final scene colour uniformly. This means the uex pass is physically incorrect: it cannot distinguish which light each shadow should block, so all shadows darken the image regardless of light contribution weight. This is a genuine limitation.
The practical upside of the post-process position is that it works with custom shading models written via ShaderWorks Scene.AllLightParams. Engine contact shadows have no visibility into custom lighting — they are wired into the deferred lighting pass only.
Shadow Mask Quality
UE5's Bend path outputs a raw shadow mask with no post-generation filtering. Bend Studio's technique produces characteristic striation patterns (banding along the ray-march direction) that are visible in flat-lit areas at lower sample counts. The uexPostProcessWorks composite shader applies a 3×3 depth-aware bilateral blur to the shadow mask before compositing. The bilateral weights reject samples across depth discontinuities so geometry edges are preserved, while the blur smooths out striation artefacts on continuous surfaces. UE5's built-in path has no equivalent filtering step.
Distance Fade
UE5's depth-based intensity mode fades shadow intensity linearly with depth. The uex composite uses a smoothstep S-curve for the distance fade, which produces a more perceptually gradual rolloff — shadows don't abruptly thin at the fade boundary.
Multi-light
UE5's contact shadow system gets multi-light "for free" because it is called once per light by the renderer. The uex pass replicates this at the post-process level by dispatching the ray-march once per light (up to 4, provided by Shader Works), then min-merging the per-light masks into a single accumulator in a dedicated compute pass. The result is equivalent visually, though at higher cost than the engine's approach for scenes with many lights.
Noise and Micro-surface Filtering
The uex adaptation adds MinDepthDelta — a minimum depth difference required before a sample casts a shadow. This filters out micro-crevice darkening from surface normal noise and compressed textures, which the UE5 implementation does not expose. On high-detail meshes at close range this prevents a speckled shadow layer across smooth surfaces.
SM5 / DX11 Compatibility
UE5's Bend integration (FScreenSpaceShadowsBendCS) is compiled with CFLAG_ForceDXC, which requires DXC and DX12/Vulkan. The uex adaptation explicitly targets ERHIFeatureLevel::SM5 and avoids SM6 wave intrinsics in the early-out path (replaced with a groupshared LDS fallback), making it usable on SM5 / DX11 hardware.
Summary
| Feature | UE5 Built-in (Stochastic) | UE5 Built-in (Bend) | uexPostProcessWorks |
|---|---|---|---|
| Resolution | Half-res + upsample | Full-res | Full-res |
| Bilateral blur on mask | No | No | Yes (3×3 depth-aware) |
| Physics correctness | Per-light attenuation ✓ | Per-light attenuation ✓ | Post-process multiply ✗ |
| Custom shading model support | No | No | Yes (via ShaderWorks) |
| Multi-light | Implicit (called per light) | Implicit (called per light) | Explicit min-merge (up to 4) |
| Distance fade | Linear | Per-intensity mode | Smoothstep S-curve |
| MinDepthDelta filtering | No | No | Yes |
| SM5 / DX11 support | Yes | No (DXC forced) | Yes |
| Per-object stencil exclusion | No | No | Yes |