Pipeline Execution Order — The EPass Enum管线执行顺序 — EPass 枚举

The entire post-processing chain is orchestrated by a single function: AddPostProcessingPasses() (PostProcessing.cpp:347). It uses a TOverridePassSequence — a priority queue of passes where the last enabled pass receives the final viewport output. Every pass is declared as an EPass enum value, and the order of that enum is the canonical GPU execution order. Passes not needed for the current frame are culled by RDG automatically.

整个后处理链由单一函数 AddPostProcessingPasses()PostProcessing.cpp:347)编排。 它使用 TOverridePassSequence——一个 Pass 优先队列,最后一个启用的 Pass 接收最终视口输出。 每个 Pass 声明为 EPass 枚举值,该枚举的顺序即为规范的 GPU 执行顺序。 当前帧不需要的 Pass 由 RDG 自动剔除。

// PostProcessing.cpp:440 — EPass enum defines the pipeline order
// (some passes run before this enum: DOF, PP Materials BeforeDOF/AfterDOF)

enum class EPass : uint32
{
    MotionBlur,                        // runs AFTER TSR so it has full-res output
    PostProcessMaterialBeforeBloom,    // BL_SceneColorBeforeBloom chain
    Tonemap,                           // ACES / Filmic, Color Grading LUT, Vignette
    FXAA,                              // only when AntiAliasingMethod == AAM_FXAA
    PostProcessMaterialAfterTonemapping, // BL_SceneColorAfterTonemapping chain

    // Debug / visualize passes (stripped in Shipping builds):
    VisualizeLumenScene,
    VisualizeDepthOfField,
    VisualizeStationaryLightOverlap,
    VisualizeLightCulling,
    VisualizeSubstrate,
    VisualizeLightGrid,
    VisualizeSkyAtmosphere,
    SelectionOutline,          // editor-only
    EditorPrimitive,           // editor-only (gizmos, grid)
    VisualizeVirtualShadowMaps,
    VisualizeShadingModels,
    VisualizeGBufferHints,
    VisualizeGBufferOverview,
    VisualizeHDR,
    VisualizeLocalExposure,
    VisualizeMotionVectors,
    VisualizeTemporalUpscaler,
    PixelInspector,            // editor-only
    HMDDistortion,             // VR lens correction
    HighResolutionScreenshotMask,

    // Final output:
    PrimaryUpscale,            // spatial upscale when TSR not active (e.g. DLSS)
    SecondaryUpscale,          // second stage upscale
    AlphaInvert,
    MAX
};

Pre-Temporal Stage — Before TSR/TAA时域前阶段 — TSR/TAA 之前

Several passes run before TSR/TAA, at the native primary resolution. These are not part of the EPass enum — they are dispatched directly inside AddPostProcessingPasses() before the enum-driven sequence begins:

多个 Pass 在 TSR/TAA 之前以原始主分辨率运行。 它们不属于 EPass 枚举——而是在枚举驱动的序列开始之前直接在 AddPostProcessingPasses() 中调度:

TSR / TAA — The Temporal PivotTSR / TAA — 时域枢纽

TSR (Temporal Super Resolution) is UE's in-house upscaler, implemented in TemporalSuperResolution.cpp (174 KB — the largest single post-process file). It sits between the pre-temporal passes and Motion Blur, and performs two jobs at once:

TSR(Temporal Super Resolution)是 UE 的自研上采样器,实现于 TemporalSuperResolution.cpp (174 KB——最大的单个后处理文件)。它位于时域前通道和运动模糊之间,同时完成两项工作:

TSR also generates half/quarter/eighth-resolution mip outputs in the same dispatch pass. These are consumed directly by the Bloom chain to avoid redundant downsampling passes. This is why Motion Blur and Bloom run after TSR — they need the full secondary-resolution output.

