FSceneViewExtensionBase를 이용한 렌더 패스 추가

1. 목표


Custom Lens Flare효과를 구현하였을 때, 이 효과를 엔진 소스 코드 수정 없이 추가할 수 있다면 엔진 버전 업그레이드에 따른 마이그레이션이 용이할 것이라 생각했다.

2. 시도


일단 공식 문서를 둘러보았을때, 관련된 정보나 튜토리얼을 찾을수 없었다. 그래서 나와 비슷한 목표를 가진 사람들이 있었을 거라 생각하고 정보를 찾던 중 fab에서 관련된 플러그인을 찾을 수 있었다.
4. Archive/Unreal Lab/__Attachments/Pasted image 20251016223453.png
링크

플러그인 기반으로 Fog와 같은 Volumetric 효과를 구현한 프로젝트였으며, 더 자세한 정보를 찾기 위해 문서를 둘러보았다.
4. Archive/Unreal Lab/__Attachments/Pasted image 20251016223641.png
링크

문서에서 해당 플러그인이 SceneViewExtensionBase를 활용하여 구현되었음을 확인하였고, 해당 클래스에 대해서 분석을 시도했다.

SceneViewExtensionBase.h

class ISceneViewExtension
{
	[...]
	
	/**
	 * Called right before Post Processing rendering begins
	 */
	virtual void PrePostProcessPass_RenderThread(FRDGBuilder& GraphBuilder, const FSceneView& InView, const FPostProcessingInputs& Inputs) {};
}

[...]

class FSceneViewExtensionBase : public ISceneViewExtension, public TSharedFromThis<FSceneViewExtensionBase, ESPMode::ThreadSafe>
{
public:
	FSceneViewExtensionBase(const FAutoRegister&) {}
	ENGINE_API virtual ~FSceneViewExtensionBase();

	// Array of Functors that can be used to activate an extension for the current frame and given context.
	TArray<FSceneViewExtensionIsActiveFunctor> IsActiveThisFrameFunctions;

	// Determines if the extension should be active for the current frame and given context.
	ENGINE_API virtual bool IsActiveThisFrame(const FSceneViewExtensionContext& Context) const override final;
};

FSceneViewExtensionBase는 ISceneViewExtension를 상속하였으며, ISceneViewExtension에서 제공하는 다양한 함수중 PrePostProcessPass_RenderThread()를 오버라이딩하여 렌더패스를 추가하기로 결정하였다.

DeferredShadingRenderer.cpp

[...]

// FDeferredShadingRenderer::Render
// Line 3414
for (int32 ViewExt = 0; ViewExt < ViewFamily.ViewExtensions.Num(); ++ViewExt)
{
	for (int32 ViewIndex = 0; ViewIndex < ViewFamily.Views.Num(); ++ViewIndex)
	{
		FViewInfo& View = Views[ViewIndex];
		RDG_GPU_MASK_SCOPE(GraphBuilder, View.GPUMask);
		PostProcessingInputs.TranslucencyViewResourcesMap = FTranslucencyViewResourcesMap(TranslucencyResourceMap, ViewIndex);
		ViewFamily.ViewExtensions[ViewExt]->PrePostProcessPass_RenderThread(GraphBuilder, View, PostProcessingInputs);
	}
}

[...]

위와 같이 PrePostProcessPass_RenderThread가 실행되는 코드를 확인할 수 있다.

2.1. 구현


TyTSceneViewExtension.h

class TYTPOSTPROCESS_API FTyTSceneViewExtension : public FSceneViewExtensionBase
{
public:
	FTyTSceneViewExtension(const FAutoRegister& AutoRegister);
	~FTyTSceneViewExtension();

	virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override;
	virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override;
	virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override;

	virtual void PrePostProcessPass_RenderThread(
		FRDGBuilder& GraphBuilder,
		const FSceneView& InView,
		const FPostProcessingInputs& Inputs
	) override;
};

TyTSceneViewExtension.cpp

void AddTyTPostProcessPass(
	FRDGBuilder& GraphBuilder,
	const FViewInfo& ViewInfo,
	const FTyTPostProcessInputs& Inputs
)
{
	// Unreal Insights
	RDG_GPU_STAT_SCOPE(GraphBuilder, TyTPostProcess);
	// Render Doc
	RDG_EVENT_SCOPE(GraphBuilder, "TyTPostProcess");

	FTyTPostProcessPS::FParameters* PassParameters = GraphBuilder.AllocParameters<FTyTPostProcessPS::FParameters>();
	PassParameters->RenderTargets[0] = *Inputs.RenderTargetBinding;
	PassParameters->InputSampler = TStaticSamplerState<SF_Bilinear>::GetRHI();
	PassParameters->SceneColorTexture = Inputs.SceneColor;
	[...]

3. 결과