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

16 KiB
Raw Blame History

title, date, excerpt, tags, rating
title date excerpt tags rating
自定义后处理 2026-05-03 00:00:00 REDPostProcess 全套自定义后处理Diffusion Filter、角色辉光、自定义 DOF
ARC
Rendering
PostProcess
Toon

自定义后处理

返回 Rendering

概述

ARC 引擎新增了完整的自定义后处理系统REDPostProcess提供 Diffusion Filter扩散滤镜、角色辉光Chara Glow和自定义景深DOF三大功能。这些效果专门为动画风格渲染设计与标准 UE4 后处理管线并行工作。

Shader 端REDPostProcess.usf

Diffusion Filter扩散滤镜

基于亮度的柔焦/辉光效果,使用 13-tap 高斯模糊7 权重核):

// 从场景颜色提取高亮区域
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变体

// Screen 混合 + Power 调整
float3 BlendResult = 1.0 - (1.0 - A) * (1.0 - B);
Result = pow(BlendResult, Power);

角色辉光Chara Glow

从 GBufferD 的 B 通道读取 OutlineID 作为辉光遮罩,进行可变半径的 Box Blur

// 读取 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 专用版本,支持近景/远景独立模糊:

// 高斯模糊 Pass水平 + 垂直分离)
// 13-tap 核权重0.0044, 0.0540, 0.2420, 0.3990, ...

Soft Focus / Glow / Sepia

其他后处理变体:

  • Soft Focus:场景与模糊版本的简单混合 + 饱和度控制
  • Glow:亮度阈值 + Screen 混合辉光
  • SepiaDiffusion Filter 2 的棕褐色变体

C++ 端REDPostProcess.h/.cpp

后处理 Pass 类型

// 4 种主要 Pass
enum class EREDPostProcessPass
{
    DiffusionFilter,    // 扩散滤镜
    CharaGlow,          // 角色辉光
    DownSample,         // 降采样
    BlurH, BlurV,       // 水平/垂直高斯模糊
    CustomGaussianDOF   // 自定义 DOF
};

插入点控制

通过 CVar 控制后处理在管线中的插入位置:

// r.REDPostprocessAfterTranslucency
// 0 = 在 SeparateTranslucency 之前
// 1 = 在 SeparateTranslucency 之后
// 2 = 在 Bloom 之后

PostProcessSettings 扩展

// Scene.h 新增
float DiffusionFilterLuminancePow;
float DiffusionFilterLuminanceThreshold;
float CharaGlowArea;
FLinearColor CharaGlowColor;

完整代码解析

REDPostProcess.usf 完整着色器注解

辅助函数

// 灰度计算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 对称核)

// 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 — 降采样

// 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 — 分离高斯模糊

// 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 — 基于亮度的辉光提取

// 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

// 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 — 柔焦

// 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 — 辉光

// 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 — 角色辉光模糊

// 角色辉光 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 — 角色辉光合成

// 角色辉光 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++ 端实现

// 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> { ... };
// 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);
}

关联文档

代码修改情况

文件路径 行号 修改类型 修改内容
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/ThresholdCharaGlowArea/Color