TSR 还在同一 Dispatch Pass 中生成半/四分之一/八分之一分辨率的 Mip 输出, Bloom 链直接消费这些输出以避免冗余下采样 Pass。 这就是为什么运动模糊和 Bloom 在 TSR 之后运行——它们需要完整的辅分辨率输出。

// PostProcessing.cpp:967 — TSR/TAA dispatch
if (TAAConfig == EMainTAAPassConfig::TSR)
{
    Outputs = AddMainTemporalSuperResolutionPasses(GraphBuilder, View, UpscalerPassInputs);
}
else if (TAAConfig == EMainTAAPassConfig::TAA)
{
    Outputs = AddGen4MainTemporalAAPasses(GraphBuilder, View, UpscalerPassInputs);
}
else if (TAAConfig == EMainTAAPassConfig::ThirdParty)
{
    // DLSS / FSR / XeSS registered via ITemporalUpscaler interface
    Outputs = AddThirdPartyTemporalUpscalerPasses(GraphBuilder, View, UpscalerPassInputs);
}

// TSR outputs: FullRes + HalfRes + QuarterRes + EighthRes
// The downsample chain is built directly from these outputs — no extra passes needed
SceneColorSlice = Outputs.FullRes;
HalfResSceneColor    = Outputs.HalfRes;     // ← Bloom samples here
QuarterResSceneColor = Outputs.QuarterRes;
EighthResSceneColor  = Outputs.EighthRes;

Bloom & Exposure ChainBloom 与曝光链

The Bloom / Exposure chain runs in the background, parallel to (but feeding into) the main post-processing sequence. Its inputs are the downsized mip outputs from TSR:

Bloom/曝光链在后台运行,与主后处理序列并行(但馈入其中)。其输入是来自 TSR 的缩小 Mip 输出:

Tonemap & Color Grading色调映射与色彩分级

The Tonemap pass (PostProcessTonemap.cpp) is the pivot from HDR to LDR. It is one of the most expensive single passes because it combines many operations into one full-screen shader dispatch to minimize bandwidth:

Tonemap Pass(PostProcessTonemap.cpp)是从 HDR 到 LDR 的枢纽。 它是最昂贵的单个 Pass 之一,因为它将许多操作合并到一个全屏着色器 Dispatch 中以最小化带宽:

Color Grading LUT baking: The LUT is baked once per frame by AddCombineLUTPass() (PostProcessCombineLUTs.cpp). It iterates all active post-process volumes, weights their LUTs by blend radius/priority, and bakes the combined result into a single 32³ texture. This is why changing a post-process volume's Color Grading settings takes effect immediately without re-compiling shaders. 色彩分级 LUT 烘焙:LUT 由 AddCombineLUTPass()PostProcessCombineLUTs.cpp)每帧烘焙一次。它遍历所有激活的后处理体积,按混合半径/优先级为其 LUT 加权,并将组合结果烘焙到单个 32³ 纹理中。这就是为什么更改后处理体积的色彩分级设置会立即生效而无需重新编译着色器。

All Post-Processing Effects — Reference所有后处理效果 — 参考

