13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Unreal Engine (UE)Advent Calendar 2023

Day 18

UE5 デリゲートを使ったフレームキャプチャをする方法

Last updated at Posted at 2023-12-25

概要

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でキャプチャしてイベントを確認します。

image.png
Sceneのイベント内で、RenderFinishの手前で実行されています。
この後にSlate UIのイベントが実行されてくるのでUIの描画が行われる前のタイミングです

追加したパスでキャプチャする

イベントの実行タイミングは意図通りのものだったので、追加したパスで現在のバックバッファをレンダーターゲットに描き込めばよさそうです

キャプチャするソースコードは、com04さんの記事を元にUE5.3に合わせてAPIの変更に対応しました。
厳密に動作検証しているわけではないので、不具合あるかもしれません。

https://github.com/zi-su/BackBufferCapture/tree/main
test.gif

おわりに

日々エンジン更新されているので検索して出てきた情報が古かった場合には、最新ドキュメントとソースコードを確認してもっと賢い方法や楽な方法がないか調べるようにしていきたいですね。

13
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
13
8

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?