UE 后处理管线解析
UE 后处理管线解析
后处理(Post Process)是 FDeferredShadingSceneRenderer::Render(FRDGBuilder&) 的最后一个阶段,用来调整整个场景的外观和视觉。这篇按 UE 源码走一遍后处理管线:AddPostProcessingPasses 的完整 Pass 顺序、PassSequence 怎么保证顺序、Gamma/线性空间、HDR 与 ToneMapping 的四代演进,以及怎么塞一个自定义后处理 Pass 进去。
AddPostProcessingPasses:入口与 Pass 顺序
后处理的总入口是 AddPostProcessingPasses。它做的事按顺序是:
- 获取纹理、视图数据和场景颜色
- 初始化纹理(
EyeAdaptationBuffer、HistogramTexture、LocalExposureTexture等) - 处理后处理开启标记
- 声明后处理序列
PassSequence实例,逐个判断开/关
IsPostProcessingEnabled 为真时,Pass 大致按这个顺序执行(顺序本身就是重点,因为后处理是叠加的):
- 后处理材质链(半透明之前)
- 光圈景深(DOF)
- 后处理材质链(色调映射之前)
- TAA / SSR
- 后处理材质链(SSR Input)
- 运动模糊(并在此生成/保存半分辨率场景颜色到历史)
- 局部曝光参数、直方图、人眼适应(自动曝光)
- 泛光(
BloomSetup→GaussianBloom→LensFlare) - 颜色查找(
CombineLUT) - 色调映射(Tonemap)
- FXAA
- 后处理材质链(色调映射之后)
- 可视化 Pass、编辑器 Pass
- 主放大 Pass、次放大 Pass
如果后处理整体关闭,则走「最低限度后处理」——只做半透明合成和伽玛校正。
PassSequence 为什么必须存在
后处理效果是叠加的,顺序错了结果就错。TOverridePassSequence 用一个枚举把所有 Pass 按固定规则有序管理和执行:
AcceptPass:如果没按枚举顺序调用,直接报错——这就是它防错的核心。AcceptOverrideIfLastPass:如果某个 Pass 是最后一个,接受它覆盖输出 RT。
后处理材质插入点 EBlendableLocation
自定义后处理材质靠 Blendable Location 决定插到管线哪个位置:
| Location | 用途 |
|---|---|
| After Tonemapping | 默认 |
| Before Tonemapping | 后处理需要色调映射前的场景颜色 |
| Before Translucency | 不想影响透明物体 |
| Replacing the Tonemapper | 用自定义算法替代 UE 默认色调映射 |
| SSR Input | 自定义 SSR 算法 |
AddPostProcessMaterialChain 遍历材质链、给每个材质加一个 Pass;AddPostProcessMaterialPass 加单个后处理材质 Pass。
Gamma 与线性空间
- Gamma 空间管线:渲染前后不对纹理颜色做线性转换。
- 线性空间管线:Shader 前期去掉 Gamma 校正,后期再恢复。
为什么要线性空间:随光强增加,非线性方式算出来的表面会偏亮,很多地方曝光过度;线性渲染下表面颜色随光照强度线性增加,更接近现实。UE 默认就是线性空间管线——所有纹理和颜色在 shader 计算中保持线性,最后呈现到屏幕前经 Gamma 校正转成 sRGB。材质编辑器里一般不用操心这个转换,UE 把 Gamma 恢复和色调映射放一起处理了。
HDR 与 ToneMapping
HDR(高动态范围)有更高对比度和更广色域,但显示器被限制在 0.0~1.0——强光超过 1.0 的部分全按白色显示,丢失所有细节。现代引擎在线性空间里光照计算可能产生比普通白色亮成百上千倍的值,不压缩到合理值域,设备就显示异常。色调映射(ToneMapping)就是把过高的颜色值压到显示设备兼容的范围。这个思路源自早期电影——胶片能表达的亮度范围有限,摄影师把高亮和阴影往中间压,形成了 S 曲线映射。
ToneMapping 曲线的四代演进:
| 算法 | 年份/出处 | 特点 |
|---|---|---|
| Reinhard | 2002 | 简单直接,亮的变暗、暗的变亮。缺点是整体灰暗,颜色都往灰色方向压 |
| CE (CryEngine2) | 2007,孤岛危机 | 用 exp 模拟 S 曲线,颜色更鲜艳,快。缺点是纯凑函数,无法继续改进 |
| Filmic (Uncharted2) | 2010 | 艺术家人肉调完后拟合出的高次表达式,魔术数多,但开启了曲线拟合的路线。缺点是两个多项式,计算量偏大 |
| ACES | 美国电影艺术与科学学会 | 一套解决设备间色彩空间转换的编码系统。实时渲染只需其中一部分,同样是多项式拟合但式子比 Filmic 简单,只要一个多项式,比前面任何一个都鲜艳且不丢细节 |
UE5 色调映射入口是 AddTonemapPass:创建输出纹理 → 采样器设置(BilinearClampSampler/PointClampSampler)→ 处理色差和色调映射参数 → 启用 CS/PS,输入是场景颜色、Bloom、LUT,输出 HDR。
其中 ColorGradingLUT 是在 tonemap 之前的 CombineLUTs 生成的 32×32×32 3D 纹理,每帧根据场景颜色动态生成,不是生成一次就缓存。CombineLUT 的 shader 里重点是 FilmToneMap——除了常规色彩空间转换和曲线映射,还加了 Slope(斜率)、Toe(脚趾)、Shoulder(肩部)、Black Clip、White Clip 等不同色阶的调整。
添加自定义后处理 Pass
涉及文件:自定义的 .cpp/.h/.usf、PostProcessing.cpp,以及可选的 Scene.h/Scene.cpp/SceneView.cpp。步骤:
- 在
Engine\Shaders\Private下加XXXX.usf。 - 在
Engine\Source\Runtime\Renderer\Private\PostProcess下新建XXXX.h/.cpp,写输入参数结构体和函数:FScreenPassTexture AddXXXPass(FRDGBuilder& GraphBuilder, const FViewInfo& View, const FXXXInput& Inputs); - PS 函数中用 RDG 语法创建参数,用
IMPLEMENT_GLOBAL_SHADER绑定 shader 和入口。 - 创建 Struct 输入和外部调用函数;在调用函数里创建 Output、初始化 RDG 参数、
AddPass、返回 Output。 - 在
AddPostProcessingPasses的某个阶段调用它。 - (可选)暴露编辑器可视化:在
Scene.h建对应参数和 override 参数,Scene.cpp里初始化(UE4.27 还需在拷贝构造里处理),SceneView.cpp的OverridePostProcessSettings里做覆盖。
延伸阅读
- Brian Karis, Real Shading in Unreal Engine 4(SIGGRAPH 2013)
- UE 官方文档:Custom Post Process、Render Dependency Graph (RDG)