🔦

UnrealEngine5 + TouchDesignerで作るビジュアルパフォーマンス

2022/12/22に公開

こちらはTouchDesigner Advent Calendar 2022の23日目の記事です。

今年の12/9日にMUTEK JPで行った Mars89 & HEXPIXELS のオーディオビジュアルパフォーマンスでやったことをブレイクダウンしていきます。神田くんの記事 と関連する所も多いので、そちらも是非ご覧ください!


パフォーマンスの様子

https://www.youtube.com/watch?v=ZLHGYjWJ-cc

HEXPIXELSのスタイル

MUTEKにはHEXPIXELS名義で出演しました。HEXPIXELSは僕とKezzadrixこと神田竜くんの二名でやっているVJユニットで、比較的長い間やっているのでスタイルはだんだん変わっていっていますが、近年は:

  • シーンのレイアウトガイドを決める
  • カメラや音解析のデータなど、レンダリングに必要になる諸情報をOSCで共有
  • 2台のPCからの最終出力の映像をHDMIミキサーで合成

というシステムを採用しています。

制作の際、シーンを作るためのレイアウトガイドも事前に共有しているので作り始めているので、双方が全然違ったコンセプトのシーンを作っていても同じガイドに沿っているのでなんか合って見える!不思議だね! というスタイルの絵作りをしています。

これまでのHEXPIXELS活動の様子

コンセプト

https://www.youtube.com/watch?v=birEAtzdCsk

最近の個人的なテーマが光と影なのですが、今回は今までよりフォトリアルな見た目にしたかったので、思い切って全てのレンダリングをUE5ですることにしました。

せっかくUE5を使うのであればTouchDesignerでは難しいリアルタイムレイトレーシングを使った鏡面全反射などが使えると、ライトシミュレーション的な側面が強くなって面白い効果が出るのでは?と思いそのあたりを重点的にリサーチすることにしました。

イメージとしては ストレンジャー・シングス の真っ暗で床に水が貼ってあるシーンみたいなことができたらいいな~と思っていました。

https://www.buzzfeed.com/crystalro/stranger-things-void-real-life
(結果として神田くんの異世界めいたシーンとかさなることでストレンジャー・シングスみが増したのではないかとおもっています)

また、これまでのHEXPIXELSの活動の中で、人の形をしたものが画面の中にいると自然とそこが絵の中心に見えるという効果を発見していたので、今回も人を出すことにして以前に制作した頭がスポットライトになっているロボットと、謎の発光する球をかぶされた兄ちゃんを配置しています。

その他にも光を遮ることで特徴的なシルエットが出るオブジェクトが欲しかったので、3Dモデルマーケットプレイスでアンテナ(パラボラアンテナ、八木アンテナ、携帯電話の基地局のようなもの)を大量に買って配置しています。アンテナはかっこいい

結果的に、基本的な構造としては以前にやったパフォーマンスの作り方を踏襲しつつ、レンダリングのクオリティが上がった感じになったのではないかと思います。

TouchDesignerとUnrealEngineの連携

TDとUEの連携には以前にTDSWでワークショップをした時に作ったプラグイン、OSCActorをUE5.1対応にアップデートして使っています。新しいOSCActorはPIE状態でなくても動くようになりました。神
https://youtu.be/Lvna6WZKOS0
https://github.com/backspacetokyo/UE-OSCActor

また、テクスチャの送受信にはSpoutを使っています。こちらで使ったプラグインもUE5.1対応にアップデートしました。テクスチャはレンダリング後の映像の受け渡しだけでなく、オーディオリアクティブなテクスチャを送ってそれを使ったレンダリングをする、などにも利用しています。
https://github.com/backspacetokyo/UE-Spout2Media

慣れの問題も多分にあると思いますが、UEのBlueprintやマテリアルはTDと比較するとコンパイル時間や操作性などの面で効率が悪かったのでなるべくTD側で調整できる領域を増やしてより早くイテレーションを回せるような制作環境を目指しました

カラーマネジメント / ポストプロセス

これまでの経験上、光の表現をキレイにしたい時はカラーマネジメントが大事 ということを学んだので、そこを意識した構成になっています。

カラーパイプライン
まず最初にUEのレンダリング結果のテクスチャを可能な限り情報のロスなく扱いたい、という所で問題がありました。

UEのフレームバッファはデフォルトだとRGB10bitのフォーマットになっているので、1以上の高輝度の情報がクリップされてしまい渡せません。プロジェクト設定で Frame Buffer Pixel FormatFloat RGBA にしてもクリップされたような挙動になってしまい困っていたのですが、PostProcessVolumeにReplacing the Tonemapperの設定をしたポストロセスマテリアルをアサインして、そこでLogCエンコードすると高輝度情報を保ったままSpoutでテクスチャの送信ができました。

PPM_LogC

LinearToLogcのHLSLコード
float cut = 0.010591;
float a = 5.555556;
float b = 0.052272;
float c = 0.247190;
float d = 0.385537;
float e = 5.367655;
float f = 0.092809;
float _e_cut_f = 0.149658;

x = float3(
	x.x * 0.631341 + x.y * 0.270797 + x.z * 0.097862,
	x.x * 0.0368226 + x.y * 0.793056 + x.z * 0.170122,
	x.x * 0.0173727 + x.y * 0.148808 + x.z * 0.83382
);