Effect效果 Source File源文件 Description & Key CVar说明与关键控制台变量
Depth of FieldDiaphragmDOF.cppPhysically accurate bokeh via scatter-gather algorithm; 3 quality modes. r.DOF.Algorithm通过散射-收集算法实现物理精确散景;3 种质量模式。r.DOF.Algorithm
Subsurface ScatteringPostProcessSubsurface.cppScreen-space SSS for skin/wax; reads GBufferD. Runs before TAA. r.SSS.Quality皮肤/蜡质的屏幕空间 SSS;读取 GBufferD。在 TAA 之前运行。r.SSS.Quality
TSRTemporalSuperResolution.cppUE's in-house temporal upscaler (174KB source). r.TSR.History.ScreenPercentageUE 自研时间超分辨率(174KB 源码)。r.TSR.History.ScreenPercentage
TAA (Gen4)TemporalAA.cppTemporal anti-aliasing without upscaling. r.TemporalAA.Algorithm无上采样的时间抗锯齿。r.TemporalAA.Algorithm
Motion BlurPostProcessMotionBlur.cppScreen-space velocity-based blur; per-object + camera. r.MotionBlur.Amount基于速度的屏幕空间模糊;逐物体 + 相机。r.MotionBlur.Amount
Eye AdaptationPostProcessEyeAdaptation.cppAuto-exposure via histogram or basic luminance. r.EyeAdaptation.MethodOverride通过直方图或基本亮度实现自动曝光。r.EyeAdaptation.MethodOverride
Local ExposurePostProcessLocalExposure.cppPer-region bilateral grid exposure. r.LocalExposure.Method逐区域双边网格曝光。r.LocalExposure.Method
Bloom (Gaussian)PostProcessWeightedSampleSum.cpp6-level downsample + upsample pyramid. r.Bloom.Intensity6 级下采样 + 上采样金字塔。r.Bloom.Intensity
Bloom (FFT)PostProcessFFTBloom.cppConvolution bloom via 2D FFT. r.Bloom.Method 1通过 2D FFT 的卷积 Bloom。r.Bloom.Method 1
Lens FlaresPostProcessLensFlares.cppStreak flares from bright spots, composited onto Bloom. r.LensFlare来自亮点的条纹炫光,合成到 Bloom。r.LensFlare
Color Grading LUTPostProcessCombineLUTs.cppCombines ≤5 post-process volume LUTs into one 32³ texture. r.LUT.Size将最多 5 个后处理体积 LUT 合并为一个 32³ 纹理。r.LUT.Size
TonemapPostProcessTonemap.cppHDR→LDR: ACES/Filmic + LUT + Bloom composite + Vignette + CA + Film Grain. r.Tonemapper.TypeHDR→LDR:ACES/Filmic + LUT + Bloom 合成 + 渐晕 + 色差 + 胶片颗粒。r.Tonemapper.Type
FXAAPostProcessAA.cppFast AA on LDR output; no temporal lag. r.FXAALDR 输出上的快速 AA;无时间延迟。r.FXAA
PP MaterialsPostProcessMaterial.cppArtist-authored materials injected at BeforeDOF / BeforeBloom / ReplacingTonemapper / AfterTonemapping.美术编写的材质,注入 BeforeDOF / BeforeBloom / ReplacingTonemapper / AfterTonemapping。
Lens DistortionLensDistortion.cppBarrel/pincushion correction; can run inside TSR. r.LensDistortion桶形/枕形校正;可在 TSR 内运行。r.LensDistortion
Neural Post ProcessNeuralPostProcess.cppExperimental NNE-based upscaling/filtering. r.NNE.ModelData实验性 NNE 上采样/滤波。r.NNE.ModelData

Custom Post-Processing — Three Methods自定义后处理——三种方法

Method 1: Post-Process Material (No C++)方法 1:后处理材质(无需 C++)

Create a Material with Material Domain = Post Process. Assign it to a Post Process Volume or Camera. The Blendable Location controls where in the pipeline it runs:

创建材质域为 Post Process 的材质,分配给后处理体积或相机。 可混合位置控制它在管线中的运行位置:

Blendable LocationStage阶段SceneColor stateSceneColor 状态
BL_SceneColorBeforeDOFBefore Depth of Field景深之前HDR, primary resolution, sharpHDR,主分辨率,清晰
BL_SceneColorBeforeBloomAfter TSR, before BloomTSR 之后,Bloom 之前HDR, full secondary resolutionHDR,完整辅分辨率
BL_ReplacingTonemapperReplaces tonemapper entirely完全替换色调映射器HDR in, must output LDR [0,1]HDR 输入,必须输出 LDR [0,1]
BL_SceneColorAfterTonemappingAfter tonemap, before upscale色调映射后,上采样前LDR [0,1], final color spaceLDR [0,1],最终色彩空间

Method 2: ISceneViewExtension (Plugin-Safe)方法 2:ISceneViewExtension(插件安全)

