UE5.5で追加された待望の機能、UserSceneTextureを使って作るポストエフェクト

久しぶりの投稿です。

今年は転職の年でした。新しい職場ではUnrealEngineを用いてレイトレやLumenのハイエンドでフォトリアルな表現とスタイライズドな表現の共存させ、互いを引き立たせる仕事をしています。

# UE5.5で入った新機能
さて、表題の件ですが、先日リリースされたUE5.5で長年のUEユーザー待望の機能が実装されました。

その名もUserSceneTextureです。
なんとポストエフェクトの結果をユーザー定義の中間テクスチャに描きこむ事が出来きます。
これまでエンジン改造をしなければ実装できなかったあんな機能やこんな機能を実装する事が出来るようになったのです。

公式が無改造エンジンを[素うどんと称する](https://x.com/UnrealEngineJP/status/1831547324142878937)くらいなので、往年のUEエンジニアは息を吸うように日々改造している思いますが、メンテコストが減るのはうれしい物です。標準で使えるものはどんどん使っていきましょう。

# 使い方を見てみる
UserSceneTextureの実装はとてもシンプルで、片手で足りる手順にて使用する事が出来ます。

## 書き込み
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/e36098d8-0e2a-ee3a-5a49-47dfd0827a7e.png)
一番シンプルな使い方はUserSceneTextureに任意の名前を入れるだけです。
これだけで書き込み先が次のポストプロセスの入力ではなく、指定した名前の一時バッファになります。

その他のオプションとしてはUserTextureDivisorでダウンサンプリングの指定をしたり、Disable Pre Exposure ScaleでPreExporsureを掛けるかどうかの選択ができます。

## フェッチ
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/8ca9e8e5-879c-4f03-9709-a53e02cfeaf0.png)

フェッチ時は書き込み時に指定した名前を指定する事で取得できます。
フィルタリングの指定とクランプするかどうかの指定ができます。

### シェーダーコード内でのフェッチ
ほぼSceneTextureと同じですね。
```
float2 uv = ClampSceneTextureUV(ViewportUVToSceneTextureUV(DERIV_BASE_VALUE(inUV), PPI_UserSceneTexture0), PPI_UserSceneTexture0);
float3 tex = SceneTextureLookup(uv, PPI_UserSceneTexture0, false).xyz;
```

# 機能の検証として何を実装するか
では実際にこれらの機能を使って何かサンプルを作る際に、真っ先に思いつくのはガウスブラーを縦横分離して適用する事です。
しかしながら、この実装はわざわざバッファを退避させる必要がなく、これまでの仕様でもやろうと思えばできていたので今回は見送ります。

## SDF
そんな中思いついたのがSDFをリアルタイムで表示する事です。
今回はステンシルで作成したプレイヤーのマスクをもとにSDFをリアルタイムで作った紹介をします。


### SDFのUE実装
今回は[ながさんのUE実装](https://github.com/nagakagachi/NagaSdfTextureToolForUE)を参考にリアルタイム化しました。
5.3ベースで書かれており、わかりやすいコードです。
テクスチャフェッチ部分をUserSceneTextureに置き換えていくだけで動きます。
テクセルサイズや解像度なども同ノードから取得します。

大まかな処理の流れは以下です。
ステンシルに描かれたキャラクターのマスクを作業バッファへコピー
↓　
SDFセットアップ
↓
SDFのイテレーション
↓
出来上がったものを作業バッファからコピー

画像の通りパス毎にマテリアルを積みます。
イテレーションの部分は2つのバッファを交互に再利用します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/72232821-234d-aa31-4bc8-9b65ef509e16.png)

### 実行結果
それっぽい結果が得られました。
![output-palette.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/cee5714c-db4a-6d46-82e0-952528ffab41.gif)

# 応用してみる
せっかくSDFが生成できたので、SDFと相性のいいスタイライズドな表現を２つほど紹介します。

## アウトライン
指定した距離でアウトラインカラーとブレンドするのみの簡単な実装です。

法線や深度を必要とせずに描くと以下のメリットがあります。
- 境界の中心を通る線を引くことが出来る
- 意図せぬ立体感を感じにくい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/38b64272-173c-24aa-72ca-346e7df92c3f.png)
もちろん画角やカメラとの距離を変えても太さは変わりません
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/4556f8f7-0da3-4174-0055-ec7f14edd64d.png)

## エッジのハイライト(2D的な)
マスクと比較して体の内側方向だけを見ます。
リムライトやフレネルを利用した同等の表現に比べ、意図的に立体感を消したハイライトをつける事が出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/931a67d8-66f0-99f1-2196-e8be2f37a389.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/502600/d0d0330a-7668-b3f3-a380-bb5c2efab5c7.png)

# まとめ
UserSceneTextureによってこれまで実装するのに心構えが必要だったものを気軽に作れるようになりました。
動的解像度との相性の検証など、ポストエフェクトの後半で解像度が変わった際にどういう挙動をするのか詳しく追えていないので機会あればまた共有したいです。

# 参考
[5.5リリースノート](https://dev.epicgames.com/documentation/ja-jp/unreal-engine/unreal-engine-5.5-release-notes)
[ロードマップ](https://portal.productboard.com/epicgames/1-unreal-engine-public-roadmap/c/1666-render-pass-improvements)
[NagaSdfTextureToolForUE](https://github.com/nagakagachi/NagaSdfTextureToolForUE)