x = (x > cut) ? c * log10(a * x + b) + d: e * x + f;

return x;

SpoutでTDにテクスチャを送信後、エンコードと同じようにしてGLSL TOPでLinear RGBにデコードします。その後Linear RGBのカラースペースでブルームや色調整をしていました。

LogcToLinearのglslコード
float cut = 0.010591;
float a = 5.555556;
float b = 0.052272;
float c = 0.247190;
float d = 0.385537;
float e = 5.367655;
float f = 0.092809;
float _e_cut_f = 0.149658;

out vec4 fragColor;

void main()
{
	vec4 color = texture(sTD2DInputs[0], vUV.st);
	
	vec3 t = color.rgb;
	t.x = (t.x > e * cut + f) ? (pow(10.0, (t.x - d) / c) - b) / a: (t.x - f) / e;
	t.y = (t.y > e * cut + f) ? (pow(10.0, (t.y - d) / c) - b) / a: (t.y - f) / e;
	t.z = (t.z > e * cut + f) ? (pow(10.0, (t.z - d) / c) - b) / a: (t.z - f) / e;

	t = vec3(
		t.x * 1.61747 + t.y * -0.537248 + t.z * -0.080223,
		t.x * -0.0705739 + t.y * 1.33458 + t.z * -0.264007,
		t.x * -0.0211052 + t.y * -0.226983 + t.z * 1.24809
	);

	color.rgb = t;

	fragColor = TDOutputSwizzle(color);
}

トーンマッピング
"高輝度情報を含んだLinear RGB値""0-1の範囲内でガンマのかかった自然な見た目のsRGBやRec.709" に変換する工程をトーンマッピングと呼びます(多分)。これまではいつも filmic-blender を使っていたのですが、今回半分はLogCのワークフローなので、どうせならということで LogC → Rec.709 のLUTを使ってトーンマップ相当のことを試してみました。

https://www.youtube.com/watch?v=MX5sQs9Nzpw

LUTによってかなり印象が違うのがわかると思います。↑ は有料のLUT集になりますが、butteryluts からいくつかカチカチしてみているの図です。LUTを最後に入れたセットアップを作ってグレーディングの色調整をしている時が一番クリエイティブな気分になれるのを最近発見しました。

問題点

レンダリングのクオリティが上がってめでたしめでたし、という訳にもいかず、問題点も多々ありました。

ディレイが結構ある
Spoutでテクスチャの送受信をしている関係上、一定の送れが発生していました。このディレイは以前はそこまで気になっていなかったので、Spout2Mediaで使っているMedia Frameworkのどこかでバッファが入っているのでは?と思っています。要検証

アセットワークフロー
TDと違ってUEではジオメトリデータをソフト内で簡単に作るのが難しいため、どうしても外部のDCCツール、今回はBlenderとHoudiniを使ってアセットを制作、インポートする必要がありました。
普段そこまで頻繁にDCCツールを使っていないので、こういうの作りたい時どうするんだっけ…? からはじまり、どうやったらUEに効率的にデータをインポートできるのか、シーンのレイアウトはどこでやるべきか、など手探りしながら制作することになりました。

もうちょっと日常的に触るようにするべきだと反省しつつ、最強のワークフローはまだ発見できていません。USDは良さそうなんだけどまだよくわかってない

ノイズ問題
作り始めのころ、鏡面反射をさせようとするとノイズがひどくて何が問題なんだ… と悩んでいたのですが、GI / リフレクションにLumenではなくてStandalone Ray Tracedを使うアンチエイリアスにTSRではなくTAAを使う で解決できました。

GI / リフレクションに関してはおそらくLumenが大規模シーンでの使用にフォーカスしているのでキャッシュがよく効くようになっており、急激な環境の変化のあるシーンには弱い仕様になっているのが原因なのではと推察されます。ただ、Standalone Ray Tracedは (Deprecated) という不吉な文言が入っています。。これからどうなるんだ

アンチエイリアスは単純にTSRのアルゴリズム的に時間軸方向でサンプリングをしているからピカピカしてたらそりゃノイズ乗るよね、ということだと思います。TAAも同様のことは起きているけど比較的目立たなかったというだけかも。NVIDIAのDLSSを使うとよさそうだったのですが、まだUE5.1に対応しておらず見送りとなりました

まとめ

UE5を使ってレンダリングクオリティを上げたいという当初の目標は達成できたのではないかと思思っています。心配してたクソ重いんじゃないかという疑惑も全然そんなことはなく、Geforce RTX 3080 Laptopが80%程度でCPUはスカスカ、といった感じのリソース感でした。RTXはすごい

UEやDCCツールの練度が足りてなく、トラブルシューティングにも時間が割かれてしまったのでパフォーマンス中に手数面でウーンという場面が多かったので日常的に使っていってもっとサクサク制作できたらなと思いました。特にアセットワークフローはさらに研究していきたいです。

最後に、今回制作したプラグイン等々のサンプルプロジェクトがこちらです。それではよいお年を!
https://drive.google.com/file/d/1ff1mp-psDQCMqN28VXdXoRKL1w94zWU3/view?usp=sharing

Discussion