概要
UE5アドベントカレンダーシリーズ6の18日目です。
動作環境
UE5.3.2
ゲーム開発していると現在レンダリングしている画面をキャプチャしてレンダーターゲットテクスチャとして使いたいときがよくあります。
UnrealEngineにはキャプチャする様々な方法がありますが重かったり情報が古くなったりしているので改めて紹介します
従来の方法
検索してよく出てくる方法をまとめます。
SceneCapture2Dを使ったキャプチャ
検索して真っ先に出てくる方法だと思います。
一番手軽ですが、ランタイムで全画面をキャプチャするのに使うには非常に重く、キャプチャ対象を絞ってキャラのみにするなど用途に合わせた使い方が必要になります
FrameGrabberを使う
FrameGrabberを使ってキャプチャする方法もあります。
紹介されている通りUIも含めたキャプチャになってしまうので困ることもあります。
具体的にはポーズやメインメニューを開いたりしたとき、背景をキャプチャした絵で止めて処理負荷対策をするときなどです。
エンジン改造をしてキャプチャする
2019年のアドベントカレンダーで紹介されているエンジン改造の方法もあります。
エンジン改造はビルドも必要になり、難易度も高いです。
2019年当時は4.23.1ということで、記事執筆時点の最新5.3.2(記事執筆時点)ではソースコードも変わり参考にするのも難しいです。
デリゲートを使った方法
現在では3Dレンダリングの最後に好きな描画コマンドを付け足すことができるデリゲートが公開されています。
こちらを使ってUIレンダリング前の段階を任意のレンダーターゲットに描き込むことでキャプチャすることができます
デリゲートを確認
Engine.hに以下のデリゲートが追加されています
UE_DEPRECATED(5.0, "Please use GetPreRenderDelegateEx().")
FPreRenderDelegate& GetPreRenderDelegate() { return PreRenderDelegate; }
FPreRenderDelegateEx& GetPreRenderDelegateEx() { return PreRenderDelegateEx; }
UE_DEPRECATED(5.0, "Please use GetPostRenderDelegateEx().")
FPostRenderDelegate& GetPostRenderDelegate() { return PostRenderDelegate; }
FPostRenderDelegateEx& GetPostRenderDelegateEx() { return PostRenderDelegateEx; }
呼び出しタイミングは
FDeferredShadingSceneRenderer::Render(FRDGBuilder& GraphBuilder)の末尾
RenderFinish呼び出し前
FMobileShadingRenderer::REnderの末尾
RenderFinish呼び出し前
FMobileSceneRenderer::RenderHitProxiesの最終行
FDeferrredShadingSceneRenderer::RenderHitProxiesの末尾
となっています。
デリゲートの定義は
DECLARE_MULTICAST_DELEGATE_OneParam(FPostRenderDelegateEx, class FRDGBuilder&);
となっているので、任意のFRDGBuilderを構築して渡せばなにか実行できそうです
適当にGameInstanceSubsystemのクラスを作成しInitializeでデリゲートに処理を渡して動作を確認します
UCLASS()
class BACKBUFFERCAPTURE_API UHogeSubsystem : public UGameInstanceSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
void Render(FRDGBuilder& GraphBuilder);
};
#include "HogeSubsystem.h"
#include "RenderGraphEvent.h"
#include "RenderGraphUtils.h"
DECLARE_GPU_STAT(HogeSubSystemRender);
void UHogeSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
GEngine->GetPostRenderDelegateEx().AddUObject(this, &UHogeSubsystem::Render);
}
void UHogeSubsystem::Render(FRDGBuilder& GraphBuilder)
{
AddPass(GraphBuilder, RDG_EVENT_NAME("UHogeSubsystem::Render"), [](FRHICommandListImmediate& RHICmdList)
{
});
}
エディタ再生しRenderDocでキャプチャしてイベントを確認します。
Sceneのイベント内で、RenderFinishの手前で実行されています。
この後にSlate UIのイベントが実行されてくるのでUIの描画が行われる前のタイミングです
追加したパスでキャプチャする
イベントの実行タイミングは意図通りのものだったので、追加したパスで現在のバックバッファをレンダーターゲットに描き込めばよさそうです
キャプチャするソースコードは、com04さんの記事を元にUE5.3に合わせてAPIの変更に対応しました。
厳密に動作検証しているわけではないので、不具合あるかもしれません。
https://github.com/zi-su/BackBufferCapture/tree/main
おわりに
日々エンジン更新されているので検索して出てきた情報が古かった場合には、最新ドキュメントとソースコードを確認してもっと賢い方法や楽な方法がないか調べるようにしていきたいですね。