背景
这段时间大量接触了unreal 5 的后处理相关内容,踩了不少奇奇怪怪的坑,且 unreal 的文档也非常混乱,很多地方不测试根本没法知道实现效果是怎么样的,因此就开贴记录留念一下。由于项目相关的原因,我们不能在大世界手动添加后处理volume,只能使用蓝图的后处理组件进行相关开发,因此此处主要讨论后处理组件相关内容。同时我也并不负责美术工作,因此那些具体效果的实现、相关参数和材质也并不包含在这里。我们当前使用的 UE 版本是 5.0.3,因此本文仅依据后处理在 5.0.3 中的表现。
关于后处理
后处理,其实可以称为渲染后处理,是在 3D 场景已经渲染完成以后再对成果进行处理,从而实现一些场景整体的效果。在较为底层的 OpenGL 实现中,后处理可以通过将渲染结果存为 2D 贴图存入 frame buffer,再使用各种图像处理算法在 shader (着色器)中对该贴图进行处理。在 unreal 中,后处理可以通过后处理体积(volume)或在蓝图中添加后处理组件实现。它们是 unreal 提供的用于实现后处理的特殊类型的组件,包含了很多常用的后处理相关的参数,并且可以添加后处理材质。
全局和局部
unreal 提供了 unbound/infinite Extent 参数来开启全局后处理。当 unbound 设置为 true 时,相机在场景中的任何位置后处理都会起作用,否则就只能在限定的范围内起作用。当相机处于限定的范围内时,后处理效果就会作用在该相机返回的画面上(注意后处理永远是针对整个场景的,范围只用于确定生效与否)。此外,经过实测发现,unbound 并非完全无界,当镜头足够远(大约1:1拉到可以看到整个地球的范围)时,后处理效果依然会消失(个人推测是 Unreal 定义的 world 或者 Level 的最大范围)。
后处理 volume 本身带有碰撞盒(BrushComponent),因此当后处理非全局时,碰撞盒的范围就是后处理作用的范围。然而后处理组件本身不带碰撞盒!!!(我查了好久才发现= _ =)。这里非 unbound 使用的边界是继承自件父节点的边界,因此如果需要有界的后处理组件,需要将后处理组件放在一个有碰撞盒的 actor 内部,或者在组件外套一个碰撞盒,如 Box Collision,如下图所示。

效果修改
后处理 volume 或组件本身带有上千个用于调整效果的参数,绝大部分都没有提供单独的 get 和 set 函数。且整个 settings 是一个无法 break 的 structure。因此当我们需要修改参数时,需要获取 postprcess component 的 PostProcessSettings 参数(类型是一个structure),调用 Set Members in PostPorcessSettings 节点,再在 details 面板上勾选需要暴露出的参数,如下图所示。C++ 也是类似的操作,我们可以获取 postprcess component 的 Settings,将需要改的参数设置为可更改再修改数值即可。代码可参考 component->Settings.bOverride_WhiteTemp = true; component->Settings.WhiteTemp = 15000;
。注意获取 Settings 的操作会返回 Settings 的拷贝,所以如果将它赋值再修改的话需要将修改后的 settings 重新赋给组件的 settings。

覆盖和冲突
当一个区域被多个后处理组件或 volume 包含时,后处理提供了 bland weight(混合权重) 参数和 priority(优先级)参数来管理被多个后处理影响的区域的效果。priority(优先级)参数是用来控制多个后处理的优先级的,优先级高的后处理效果会叠加在优先级低的效果之上,而 bland weight 则是当前后处理的展示权重。对于每叠加一个优先级更高的后处理,效果参数值 $ x = x_原 + \Delta x w $ 。假设我们当前所处的位置被两个后处理所影响,优先级较高的后处理 Temp(白平衡)为 10000,bland weight 为 0.3,优先级较低的后处理 Temp 为 3000,bland weight 为 0.6。场景默认白平衡为 6500。最终所展现效果 Temp 值为 $ (10000 - (6500 + (3000 - 6500) 0.6)) 0.3 + (6500 + (3000 - 6500) 0.6) $。
此外 unreal 还提供了 blend radius 融合半径参数,在有界后处理的边界外加一个指定半径的渐变,从原来的效果渐变为后处理的效果。半径的数值单位和 UE 的数值单位相同。
不过需要注意的是, UE 5.0 当前有一个 bug,当我们试图在蓝图或者C++中动态修改 priority 的数值时,无论在运行时还是编辑器模式下,场景都是不会自动刷新的。即,如果使用 “set priority” 节点或直接在C++中改变 Priority 的值,将一个后处理组件的优先级从比另一个组件的优先级低变为比另一个组件的优先级高,虽然在细节面板里 priority 的值已经修改了,但场景却不会刷新。只有当我们再修改一个会影响场景效果的值,如修改 settings 或者其中一个后处理的 visible,场景才会刷新,优先级效果也会起作用。
后处理材质
后处理材质可以支持更多自定义的后处理效果的实现,可以根据需要获取场景的各个数值(如 world position)进行计算和编码。创建后处理材质需要在材质设置的地方将 material domain 设置成 PostPorcess。这样可以看到 material attribut 只有 emission color(自发光)一个出口,这个出口就是屏幕最终展现的效果。注意由于效果是叠加的自发光,输入颜色较深时效果会偏暗,如果传入黑色则就完全没有效果了。
后处理材质是 Unreal 的一种可混合物,虽然可混合物当前基本只用在后处理材质上,但其有一个完整的概念体系,所以这里就不展开了。唯一需要注意的是,后处理材质在 settings 里的存储方式是 WeightedBlendables,因此当我们需要将材质和它的权重转为 WeightedBlendable 存到数组里,再将数组合成成 WeightedBlendables。具体实现方案可以参考下图。

Reference
- https://docs.unrealengine.com/5.0/zh-CN/post-process-effects-in-unreal-engine/
- https://docs.unrealengine.com/5.1/zh-CN/post-process-materials-in-unreal-engine/
- https://docs.unrealengine.com/5.0/en-US/API/Runtime/Engine/Components/UPostProcessComponent/
- https://docs.unrealengine.com/5.0/zh-CN/blendables-in-unreal-engine/
- https://docs.unrealengine.com/5.0/en-US/content-examples-sample-project-for-unreal-engine/