PostProcessing.cpp 拿到的是整套场景纹理、View 信息以及各阶段生成的中间结果。
这里可以看到scenecolor scenedepth velocity都有

custom depth \ custom stencil
velocity主要做后期处理的taa motion blus等等
我们可以尝试用custom depth + custom stencil + world normal做描边效果,跑一遍流程
核心也在于如何获取custom depth custom stencil worldnormal,先直接放代码:
新增文件
路径:
Engine/Source/Runtime/Renderer/Private/PostProcess/LearningOutline.h
内容:
// Copyright Epic Games, Inc. All Rights Reserved.
#pragma once
#include "SceneTexturesConfig.h"
#include "ScreenPass.h"
class FRDGBuilder;
class FViewInfo;
struct FLearningOutlineInputs
{
FScreenPassTexture SceneColor;
FScreenPassTexture SceneDepth;
FScreenPassTexture CustomDepth;
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures = nullptr;
};
FScreenPassTexture AddLearningOutlinePass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLearningOutlineInputs& Inputs);
因为需要从postprocessing.cpp输入scenecolor scenedepth customdepth信息,所以FLearningOutlineInputs里面需要这些数据,给到AddLearningOutlinePass这个函数,让它能够将参数传给GPU PS,然后去调用到.usf的内容
新增文件
路径:
Engine/Source/Runtime/Renderer/Private/PostProcess/LearningOutline.cpp
内容:
// Copyright Epic Games, Inc. All Rights Reserved.
#include "PostProcess/LearningOutline.h"
#include "DataDrivenShaderPlatformInfo.h"
#include "PixelShaderUtils.h"
#include "SceneRendering.h"
namespace
{
TAutoConsoleVariable<int32> CVarLearningOutlineEnable(
TEXT("r.LearningOutline.Enable"),
0,
TEXT("Enable custom depth outline."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineThickness(
TEXT("r.LearningOutline.Thickness"),
1.0f,
TEXT("Outline sample distance in pixels."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineDepthThreshold(
TEXT("r.LearningOutline.DepthThreshold"),
0.02f,
TEXT("Relative custom depth difference threshold."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineNormalThreshold(
TEXT("r.LearningOutline.NormalThreshold"),
0.10f,
TEXT("World normal difference threshold."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineVisibilityThreshold(
TEXT("r.LearningOutline.VisibilityThreshold"),
0.01f,
TEXT("Depth threshold used to determine whether the custom depth object is visible."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<int32> CVarLearningOutlineVisibleOnly(
TEXT("r.LearningOutline.VisibleOnly"),
1,
TEXT("0: Draw occluded outlines. 1: Draw visible outlines only."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineOpacity(
TEXT("r.LearningOutline.Opacity"),
1.0f,
TEXT("Outline opacity."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineColorR(
TEXT("r.LearningOutline.ColorR"),
1.0f,
TEXT("Outline red channel."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineColorG(
TEXT("r.LearningOutline.ColorG"),
0.05f,
TEXT("Outline green channel."),
ECVF_RenderThreadSafe);
TAutoConsoleVariable<float> CVarLearningOutlineColorB(
TEXT("r.LearningOutline.ColorB"),
0.0f,
TEXT("Outline blue channel."),
ECVF_RenderThreadSafe);
class FLearningOutlinePS : public FGlobalShader
{
public:
DECLARE_GLOBAL_SHADER(FLearningOutlinePS);
SHADER_USE_PARAMETER_STRUCT(
FLearningOutlinePS,
FGlobalShader);
BEGIN_SHADER_PARAMETER_STRUCT(FParameters, )
SHADER_PARAMETER_STRUCT_REF(
FViewUniformShaderParameters,
View)
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(
FSceneTextureUniformParameters,
SceneTextures)
SHADER_PARAMETER_RDG_TEXTURE(
Texture2D,
OutlineSceneColorTexture)
SHADER_PARAMETER_SAMPLER(
SamplerState,
OutlineSceneColorSampler)
SHADER_PARAMETER_RDG_TEXTURE(
Texture2D,
OutlineSceneDepthTexture)
SHADER_PARAMETER_SAMPLER(
SamplerState,
OutlineSceneDepthSampler)
SHADER_PARAMETER_RDG_TEXTURE(
Texture2D,
OutlineCustomDepthTexture)
SHADER_PARAMETER_SAMPLER(
SamplerState,
OutlineCustomDepthSampler)
SHADER_PARAMETER_RDG_TEXTURE_SRV(
Texture2D<uint2>,
OutlineCustomStencilTexture)
SHADER_PARAMETER(
FScreenTransform,
SvPositionToSceneColorUV)
SHADER_PARAMETER(
FScreenTransform,
SvPositionToSceneTextureUV)
SHADER_PARAMETER(
FScreenTransform,
SvPositionToCustomDepthUV)
SHADER_PARAMETER(
FVector2f,
SceneTextureTexelSize)
SHADER_PARAMETER(
FVector2f,
CustomDepthTexelSize)
SHADER_PARAMETER(
FVector2f,
CustomDepthExtent)
SHADER_PARAMETER(float, Thickness)
SHADER_PARAMETER(float, DepthThreshold)
SHADER_PARAMETER(float, NormalThreshold)
SHADER_PARAMETER(float, VisibilityThreshold)
SHADER_PARAMETER(float, Opacity)
SHADER_PARAMETER(int32, VisibleOnly)
SHADER_PARAMETER(
FVector3f,
OutlineColor)
RENDER_TARGET_BINDING_SLOTS()
END_SHADER_PARAMETER_STRUCT()
static bool ShouldCompilePermutation(
const FGlobalShaderPermutationParameters& Parameters)
{
return IsFeatureLevelSupported(
Parameters.Platform,
ERHIFeatureLevel::SM5);
}
};
IMPLEMENT_GLOBAL_SHADER(
FLearningOutlinePS,
"/Engine/Private/PostProcess/LearningOutline/LearningOutline.usf",
"LearningOutlinePS",
SF_Pixel);
FScreenTransform GetOutlineSvPositionToTextureUV(
const FScreenPassTextureViewport& OutputViewport,
const FScreenPassTextureViewport& InputViewport)
{
return
FScreenTransform::ChangeTextureBasisFromTo(
OutputViewport,
FScreenTransform::ETextureBasis::TexelPosition,
FScreenTransform::ETextureBasis::ViewportUV)
*
FScreenTransform::ChangeTextureBasisFromTo(
InputViewport,
FScreenTransform::ETextureBasis::ViewportUV,
FScreenTransform::ETextureBasis::TextureUV);
}
}
FScreenPassTexture AddLearningOutlinePass(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
const FLearningOutlineInputs& Inputs)
{
check(Inputs.SceneColor.IsValid());
check(Inputs.SceneDepth.IsValid());
check(Inputs.SceneTextures);
if (CVarLearningOutlineEnable.GetValueOnRenderThread() == 0)
{
return Inputs.SceneColor;
}
// 没有生成 CustomDepth 时直接返回。
if (!Inputs.CustomDepth.IsValid())
{
return Inputs.SceneColor;
}
FScreenPassRenderTarget Output =
FScreenPassRenderTarget::CreateFromInput(
GraphBuilder,
Inputs.SceneColor,
View.GetOverwriteLoadAction(),
TEXT("LearningOutline"));
const FScreenPassTextureViewport OutputViewport(Output);
const FScreenPassTextureViewport SceneColorViewport(
Inputs.SceneColor);
const FScreenPassTextureViewport SceneDepthViewport(
Inputs.SceneDepth);
const FScreenPassTextureViewport CustomDepthViewport(
Inputs.CustomDepth);
FLearningOutlinePS::FParameters* PassParameters =
GraphBuilder.AllocParameters<
FLearningOutlinePS::FParameters>();
FRHISamplerState* BilinearClampSampler =
TStaticSamplerState<
SF_Bilinear,
AM_Clamp,
AM_Clamp,
AM_Clamp>::GetRHI();
FRHISamplerState* PointClampSampler =
TStaticSamplerState<
SF_Point,
AM_Clamp,
AM_Clamp,
AM_Clamp>::GetRHI();
PassParameters->View = View.ViewUniformBuffer;
PassParameters->SceneTextures = Inputs.SceneTextures;
PassParameters->OutlineSceneColorTexture =
Inputs.SceneColor.Texture;
PassParameters->OutlineSceneColorSampler =
BilinearClampSampler;
PassParameters->OutlineSceneDepthTexture =
Inputs.SceneDepth.Texture;
PassParameters->OutlineSceneDepthSampler =
PointClampSampler;
PassParameters->OutlineCustomDepthTexture =
Inputs.CustomDepth.Texture;
PassParameters->OutlineCustomDepthSampler =
PointClampSampler;
PassParameters->OutlineCustomStencilTexture =
(*Inputs.SceneTextures)->CustomStencilTexture;
PassParameters->SvPositionToSceneColorUV =
GetOutlineSvPositionToTextureUV(
OutputViewport,
SceneColorViewport);
PassParameters->SvPositionToSceneTextureUV =
GetOutlineSvPositionToTextureUV(
OutputViewport,
SceneDepthViewport);
PassParameters->SvPositionToCustomDepthUV =
GetOutlineSvPositionToTextureUV(
OutputViewport,
CustomDepthViewport);
const FIntPoint SceneTextureExtent =
Inputs.SceneDepth.Texture->Desc.Extent;
const FIntPoint CustomDepthExtent =
Inputs.CustomDepth.Texture->Desc.Extent;
PassParameters->SceneTextureTexelSize =
FVector2f(
1.0f / static_cast<float>(SceneTextureExtent.X),
1.0f / static_cast<float>(SceneTextureExtent.Y));
PassParameters->CustomDepthTexelSize =
FVector2f(
1.0f / static_cast<float>(CustomDepthExtent.X),
1.0f / static_cast<float>(CustomDepthExtent.Y));
PassParameters->CustomDepthExtent =
FVector2f(
static_cast<float>(CustomDepthExtent.X),
static_cast<float>(CustomDepthExtent.Y));
PassParameters->Thickness =
FMath::Clamp(
CVarLearningOutlineThickness.GetValueOnRenderThread(),
1.0f,
8.0f);
PassParameters->DepthThreshold =
FMath::Max(
0.0f,
CVarLearningOutlineDepthThreshold.GetValueOnRenderThread());
PassParameters->NormalThreshold =
FMath::Clamp(
CVarLearningOutlineNormalThreshold.GetValueOnRenderThread(),
0.0f,
1.0f);
PassParameters->VisibilityThreshold =
FMath::Max(
0.0f,
CVarLearningOutlineVisibilityThreshold.GetValueOnRenderThread());
PassParameters->Opacity =
FMath::Clamp(
CVarLearningOutlineOpacity.GetValueOnRenderThread(),
0.0f,
1.0f);
PassParameters->VisibleOnly =
CVarLearningOutlineVisibleOnly.GetValueOnRenderThread();
PassParameters->OutlineColor =
FVector3f(
CVarLearningOutlineColorR.GetValueOnRenderThread(),
CVarLearningOutlineColorG.GetValueOnRenderThread(),
CVarLearningOutlineColorB.GetValueOnRenderThread());
PassParameters->RenderTargets[0] =
Output.GetRenderTargetBinding();
TShaderMapRef<FLearningOutlinePS> PixelShader(
View.ShaderMap);
FPixelShaderUtils::AddFullscreenPass(
GraphBuilder,
View.ShaderMap,
RDG_EVENT_NAME("LearningOutline"),
PixelShader,
PassParameters,
Output.ViewRect);
return FScreenPassTexture(Output);
}
assParameters->View = View.ViewUniformBuffer;
不是把 View 传给另一个 C++ 函数,而是把整组 View Uniform 参数绑定给 GPU Shader。
对应参数声明:
SHADER_PARAMETER_STRUCT_REF(
FViewUniformShaderParameters,
View)
Shader 中可以直接写:
View.PreExposure
View.BufferSizeAndInvSize
View.InvDeviceZToWorldZTransform
新增 Shader 文件
路径:
Engine/Shaders/Private/PostProcess/LearningOutline.usf
// Copyright Epic Games, Inc. All Rights Reserved.
#include "../../Common.ush"
#include "../../DeferredShadingCommon.ush"
#include "../../ScreenPass.ush"
Texture2D OutlineSceneColorTexture;
SamplerState OutlineSceneColorSampler;
Texture2D OutlineSceneDepthTexture;
SamplerState OutlineSceneDepthSampler;
Texture2D OutlineCustomDepthTexture;
SamplerState OutlineCustomDepthSampler;
Texture2D<uint2> OutlineCustomStencilTexture;
FScreenTransform SvPositionToSceneColorUV;
FScreenTransform SvPositionToSceneTextureUV;
FScreenTransform SvPositionToCustomDepthUV;
float2 SceneTextureTexelSize;
float2 CustomDepthTexelSize;
float2 CustomDepthExtent;
float Thickness;
float DepthThreshold;
float NormalThreshold;
float VisibilityThreshold;
float Opacity;
int VisibleOnly;
float3 OutlineColor;
struct FMarkedSample
{
uint Stencil;
float CustomDepth;
float SceneDepth;
float Visible;
};
int2 GetCustomDepthPixelPosition(float2 UV)
{
int2 PixelPosition =
int2(UV * CustomDepthExtent);
return clamp(
PixelPosition,
int2(0, 0),
int2(CustomDepthExtent) - 1);
}
uint LoadCustomStencil(float2 UV)
{
const int2 PixelPosition =
GetCustomDepthPixelPosition(UV);
return OutlineCustomStencilTexture.Load(
int3(PixelPosition, 0))
STENCIL_COMPONENT_SWIZZLE;
}
float LoadLinearCustomDepth(float2 UV)
{
const float DeviceZ =
Texture2DSampleLevel(
OutlineCustomDepthTexture,
OutlineCustomDepthSampler,
UV,
0).r;
return ConvertFromDeviceZ(DeviceZ);
}
float LoadLinearSceneDepth(float2 UV)
{
const float DeviceZ =
Texture2DSampleLevel(
OutlineSceneDepthTexture,
OutlineSceneDepthSampler,
UV,
0).r;
return ConvertFromDeviceZ(DeviceZ);
}
float3 LoadWorldNormal(float2 UV)
{
return GetGBufferData(UV).WorldNormal;
}
FMarkedSample GetMarkedSample(
float2 CustomDepthUV,
float2 SceneTextureUV)
{
FMarkedSample Result;
Result.Stencil =
LoadCustomStencil(CustomDepthUV);
Result.CustomDepth = 0.0f;
Result.SceneDepth =
LoadLinearSceneDepth(SceneTextureUV);
Result.Visible = 0.0f;
if (Result.Stencil != 0)
{
Result.CustomDepth =
LoadLinearCustomDepth(CustomDepthUV);
const float RelativeDepthDifference =
abs(Result.CustomDepth - Result.SceneDepth)
/ max(abs(Result.SceneDepth), 1.0f);
Result.Visible =
RelativeDepthDifference <= VisibilityThreshold
? 1.0f
: 0.0f;
}
return Result;
}
float CompareMarkedSamples(
FMarkedSample Center,
FMarkedSample Neighbor,
float2 CenterSceneUV,
float2 NeighborSceneUV)
{
const bool CenterMarked =
Center.Stencil != 0;
const bool NeighborMarked =
Neighbor.Stencil != 0;
if (!CenterMarked && !NeighborMarked)
{
return 0.0f;
}
// 不同 Stencil ID 一定认为是物体边界。
float StencilEdge =
Center.Stencil != Neighbor.Stencil
? 1.0f
: 0.0f;
if (VisibleOnly != 0)
{
const float AnyVisible =
max(Center.Visible, Neighbor.Visible);
StencilEdge *= AnyVisible;
}
float DepthEdge = 0.0f;
float NormalEdge = 0.0f;
if (CenterMarked && NeighborMarked)
{
const float RelativeDepthDifference =
abs(Center.CustomDepth - Neighbor.CustomDepth)
/ max(abs(Center.CustomDepth), 1.0f);
DepthEdge =
step(
DepthThreshold,
RelativeDepthDifference);
// WorldNormal 只对真正可见的 CustomDepth 表面可靠。
if (Center.Visible > 0.0f &&
Neighbor.Visible > 0.0f)
{
const float3 CenterNormal =
LoadWorldNormal(CenterSceneUV);
const float3 NeighborNormal =
LoadWorldNormal(NeighborSceneUV);
const float NormalDifference =
1.0f - saturate(
dot(
CenterNormal,
NeighborNormal));
NormalEdge =
step(
NormalThreshold,
NormalDifference);
}
}
if (VisibleOnly != 0)
{
const float BothVisible =
Center.Visible * Neighbor.Visible;
DepthEdge *= BothVisible;
NormalEdge *= BothVisible;
}
return max(
StencilEdge,
max(DepthEdge, NormalEdge));
}
void LearningOutlinePS(
float4 SvPosition : SV_POSITION,
out float4 OutColor : SV_Target0)
{
const float2 SceneColorUV =
ApplyScreenTransform(
SvPosition.xy,
SvPositionToSceneColorUV);
const float2 SceneTextureUV =
ApplyScreenTransform(
SvPosition.xy,
SvPositionToSceneTextureUV);
const float2 CustomDepthUV =
ApplyScreenTransform(
SvPosition.xy,
SvPositionToCustomDepthUV);
const float4 SceneColor =
Texture2DSample(
OutlineSceneColorTexture,
OutlineSceneColorSampler,
SceneColorUV);
const float2 SceneOffset =
SceneTextureTexelSize * Thickness;
const float2 CustomOffset =
CustomDepthTexelSize * Thickness;
const FMarkedSample Center =
GetMarkedSample(
CustomDepthUV,
SceneTextureUV);
const FMarkedSample Left =
GetMarkedSample(
CustomDepthUV -
float2(CustomOffset.x, 0),
SceneTextureUV -
float2(SceneOffset.x, 0));
const FMarkedSample Right =
GetMarkedSample(
CustomDepthUV +
float2(CustomOffset.x, 0),
SceneTextureUV +
float2(SceneOffset.x, 0));
const FMarkedSample Top =
GetMarkedSample(
CustomDepthUV -
float2(0, CustomOffset.y),
SceneTextureUV -
float2(0, SceneOffset.y));
const FMarkedSample Bottom =
GetMarkedSample(
CustomDepthUV +
float2(0, CustomOffset.y),
SceneTextureUV +
float2(0, SceneOffset.y));
float EdgeMask = 0.0f;
EdgeMask = max(
EdgeMask,
CompareMarkedSamples(
Center,
Left,
SceneTextureUV,
SceneTextureUV -
float2(SceneOffset.x, 0)));
EdgeMask = max(
EdgeMask,
CompareMarkedSamples(
Center,
Right,
SceneTextureUV,
SceneTextureUV +
float2(SceneOffset.x, 0)));
EdgeMask = max(
EdgeMask,
CompareMarkedSamples(
Center,
Top,
SceneTextureUV,
SceneTextureUV -
float2(0, SceneOffset.y)));
EdgeMask = max(
EdgeMask,
CompareMarkedSamples(
Center,
Bottom,
SceneTextureUV,
SceneTextureUV +
float2(0, SceneOffset.y)));
// 当前 Pass 在 Tonemap 前,需要转换到 PreExposure 空间。
const float3 PreExposedOutlineColor =
OutlineColor * View.PreExposure;
const float BlendAmount =
saturate(EdgeMask * Opacity);
const float3 FinalColor =
lerp(
SceneColor.rgb,
PreExposedOutlineColor,
BlendAmount);
OutColor =
float4(
FinalColor,
SceneColor.a);
}
STENCIL_COMPONENT_SWIZZLE 不是一个数值,而是引擎定义的预处理宏,用来选择 Load() 结果中真正存放 Stencil 的分量。
你的纹理类型是:
Texture2D<uint2> OutlineCustomStencilTexture;
因此:
OutlineCustomStencilTexture.Load(...)
返回的是:
uint2
类似:
uint2(x, y)
但函数需要返回单个 Stencil:
uint LoadCustomStencil(...)
所以必须从 uint2 中选择一个分量。
在 Platform.ush (line 475)(D:/UnrealEngine-release/UnrealEngine-release/Engine/Shaders/Public/Platform.ush:475) 中默认定义为:
#ifndef STENCIL_COMPONENT_SWIZZLE
#define STENCIL_COMPONENT_SWIZZLE .g
#endif
因此在当前 D3D 平台上,这段代码:
return OutlineCustomStencilTexture.Load(
int3(PixelPosition, 0))
STENCIL_COMPONENT_SWIZZLE;
预处理后相当于:
return OutlineCustomStencilTexture.Load(
int3(PixelPosition, 0)).g;
也就是从返回的 uint2 中取 .g 分量。
某些 Vulkan、Metal 平台则定义为:
#define STENCIL_COMPONENT_SWIZZLE .x
同一段代码会变成:
return OutlineCustomStencilTexture.Load(
int3(PixelPosition, 0)).x;
原因是不同图形 API 对 Depth-Stencil SRV 的映射不同:
D3D等平台:Stencil 可能在 .g
Vulkan/Metal:Stencil 可能在 .x
所以不要直接写死:
.Load(...).g
应该使用:
.Load(...) STENCIL_COMPONENT_SWIZZLE
例如两个位置都相差 1 米:
1米 → 2米:
DeviceZ 0.1 → 0.05
差值 = 0.05
100米 → 101米:
DeviceZ 0.001 → 0.000990
差值约 = 0.00001
明明都是相差 1 米,DeviceZ 差异却完全不同。
如果直接做描边:
abs(DeviceZA - DeviceZB) > Threshold
就会出现:
近处很容易超过阈值
远处很难超过阈值
ConvertFromDeviceZ(DeviceZ)
这个函数让它的深度变得线性,且做了Z-Reverse
ConvertFromDeviceZ 做什么
它执行投影的逆变换:
非线性的 DeviceZ
↓
ConvertFromDeviceZ
↓
线性的 SceneDepth
例如:
DeviceZ 0.1 → 1米
DeviceZ 0.05 → 2米
DeviceZ 0.001 → 100米
DeviceZ 0.000990 → 101米
转换后:
2 - 1 = 1米
101 - 100 = 1米
这时深度阈值才有一致意义。
Shader 前置 include
你现在的 Shader 位于:
Engine/Shaders/Private/PostProcess/LearningOutline.usf
所以开头需要:
#include "../Common.ush"
#include "../DeferredShadingCommon.ush"
#include "../ScreenPass.ush"
GetGBufferData() 定义在:
DeferredShadingCommon.ush
调用:
FGBufferData GBufferData =
GetGBufferData(UV);
float3 WorldNormal =
GBufferData.WorldNormal;
也可以直接:
float3 WorldNormal =
GetGBufferData(UV).WorldNormal;
C++ 必须绑定 View
Shader 参数结构里要有:
SHADER_PARAMETER_STRUCT_REF(
FViewUniformShaderParameters,
View)
并赋值:
PassParameters->View =
View.ViewUniformBuffer;
因为 GetGBufferData() 内部会使用:
View.BufferSizeAndInvSize
View.BufferToSceneTextureScale
C++ 必须绑定 SceneTextures
Shader 参数结构中必须有:
SHADER_PARAMETER_RDG_UNIFORM_BUFFER(
FSceneTextureUniformParameters,
SceneTextures)
然后:
PassParameters->SceneTextures =
Inputs.SceneTextures;
因为 GetGBufferData() 内部读取的是:
SceneTexturesStruct.GBufferATexture
SceneTexturesStruct.GBufferBTexture
SceneTexturesStruct.GBufferCTexture
SceneTexturesStruct.GBufferDTexture
SceneTexturesStruct.GBufferETexture
SceneTexturesStruct.GBufferFTexture
SceneTexturesStruct.GBufferVelocityTexture
如果没有绑定 SceneTextures,它不知道应该读取哪套 GBuffer。
所以这里才可以直接获取到WorldNormal
float3 LoadWorldNormal(float2 UV)
{
return GetGBufferData(UV).WorldNormal;
}
修改 PostProcessing.cpp
打开:
Engine/Source/Runtime/Renderer/Private/PostProcess/PostProcessing.cpp
在 include 区域增加:
#include "PostProcess/LearningOutline.h"
然后找到当前的 LearningBloom 代码。把 Bloom 前面这句删掉:
SceneColorBeforeTonemapSlice = SceneColorSlice;
最终这里写成:
{
FLearningBloomInputs LearningBloomInputs;
LearningBloomInputs.SceneColor =
FScreenPassTexture::CopyFromSlice(
GraphBuilder,
SceneColorSlice);
const FScreenPassTexture LearningBloomOutput =
AddLearningBloomPass(
GraphBuilder,
View,
LearningBloomInputs);
SceneColorSlice =
FScreenPassTextureSlice::CreateFromScreenPassTexture(
GraphBuilder,
LearningBloomOutput);
}
// CustomDepth + CustomStencil + WorldNormal 描边。
{
FLearningOutlineInputs LearningOutlineInputs;
LearningOutlineInputs.SceneColor =
FScreenPassTexture::CopyFromSlice(
GraphBuilder,
SceneColorSlice);
LearningOutlineInputs.SceneDepth =
SceneDepth;
LearningOutlineInputs.CustomDepth =
CustomDepth;
LearningOutlineInputs.SceneTextures =
Inputs.SceneTextures;
const FScreenPassTexture LearningOutlineOutput =
AddLearningOutlinePass(
GraphBuilder,
View,
LearningOutlineInputs);
SceneColorSlice =
FScreenPassTextureSlice::CreateFromScreenPassTexture(
GraphBuilder,
LearningOutlineOutput);
}
SceneColorBeforeTonemapSlice = SceneColorSlice;
if (PassSequence.IsEnabled(EPass::Tonemap))
{
// 原来的 Tonemap 代码
}
void AddPostProcessingPasses(
FRDGBuilder& GraphBuilder,
const FViewInfo& View,
int32 ViewIndex,
FSceneUniformBuffer& SceneUniformBuffer,
EDiffuseIndirectMethod DiffuseIndirectMethod,
EReflectionsMethod ReflectionsMethod,
const FPostProcessingInputs& Inputs,
const Nanite::FRasterResults* NaniteRasterResults,
FInstanceCullingManager& InstanceCullingManager,
FVirtualShadowMapArray* VirtualShadowMapArray,
FLumenSceneFrameTemporaries& LumenFrameTemporaries,
const FSceneWithoutWaterTextures& SceneWithoutWaterTextures,
FScreenPassTexture TSRFlickeringInput,
FRDGTextureRef& InstancedEditorDepthTexture)
{
AddPostProcessingPasses里面有一个输入叫const FPostProcessingInputs& Inputs
里面可以拿很多好东西:
struct FPostProcessingInputs
{
TRDGUniformBufferRef<FSceneTextureUniformParameters> SceneTextures = nullptr;
FRDGTextureRef ViewFamilyTexture = nullptr;
FRDGTextureRef ViewFamilyDepthTexture = nullptr;
FRDGTextureRef CustomDepthTexture = nullptr;
FRDGTextureRef ExposureIlluminance = nullptr;
FTranslucencyViewResourcesMap TranslucencyViewResourcesMap;
FPathTracingResources PathTracingResources;
bool bSeparateCustomStencil = false;
void Validate() const
{
check(SceneTextures);
check(ViewFamilyTexture);
check(TranslucencyViewResourcesMap.IsValid());
}
};
BEGIN_GLOBAL_SHADER_PARAMETER_STRUCT(FSceneTextureUniformParameters, ENGINE_API)
// Scene Color / Depth / Partial Depth
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneColorTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, SceneDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ScenePartialDepthTexture)
// GBuffer
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferATexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferBTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferCTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferDTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferETexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferFTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferVelocityTexture)
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, GBufferSGGXTexture)
// SSAO
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, ScreenSpaceAOTexture)
// Custom Depth / Stencil
SHADER_PARAMETER_RDG_TEXTURE(Texture2D, CustomDepthTexture)
SHADER_PARAMETER_RDG_TEXTURE_SRV(Texture2D<uint2>, CustomStencilTexture)
// Misc
SHADER_PARAMETER_SAMPLER(SamplerState, PointClampSampler)
END_GLOBAL_SHADER_PARAMETER_STRUCT()
里面你可以拿到GBuffer所有内容,还有custom depth 和 custom stencil
本文核心就是上面内容

