UE5 源码 - PostProcessing.cpp 具体的数据 以及描边练习

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

本文核心就是上面内容