Quick answer: Lumen GI shows noise on fast-moving objects because temporal accumulation resets each frame, surface cache is invalidated by motion, screen traces fall back to lower-quality world-space methods, and the distant scene cache misses on the new pixel positions. Increase r.Lumen.ScreenProbeGather.TemporalFilterProbes, raise screen trace MaxRoughnessToTrace, enable surface cache compression, set MovableMeshCardCaptureMargin, and force LumenSceneLightingUpdateSpeed=1.

Here is how to fix Lumen GI noise on fast-moving objects. Static geometry in your scene looks beautifully lit by Lumen — soft bounces, accurate occlusion, no banding. The moment something moves quickly through that same lighting environment, the indirect lighting on its surface starts to crawl with grainy noise that looks like a poorly denoised path tracer. Slow down and the noise vanishes within a few frames; speed up and it reappears. Lumen’s denoising is heavily temporal, and temporal denoisers depend on samples that hold their position long enough to accumulate.

The Symptom

The character, vehicle, or projectile in motion shows visible grain or sparkle in its indirect lighting that other objects in the same area do not. Common variants:

Sparkle on rotating surfaces. A spinning fan blade or rotating turret shows speckled noise in shadow areas where the indirect light should be smooth. The noise pattern shifts every frame, producing a shimmering effect.

Trailing noise. A vehicle moving down a road leaves a band of noise that lingers behind it for a few frames before resolving. The leading edge of the vehicle is also noisier than its trailing edge because the leading pixels are entirely new history.

Disocclusion artifacts. When a moving object reveals geometry behind it, that newly visible geometry pops with bright noise for several frames before stabilizing. The frames during stabilization look much worse than the steady-state image.

What Causes This

Temporal accumulation reset. Lumen’s screen probe gather pass uses last-frame data reprojected into the current frame to amortize sampling cost. When an object moves more than the reprojection tolerance per frame, the system rejects the history and starts from one frame of samples. One frame of probe samples is not enough to denoise indirect light, so noise is what you see.

Surface cache invalidation. Lumen builds a surface cache of mesh cards that store color, normal, and emissive data. For movable meshes, the cards must be re-captured periodically as the mesh moves. If the recapture interval is long compared to the motion, Lumen evaluates lighting against stale or missing cards and falls back to lower-quality, noisier methods.

Screen traces too short. Screen-space ray traces are the cleanest source of indirect lighting because they hit the same pixels you see. When the trace runs out of iterations or hits a roughness threshold and falls back to world-space tracing, the result is much noisier per-sample. Fast-moving objects often have high screen-space coverage and the screen traces hit the iteration cap before resolving.

Distant scene cache miss. The Lumen scene representation has a distant tier for far geometry. Fast camera or object motion changes which distant cells are relevant, and uncached cells return placeholder data until they populate. The placeholder data is noisy.

The Fix

Step 1: Strengthen temporal probe filtering. The single biggest lever is the screen probe gather’s temporal filter. Enabling it and raising its strength blends more frames of probe samples together. The cost is a small amount of ghosting on contrast edges, which is usually invisible compared to the noise reduction.

// Apply Lumen tuning at runtime via console variables
void ULumenTunerSubsystem::ApplyHighQualityProfile()
{
    auto CVarSet = [](const TCHAR* Name, float Value)
    {
        IConsoleVariable* CVar =
            IConsoleManager::Get().FindConsoleVariable(Name);
        if (CVar) {
            CVar->Set(Value, ECVF_SetByGameSetting);
        }
    };

    // Enable and strengthen temporal probe filtering
    CVarSet(TEXT("r.Lumen.ScreenProbeGather.TemporalFilterProbes"),
        1.0f);
    CVarSet(TEXT("r.Lumen.ScreenProbeGather.TemporalMaxFramesAccumulated"),
        12.0f);

    // Extend screen tracing range and roughness budget
    CVarSet(TEXT("r.Lumen.ScreenTracing.MaxIterations"),
        128.0f);
    CVarSet(TEXT("r.Lumen.ScreenProbeGather.MaxRoughnessToTrace"),
        0.6f);

    // Movable-mesh surface cache margin and update speed
    CVarSet(TEXT("r.LumenScene.SurfaceCache.MovableMeshCardCaptureMargin"),
        200.0f);
    CVarSet(TEXT("r.LumenScene.Lighting.UpdateSpeed"),
        1.0f);
}

Step 2: Extend screen traces. Raise r.Lumen.ScreenTracing.MaxIterations from the default to allow longer screen-space trace walks before falling back to world-space methods. Bump r.Lumen.ScreenProbeGather.MaxRoughnessToTrace closer to 0.6 so glossy surfaces still receive screen-traced reflections instead of the much noisier world-space alternative.

Step 3: Configure surface cache for movable meshes. r.LumenScene.SurfaceCache.MovableMeshCardCaptureMargin controls how aggressively cards are re-captured for movable meshes. A larger margin captures cards covering more of the mesh’s motion path, reducing recapture cost and the resulting noise during fast motion. Pair this with r.LumenScene.Lighting.UpdateSpeed = 1 so the scene’s lighting solves at full speed instead of an interleaved pattern.

Step 4: Enable surface cache compression. Lumen surface cache compression (r.LumenScene.SurfaceCache.CompressCardData) reduces memory pressure, which lets you increase the cache resolution. Higher resolution means lower per-sample noise and faster convergence on movable meshes.

// Force a Lumen invalidation for one specific component
void ALevelStreamingTrigger::ForceLumenInvalidate(
    UPrimitiveComponent* Comp)
{
    if (!Comp) {
        return;
    }

    Comp->MarkRenderStateDirty();

    // Touch the bounds so Lumen re-evaluates surface cards
    Comp->UpdateBounds();
    Comp->MarkRenderTransformDirty();

    if (UWorld* World = Comp->GetWorld()) {
        if (FSceneInterface* Scene = World->Scene) {
            Scene->UpdatePrimitiveTransform(Comp);
        }
    }
}

Why This Works

Lumen is a temporal-amortized GI solution: it cannot afford to compute a clean image in a single frame, so it spreads the cost across many frames. The denoiser’s job is to reuse last frame’s samples wherever they remain valid. Strengthening the temporal filter widens the acceptance window for previous samples, recovering history that motion would otherwise have invalidated.

The surface cache and screen trace tuning attack the problem from a different angle: they make each individual sample better. A clean sample needs less temporal averaging to look smooth. Increasing screen trace iterations and roughness limits keeps more samples on the high-quality path, so even with reduced history they look good.

For movable meshes specifically, the card capture margin and update speed ensure Lumen has fresh surface data to evaluate against. Stale cards produce stale lighting, and as the mesh moves, the difference between current geometry and cached cards manifests as noise. Forcing more frequent updates trades a small amount of GPU cost for visibly cleaner GI on motion.

“Temporal denoisers reward stillness and punish motion. The fix is never ‘turn off temporal’ — it is ‘give the denoiser more confidence to keep history through motion’ and ‘make every fresh sample worth keeping.’”

Related Issues

If Lumen is noisy everywhere instead of only on fast-moving objects, see Lumen Global Noise on All Surfaces. If Lumen is fast but reflections look like cardboard cutouts, check Lumen Reflections Look Flat.

Temporal filter on, screen traces longer, surface cache margin wider — that triple knob fixes most motion noise.