SceneViewExtension 是 Unreal Engine 提供的一个接口类(ISceneViewExtension),它允许开发者在渲染管线的特定阶段插入自定义逻辑。
简单来说,它是引擎预留的渲染钩子 。通过继承并实现它,你可以访问到每一帧的 FSceneView 和 FSceneViewFamily,并在 Base Pass 之前、之后,或者后期处理(Post-processing)等环节修改渲染状态或缓冲区数据。
主要回调函数(生命周期)
要用好这个工具,必须理解它在渲染线程中的执行时机。以下是几个最常用的钩子:
| 函数名 | 执行线程 | 触发时机 |
|---|---|---|
SetupViewFamily |
Game Thread | 初始化阶段,用于设置 ViewFamily 的属性。 |
PreRenderViewFamily_RenderThread |
Render Thread | 渲染开始前,可以修改渲染标志位、更计算后续渲染需要的数据。 |
PreRenderView_RenderThread |
Render Thread | 针对单个 View 进行设置(多 View 场景下多次触发)。 |
PostRenderBasePass_RenderThread |
Render Thread | 在 Base Pass(不透明物体渲染)完成后调用。 |
SubscribeToPostProcessingPass |
Render Thread | 允许你在特定的后期处理节点(如 ToneMapping)插入逻辑。 |
更多请查看 ISceneViewExtension 的定义 |
使用基本流程
继承接口
需要创建一个类,继承自 FSceneViewExtensionBase(它是 ISceneViewExtension 的默认实现,处理了引用计数)。
#include "SceneViewExtension.h"
class FMyCustomViewExtension : public FSceneViewExtensionBase
{
public:
FMyCustomViewExtension(const FAutoRegister& AutoRegister) : FSceneViewExtensionBase(AutoRegister) {}
// 即使什么都不做,也必须提供空实现
virtual void SetupViewFamily(FSceneViewFamily& InViewFamily) override {}
virtual void SetupView(FSceneViewFamily& InViewFamily, FSceneView& InView) override {}
virtual void BeginRenderViewFamily(FSceneViewFamily& InViewFamily) override {}
// 实现你需要的钩子
void PreRenderViewFamily_RenderThread(FRDGBuilder& GraphBuilder, FSceneViewFamily& InViewFamily) override
{
// 这里的逻辑运行在渲染线程
}
};注册扩展
你可以通过两种方式注册:
- 自动注册: 使用
FSceneViewExtensions::NewExtension注册,下面演示在Module中注册:
class FMyProjectModule : public IModuleInterface
public:
virtual void StartupModule() override {
// 此时 CEngine 可能未完全初始化,需要延迟到 OnPostEngineInit 再执行
FCoreDelegates::OnPostEngineInit.AddLambda([](FMyProjectModule* ModulePtr) {
if (ModulePtr) ModulePtr->ViewExtension = FSceneViewExtensions::NewExtension<FMySceneViewExtension>();
}, this);
}
virtual void ShutdownModule() override {
ViewExtension.Reset();
}
private:
// 存储对象,以便传输数据
TSharedPtr<FMySceneViewExtension, ESPMode::ThreadSafe> ViewExtension;
}- 手动注册: 使用
GEngine->ViewExtensions->Add(MyExtensionInstance)。
一些技巧和注意事项
多 Extension 优先级
多个 Extension 存在时,可以重写 GetPriority() 返回不同的整数来决定执行顺序。
多玩家/多视图
需要在 PreRenderView 中判断 InView 是否是你想要干预的那个玩家视图。
线程安全
通常在 Game Thread(如 Actor 或 Subsystem)中收集数据,然后在渲染线程中使用。务必注意线程安全,可用考虑使用双缓冲或渲染命令(ENQUEUE_RENDER_COMMAND)来同步数据。
性能警示
SceneViewExtension 的回调每帧都会在渲染线程执行。如果逻辑包含复杂的 RHI 调用或资源遍历,请务必进行性能测试,避免拖慢 GPU 流水线。