Unreal Engine 5.4のOITを試す

## 概要

Unreal Engine 5でOIT(Order Independent Transparency)が実装されていたことに気づき、試してみました。

半透明のキューブをリアルタイム3DCGで苦手とする意地悪な配置にしましたが、面同士が交差する様を正しく描画できていることがわかります。

![ScreenShot00000.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/75841c54-0f58-3a4b-f153-5abbd7728573.png)

OITを切ると半透明なメッシュはメッシュ単位でしかソートされず、前後関係が狂ったような描画結果が得られました。わかりにくいですが、右、上、左の順に描画されており、面の交差は表現されていません。

![ScreenShot00001.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/f1e130b4-d540-582e-124d-3bc6a55ada11.png)


## 有効無効の切り替え方

OITを利用するには、プロジェクト設定で Enable Order Independent Transparency (Experimental)にチェックを入れます。(再起動が必要)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/f1859c9a-68b1-79b0-8a2c-37bf93e17193.png)

実行時に切り替える事も可能です。 r.OIT.SortedPixels.Enable を1にするとON、0にするとOFFです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/d69f7988-8944-607b-e4c1-b9a81ba93201.png)


## 現在Experimental、UE5.1で追加され、UE5.3とUE5.4で改良された

UE5.4時点でもExperimentalなので、正式に追加された扱いではない点は注意が必要です。リリースノートでOrder Independent Transparency又はOITを検索すると見つかります。

UE5.1でDirectX 12専用として実装されたことがわかります。

https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5.1-release-notes?application_version=5.1

UE5.3でトライアングルソートの精度向上とあります。

https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5.3-release-notes?application_version=5.3

UE5.4で、実行時切り替えがサポートされました。調べてみると確かにUE5.3では r.OIT.SortedPixels.Enable コンソール変数が存在していませんでした。この機能によりゲームの場面により切り替えたり、ユーザーに使用可否を委ねたりできるので、より実用上の要求が取り入れられたと言えそうです。

https://dev.epicgames.com/documentation/en-us/unreal-engine/unreal-engine-5.4-release-notes

ソースコードを"OIT"で検索すると関連するコードが確認できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/1ab854f3-eac1-e834-18b4-96068a7dde71.png)

## ThinTranslucentは描画結果が正しくない

上のキューブの例で、OITをONにした時だけ濃く描画されていることが気になります。発生要因を探るとどうやらThinTranslucentシェーディングモデルを使う時のようです。使用マテリアルはエンジンプラグインに含まれているM_MS_Glass_Materialで、ThinTranslucentでした。

簡単に検証用マテリアルを作って差を確認してみました。

r.OIT.SortedPixels.Enable 0 の場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/8b471787-b242-eec3-801a-487608af734f.png)

r.OIT.SortedPixels.Enable 1 の場合

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/a00f6a88-5ac3-a712-b209-9e54f8b36192.png)

マテリアルエディタ上で見ても既に結果が異なっていることがわかります。

## パフォーマンス

画面上に全く半透明メッシュが存在しない場合においても、GPU Timeの微増が確認できました。stat gpu で見てみるとTranslucencyの変化が顕著です。(RTX 4070で確認)

r.OIT.SortedPixels.Enable 0 の場合 (Translucencyは0.02ms)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/717d6fa9-711f-6a43-114d-238d70ef6a73.png)

r.OIT.SortedPixels.Enable 1 の場合 (Translucencyは0.1ms)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/da491072-58a6-7bd7-cff9-84ba86c6781b.png)


次に、画面を半透明メッシュで覆ってみます。Translucencyは4~5倍の負荷になっていることがわかります。ThinTranslucentは実用上問題がありそうなのでDefault Litの半透明にしています。

r.OIT.SortedPixels.Enable 0 の場合 (Translucencyは0.12ms)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/45236f79-fa41-a176-e2c8-5a6b87fdd9a8.png)

r.OIT.SortedPixels.Enable 1 の場合 (Translucencyは0.57ms)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/b9fcf966-5d98-3691-c142-ef69899ee5fd.png)

## Translucency Pass毎にソートされるがTranslucency Passを超えてソートされない

マテリアルのTranslucency Passプロパティの設定項目である、Before DOF, After DOF, After Motion Blurはそれぞれのパスの半透明ピクセル同士でソートされます。しかしそれぞれのパスはBefore DOF→After DOF→After Motion Blurの順に描画されており、この描画順はOITの使用に関わらず入れ替えることはできません。
![ScreenShot00002.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/66160/1fd7e0e3-e08b-662e-14bd-0653d617ccb9.png)
灰色のキューブがBefore DOF, 青のキューブがAfter DOF(デフォルト), 赤のキューブがAfter Motion Blurです。全てのキューブは同じ大きさで、青と赤のキューブは灰色のキューブの奥に配置しているのにも関わらず灰色のキューブの手前に描画されています。しかし、同じ色のキューブのピクセル同士はソートされていることがわかります。