ぼっちプログラマのメモ

UE4とかUE5とかについて書いたり書かなかったり。

UMG(Widget)をテクスチャ(RenderTarget)に描画する方法について (サンプル配布) (UE4.21に対応)

*はじめに
UMGの描画内容をテクスチャに焼き込みたいケースはしばしばあります。3Dモデルの表面に反映させたり、以前説明したStereoLayerに使用したり…その他色々!

f:id:pafuhana1213:20170521162948p:plain


SceneCapture2Dを使うことで簡単に実現はできるのですが、処理負荷の問題やレベルのどこかに配置する必要があったりと面倒です。ということで、今回はUMGの内容を直接テクスチャに描画する方法を紹介します。

説明とかいいから使いたいんだけど!?という方は、↓のサンプルプロジェクトをどうぞ。プラグイン化してます。4.15で作りました。
github.com

実装方法

WidgetRendererを使って、Widgetの内容をWindowではなくTextureに描画します。
FWidgetRenderer | Unreal Engine Documentation (うーん、あまり説明載ってない…)

WidgetRendererはBP側には公開されていないため、C++で機能実装しBPノード化する必要があります。サンプルプロジェクトのプラグインを見たほうが早い気もしますが、念のためブログにもC++コードを載せときます。

C++コード

h

 #include "UMG.h"
 #include "SlateBasics.h"
 #include "SlateCore.h"
 #include "SWidget.h"
 #include "WidgetRenderer.h"
  
 ...
 
 UFUNCTION(Category = "UMG", BlueprintCallable)
 static void DrawWidgetToTarget(UTextureRenderTarget2D * Target, UUserWidget * WidgetToRender, FVector2D DrawSize, bool UseGamma, TextureFilter Filter, float DeltaTime );

cpp

void UXXXLibrary::DrawWidgetToTarget(UTextureRenderTarget2D * Target, UUserWidget * WidgetToRender, FVector2D DrawSize, bool UseGamma, TextureFilter Filter, float DeltaTime)
{
	if (!WidgetToRender)
	{
		return;
	}
	if (DrawSize == FVector2D(0, 0))
	{
		return;
	}
	if (!Target)
	{
		return;
	}

	FWidgetRenderer * r = new FWidgetRenderer(UseGamma);
	TSharedRef<SWidget> ref = WidgetToRender->TakeWidget();
	r->DrawWidget(Target, ref, DrawSize, DeltaTime);

	delete r;
}

追記
UE4.20にて上記のコードを使うとクラッシュするので、以下のコードへの切り替えを推奨します。

FWidgetRenderer* WidgetRenderer = new FWidgetRenderer(true, false);
check(WidgetRenderer);

TSharedRef<SWidget> ref = WidgetToRender->TakeWidget();
WidgetRenderer->DrawWidget(Target, ref, DrawSize, DeltaTime);
FlushRenderingCommands();

BeginCleanup(WidgetRenderer);

追記(2019/3/16)
UE4.21対応版をアップしました。
GitHub - pafuhana1213/RenderWidgetToTarget at UE4.21

追記(2021/5/09)
UE4.25対応版をアップしました。4.26でも動くはずです
https://github.com/pafuhana1213/RenderWidgetToTarget/tree/20210509
https://github.com/pafuhana1213/RenderWidgetToTarget/releases/tag/20210509

その他

Build.csのPublicDependencyModuleNames.AddRangeに "UMG" "Slate", "SlateCore" を追加

追記
UE4.20以降は "RenderCore"も追加する必要があるようです。
[Linker Error] FWidgetRenderer - UE4 AnswerHub

最後に

これで以下のようにノードを組むことで、
f:id:pafuhana1213:20170521171024p:plain
UMGの内容をテクスチャに描画できます。
f:id:pafuhana1213:20170521170917p:plain

毎フレームテクスチャに描画すると処理負荷が気になってくるので、UMG側で変更が発生した時だけ書き込む仕組みにすることをおすすめします。あとは、今回のコードは最低限度レベルのものなので、是非色々と拡張してみて下さい!

おわり!