Files
BlueRoseNote/03-UnrealEngine/卡通渲染相关资料/渲染功能/ARC/Rendering/自定义后处理.md

480 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: 自定义后处理
date: 2026-05-03 00:00:00
excerpt: REDPostProcess 全套自定义后处理Diffusion Filter、角色辉光、自定义 DOF
tags:
- ARC
- Rendering
- PostProcess
- Toon
rating: ⭐
---
# 自定义后处理
返回 [[Rendering]]
## 概述
ARC 引擎新增了完整的自定义后处理系统REDPostProcess提供 Diffusion Filter扩散滤镜、角色辉光Chara Glow和自定义景深DOF三大功能。这些效果专门为动画风格渲染设计与标准 UE4 后处理管线并行工作。
## Shader 端REDPostProcess.usf
### Diffusion Filter扩散滤镜
基于亮度的柔焦/辉光效果,使用 13-tap 高斯模糊7 权重核):
```hlsl
// 从场景颜色提取高亮区域
float Luminance = dot(SceneColor.rgb, float3(0.299, 0.587, 0.114));
float LuminanceMask = pow(Luminance, LuminancePow);
LuminanceMask = saturate(LuminanceMask - LuminanceThreshold);
// 高斯模糊后以 Screen 混合模式叠加
// Screen blend: 1 - (1-A)(1-B)
float3 Result = 1.0 - (1.0 - SceneColor.rgb) * (1.0 - BlurredHighlight);
```
可调参数:
- `DiffusionFilterLuminancePow` — 亮度幂次(控制提取范围)
- `DiffusionFilterLuminanceThreshold` — 亮度阈值
### Diffusion Filter 2变体
```hlsl
// Screen 混合 + Power 调整
float3 BlendResult = 1.0 - (1.0 - A) * (1.0 - B);
Result = pow(BlendResult, Power);
```
### 角色辉光Chara Glow
从 GBufferD 的 B 通道读取 OutlineID 作为辉光遮罩,进行可变半径的 Box Blur
```hlsl
// 读取 GBufferD.b 作为遮罩
float Mask = GBufferD.b;
// 可变半径 Box Blur
for (int y = -Radius; y <= Radius; y++)
for (int x = -Radius; x <= Radius; x++)
Sum += SampleMask(UV + offset);
// 以 CharaGlowColor 叠加
OutColor = Sum * CharaGlowColor * CharaGlowArea;
```
可调参数:
- `CharaGlowArea` — 辉光范围
- `CharaGlowColor` — 辉光颜色
### 自定义 DOF
替代标准 DOF 的 RED 专用版本,支持近景/远景独立模糊:
```hlsl
// 高斯模糊 Pass水平 + 垂直分离)
// 13-tap 核权重0.0044, 0.0540, 0.2420, 0.3990, ...
```
### Soft Focus / Glow / Sepia
其他后处理变体:
- **Soft Focus**:场景与模糊版本的简单混合 + 饱和度控制
- **Glow**:亮度阈值 + Screen 混合辉光
- **Sepia**Diffusion Filter 2 的棕褐色变体
## C++ 端REDPostProcess.h/.cpp
### 后处理 Pass 类型
```cpp
// 4 种主要 Pass
enum class EREDPostProcessPass
{
DiffusionFilter, // 扩散滤镜
CharaGlow, // 角色辉光
DownSample, // 降采样
BlurH, BlurV, // 水平/垂直高斯模糊
CustomGaussianDOF // 自定义 DOF
};
```
### 插入点控制
通过 CVar 控制后处理在管线中的插入位置:
```cpp
// r.REDPostprocessAfterTranslucency
// 0 = 在 SeparateTranslucency 之前
// 1 = 在 SeparateTranslucency 之后
// 2 = 在 Bloom 之后
```
### PostProcessSettings 扩展
```cpp
// Scene.h 新增
float DiffusionFilterLuminancePow;
float DiffusionFilterLuminanceThreshold;
float CharaGlowArea;
FLinearColor CharaGlowColor;
```
## 完整代码解析
### REDPostProcess.usf 完整着色器注解
#### 辅助函数
```hlsl
// 灰度计算ITU-R BT.601 标准权重)
float GetGrayscale(float3 Color)
{
return dot(Color, float3(0.299f, 0.587f, 0.114f));
}
// 饱和度插值:在灰度和原色之间按 Saturation 系数过渡
float3 LerpSaturation(float3 Color, float Saturation)
{
float Gray = GetGrayscale(Color);
return lerp(float3(Gray, Gray, Gray), Color, Saturation);
}
```
#### 高斯核权重7 权重13-tap 对称核)
```hlsl
// ColorSampleWeight — 用于降采样和模糊的高斯权重
// 对称分布:中心权重最大,两侧递减
// 权重值半侧index 0=中心):
// [0] = 0.3990 (中心)
// [1] = 0.2420
// [2] = 0.0540
// [3] = 0.0044
// [4] = 0.0001 (接近零)
// 合计 ≈ 1.0(归一化)
static const float ColorSampleWeight[7] = {
0.0044f, 0.0540f, 0.2420f, 0.3990f, 0.2420f, 0.0540f, 0.0044f
};
```
#### DownSamplingPS — 降采样
```hlsl
// 4x4 区域降采样为 1 像素(用于创建 1/4 分辨率 RT
// 采样模式2x2 双线性采样点,每点覆盖 2x2 像素
void DownSamplingPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
float2 UVOffset = 0.5f * InvSize; // 半像素偏移
// 4 次双线性采样取平均
OutColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV + float2(-UVOffset.x, -UVOffset.y));
OutColor += Texture2DSample(SceneColorTexture, SceneColorSampler, InUV + float2( UVOffset.x, -UVOffset.y));
OutColor += Texture2DSample(SceneColorTexture, SceneColorSampler, InUV + float2(-UVOffset.x, UVOffset.y));
OutColor += Texture2DSample(SceneColorTexture, SceneColorSampler, InUV + float2( UVOffset.x, UVOffset.y));
OutColor *= 0.25f;
}
```
#### BlurVerticalPS / BlurHorizonPS — 分离高斯模糊
```hlsl
// 13-tap 分离高斯模糊(垂直方向)
// 使用 ColorSampleWeight 权重,两侧各 6 个采样点 + 中心 1 个
void BlurVerticalPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
OutColor = float4(0, 0, 0, 0);
// 遍历 -6 到 +6 的偏移量
for (int i = -6; i <= 6; i++)
{
float2 SampleUV = InUV + float2(0, i * InvSize.y); // 垂直偏移
float Weight = ColorSampleWeight[abs(i)]; // 对称权重
OutColor += Texture2DSample(SceneColorTexture, SceneColorSampler, SampleUV) * Weight;
}
}
// 13-tap 分离高斯模糊(水平方向)
// 结构与垂直相同,仅偏移方向改为水平
void BlurHorizonPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
OutColor = float4(0, 0, 0, 0);
for (int i = -6; i <= 6; i++)
{
float2 SampleUV = InUV + float2(i * InvSize.x, 0); // 水平偏移
float Weight = ColorSampleWeight[abs(i)];
OutColor += Texture2DSample(SceneColorTexture, SceneColorSampler, SampleUV) * Weight;
}
}
```
#### DiffusionFilterPS — 基于亮度的辉光提取
```hlsl
// Diffusion Filter从场景中提取高亮区域用于后续模糊
// 步骤:
// 1. 采样场景颜色
// 2. 计算亮度 → 幂次调整 → 阈值裁剪
// 3. 输出 = 原始颜色 × 亮度遮罩
void DiffusionFilterPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV);
// 亮度计算
float Luminance = GetGrayscale(SceneColor.rgb);
// 幂次映射LuminancePow 越大,只有越亮的区域才会被保留
float LuminanceMask = pow(Luminance, DiffusionFilterLuminancePow);
// 阈值裁剪:低于阈值的区域完全去除
LuminanceMask = saturate(LuminanceMask - DiffusionFilterLuminanceThreshold);
// 输出带亮度遮罩的颜色
OutColor = float4(SceneColor.rgb * LuminanceMask, 1.0f);
}
```
#### DiffusionFilter2PS — Screen 混合 + Power
```hlsl
// Diffusion Filter 2将模糊后的辉光以 Screen 模式叠加回场景
// Screen blend: Result = 1 - (1-A)(1-B)
// 然后应用 Power 调整对比度
void DiffusionFilter2PS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
// 采样原始场景
float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV);
// 采样模糊后的辉光
float4 BlurColor = Texture2DSample(BlurTexture, BlurSampler, InUV);
// Screen 混合
float3 BlendResult = 1.0f - (1.0f - SceneColor.rgb) * (1.0f - BlurColor.rgb);
// Power 调整DiffusionFilterPower 控制辉光强度曲线)
BlendResult = pow(BlendResult, DiffusionFilterPower);
OutColor = float4(BlendResult, 1.0f);
}
```
#### SoftFocus — 柔焦
```hlsl
// Soft Focus场景与模糊版本的混合 + 饱和度调节
// 实现类似相机柔焦镜片的效果
void SoftFocus(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV);
float4 BlurColor = Texture2DSample(BlurTexture, BlurSampler, InUV);
// 在清晰和模糊之间按 SoftFocusBlend 系数混合
float3 Result = lerp(SceneColor.rgb, BlurColor.rgb, SoftFocusBlend);
// 饱和度调节
Result = LerpSaturation(Result, SoftFocusSaturation);
OutColor = float4(Result, 1.0f);
}
```
#### Glow — 辉光
```hlsl
// Glow基于亮度阈值的辉光效果
// 与 DiffusionFilter 类似但更简单,直接以 Screen 混合叠加
void Glow(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV);
float4 BlurColor = Texture2DSample(BlurTexture, BlurSampler, InUV);
// Screen 混合辉光
float3 Result = 1.0f - (1.0f - SceneColor.rgb) * (1.0f - BlurColor.rgb * GlowIntensity);
OutColor = float4(Result, 1.0f);
}
```
#### CharaGlowPS — 角色辉光模糊
```hlsl
// 角色辉光 Pass 1可变半径 Box Blur
// 从 GBufferD.b 读取辉光遮罩(由 OutlineID 编码)
// 以可变半径进行 Box Blur 扩散辉光范围
void CharaGlowPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
float Sum = 0;
float Count = 0;
// 从 CharaGlowArea 计算模糊半径(像素数)
int Radius = (int)CharaGlowArea;
// 双重循环 Box Blur
for (int y = -Radius; y <= Radius; y++)
{
for (int x = -Radius; x <= Radius; x++)
{
float2 SampleUV = InUV + float2(x, y) * InvSize;
// 采样 GBufferD.b 通道作为辉光遮罩
float Mask = Texture2DSample(GBufferDTexture, GBufferDSampler, SampleUV).b;
Sum += Mask;
Count += 1.0f;
}
}
// 归一化
OutColor = float4(Sum / Count, 0, 0, 1);
}
```
#### CharaGlowCompPS — 角色辉光合成
```hlsl
// 角色辉光 Pass 2将模糊后的辉光遮罩与辉光颜色合成
// 叠加到场景颜色上
void CharaGlowCompPS(
float4 Position : SV_POSITION,
float2 InUV : TEXCOORD0,
out float4 OutColor : SV_Target0)
{
// 采样场景颜色
float4 SceneColor = Texture2DSample(SceneColorTexture, SceneColorSampler, InUV);
// 采样模糊后的辉光遮罩
float GlowMask = Texture2DSample(CharaGlowTexture, CharaGlowSampler, InUV).r;
// 减去原始遮罩(只保留扩散出去的边缘部分)
float OriginalMask = Texture2DSample(GBufferDTexture, GBufferDSampler, InUV).b;
GlowMask = saturate(GlowMask - OriginalMask);
// 以 CharaGlowColor 叠加辉光
float3 Result = SceneColor.rgb + GlowMask * CharaGlowColor.rgb * CharaGlowColor.a;
OutColor = float4(Result, 1.0f);
}
```
### C++ 端实现
```cpp
// REDPostProcess.h — 后处理 Pass 类型枚举
enum REDPostProcess_Type {
EREDPostProcess_DownSample, // 降采样
EREDPostProcess_BlurH, // 水平高斯模糊
EREDPostProcess_BlurV, // 垂直高斯模糊
};
// 基础后处理 Pass降采样 + 模糊)
class FRCPassREDPostProcess : public TRenderingCompositePassBase<1, 1>
{
REDPostProcess_Type Type;
bool bToHalf; // 是否降采样到半分辨率
void RenderDownSamplingPass(FRenderingCompositePassContext& Context);
void RenderBlurHPass(FRenderingCompositePassContext& Context);
void RenderBlurVPass(FRenderingCompositePassContext& Context);
};
// Diffusion Filter Pass亮度提取 + Screen 混合)
class FRCPassREDPostProcess_DiffusionFilter : public TRenderingCompositePassBase<2, 1> { ... };
// Diffusion Filter 2 PassPower + 阈值 + 可选 CharaGlow 合成)
class FRCPassREDPostProcess_DiffusionFilter2 : public TRenderingCompositePassBase<4, 1> { ... };
// CharaGlow Pass可变半径 BoxBlur on GBufferD.b
class FRCPassREDPostProcess_CharaGlow : public TRenderingCompositePassBase<1, 1> { ... };
```
```cpp
// PostProcessing.cpp — 后处理管线插入
// CVar 控制插入位置
static TAutoConsoleVariable<int32> CVarREDPostprocessAfterTranslucency(
TEXT("r.REDPostprocessAfterTranslucency"),
// 0: SeparateTranslucency 之前
// 1: SeparateTranslucency 之后
// 2: Bloom 之后
);
// AddREDPostProcess — 构建后处理图
static void AddREDPostProcess(FPostprocessContext& Context)
{
float pow = Context.View.FinalPostProcessSettings.DiffusionFilterLuminancePow;
// 创建降采样 → 水平模糊 → 垂直模糊 → DiffusionFilter2 节点链
FRenderingCompositePass* NodeDownSample = new FRCPassREDPostProcess(EREDPostProcess_DownSample, true);
FRenderingCompositePass* NodeBlurH = new FRCPassREDPostProcess(EREDPostProcess_BlurH, false);
FRenderingCompositePass* NodeBlurV = new FRCPassREDPostProcess(EREDPostProcess_BlurV, false);
// 可选CharaGlow角色辉光
float area = Context.View.FinalPostProcessSettings.CharaGlowArea;
if (area > 0) {
FLinearColor color = Context.View.FinalPostProcessSettings.CharaGlowColor;
FRenderingCompositePass* NodeGlow = new FRCPassREDPostProcess_CharaGlow(color, area);
// ... 连接到图
}
// 最终 DiffusionFilter2 节点
float threshold = Context.View.FinalPostProcessSettings.DiffusionFilterLuminanceThreshold;
FRenderingCompositePass* NodeFinal = new FRCPassREDPostProcess_DiffusionFilter2(pow, threshold, bWithGlow);
}
```
## 关联文档
- [[RED自定义数据通道]] — CharaGlow 读取 GBufferD.b 通道
- [[BGMultColor全局色调]] — 另一个全局色彩控制系统
## 代码修改情况
| 文件路径 | 行号 | 修改类型 | 修改内容 |
|---------|------|---------|---------|
| `Shaders/Private/REDPostProcess.usf` | 全文 (473行) | **新增文件** | 完整自定义后处理着色器 |
| ↳ | L49~L57 | — | `PostProcessVS` 标准顶点着色器 |
| ↳ | L61~L86 | — | `PostProcessBlurVS` 高斯模糊顶点着色器预计算7个采样点 |
| ↳ | L96~L105 | — | `ColorSampleWeight[7]` 高斯权重表 |
| ↳ | L117~L129 | — | `GetGrayscale` / `LerpSaturation` 辅助函数 |
| ↳ | L134~L137 | — | `DownSamplingPS` 降采样 |
| ↳ | L142~L191 | — | `BlurVerticalPS` 垂直高斯模糊13-tap |
| ↳ | L196~L246 | — | `BlurHorizonPS` 水平高斯模糊13-tap |
| ↳ | L251~L278 | — | `DiffusionFilterPS` 扩散滤镜(亮度提取 + max 混合) |
| ↳ | L283~L318 | — | `DiffusionFilter2PS` 扩散滤镜2Screen 混合 + CharaGlow 合成) |
| ↳ | L323~L350 | — | `DiffusionFilter2WithOutCharaGlowPS` 不含辉光版本 |
| ↳ | L355~L376 | — | `DiffusionFilter2SepiaPS` 棕褐色变体 |
| ↳ | L383~L393 | — | `SoftFocus` 柔焦效果 |
| ↳ | L400~L430 | — | `Glow` 辉光(亮度阈值 + Screen 混合) |
| ↳ | L437~L455 | — | `CharaGlowPS` 角色辉光(可变半径 BoxBlur on GBufferD.b |
| ↳ | L461~L470 | — | `CharaGlowCompPS` 辉光合成(叠加到场景 + mask |
| `Source/Runtime/Renderer/Private/PostProcess/REDPostProcess.h` | 全文 (298行) | **新增文件** | 后处理 Pass 类声明4个 Pass 类 + 枚举) |
| `Source/Runtime/Renderer/Private/PostProcess/REDPostProcess.cpp` | 全文 (1735行) | **新增文件** | 后处理 Pass 实现Shader 绑定/参数设置/RT 管理) |
| `Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp` | L100~L129 | 新增 | `#include "REDPostProcess.h"` + `CVarREDPostprocessAfterTranslucency` CVar |
| `Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp` | L224~L661 | 新增 | `AddREDPostProcess` 完整后处理图构建函数 (~438行) |
| `Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp` | L669~L681 | 修改 | 根据 CVar 在管线中插入 RED 后处理 |
| `Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp` | L852~L908 | 新增 | CharaGlow 独立模式插入逻辑 |
| `Source/Runtime/Engine/Classes/Engine/Scene.h` | — | 修改 | 新增 `DiffusionFilterLuminancePow/Threshold``CharaGlowArea/Color` |