Implement ISceneViewExtension and override SubscribeToPostProcessingPass() to inject a custom RDG compute pass at a specific point in the pipeline. This is the approach used by DLSS, FSR, and XeSS:

实现 ISceneViewExtension 并重写 SubscribeToPostProcessingPass(), 在管线特定点注入自定义 RDG Compute Pass。这是 DLSS、FSR 和 XeSS 使用的方法:

// 1. Declare your ViewExtension
class FMyPostProcessExtension : public FSceneViewExtensionBase
{
    IMPLEMENT_SCENE_VIEW_EXTENSION(FMyPostProcessExtension);
public:
    virtual void SubscribeToPostProcessingPass(
        ISceneViewExtension::EPostProcessingPass Pass,
        const FSceneView& View,
        FAfterPassCallbackDelegateArray& InOutPassCallbacks,
        bool bIsPassEnabled) override
    {
        // Inject AFTER the Tonemap pass
        if (Pass == ISceneViewExtension::EPostProcessingPass::Tonemap)
        {
            InOutPassCallbacks.Add(FAfterPassCallbackDelegate::CreateRaw(
                this, &FMyPostProcessExtension::RenderMyEffect));
        }
    }

private:
    FScreenPassTexture RenderMyEffect(
        FRDGBuilder& GraphBuilder,
        const FSceneView& View,
        const FPostProcessMaterialInputs& Inputs)
    {
        FScreenPassTexture SceneColor = Inputs.GetInput(EPostProcessMaterialInput::SceneColor);

        // Allocate output texture
        FRDGTextureDesc Desc = SceneColor.Texture->Desc;
        Desc.Flags |= TexCreate_UAV;
        FRDGTextureRef Output = GraphBuilder.CreateTexture(Desc, TEXT("MyEffect.Output"));

        // Fill pass parameters
        FMyEffectCS::FParameters* Params = GraphBuilder.AllocParameters<FMyEffectCS::FParameters>();
        Params->InputTexture = SceneColor.Texture;
        Params->Output       = GraphBuilder.CreateUAV(Output);
        Params->InvViewSize  = FVector2f(1.f/Desc.Extent.X, 1.f/Desc.Extent.Y);

        TShaderMapRef<FMyEffectCS> CS(GetGlobalShaderMap(View.GetFeatureLevel()));
        FComputeShaderUtils::AddFullscreenPass(GraphBuilder,
            GetGlobalShaderMap(View.GetFeatureLevel()),
            RDG_EVENT_NAME("MyPostEffect"), CS, Params,
            FIntVector(FMath::DivideAndRoundUp(Desc.Extent.X, 8),
                       FMath::DivideAndRoundUp(Desc.Extent.Y, 8), 1));

        return FScreenPassTexture(Output, SceneColor.ViewRect);
    }
};

// 2. Register during module startup:
GEngine->ViewExtensions->NewExtension<FMyPostProcessExtension>();

// Available injection points (EPostProcessingPass enum):
//   BeforeDOF, AfterDOF, TranslucencyAfterDOF,
//   MotionBlur, Tonemap, FXAA, VisualizeDepthOfField,
//   ReplacingTonemapper

Method 3: Direct Engine Source Modification方法 3:直接引擎源码修改

For passes that need GBuffer data, Lumen surface cache, VSM pages, or Nanite visibility info, you must modify the engine source directly — either inserting your pass into FDeferredShadingSceneRenderer::Render() or AddPostProcessingPasses(). This requires a custom engine build but gives unrestricted access to every intermediate resource in the frame.

对于需要 GBuffer 数据、Lumen 表面缓存、VSM 页面或 Nanite 可见性信息的 Pass, 必须直接修改引擎源码——将 Pass 插入 FDeferredShadingSceneRenderer::Render()AddPostProcessingPasses()。这需要自定义引擎构建,但可以无限制访问帧中的每个中间资源。

Source Navigation源码导航