UE5.4の半透明について

# UE5.4の半透明
　UE5.4から半透明の描画順などに色々と変更が加わりました。アーティストにも混乱が見られるのと自分でも整理しておきたくなったので調査してまとめてみたいと思います。半透明は闇が深いです。

:::note warn
　この記事はデフォルト設定のUE5.4をベースにしています。プロジェクト設定や将来のバージョンで大きく状況が異なる可能性があります。例えばForward RenderingやMobile Rendererでは色々違う話になります。
:::

# UE5の半透明描画
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/0baaf67b-a4e8-f2eb-160b-c21401b5e4a9.png)

　まずUE5の半透明描画について説明しておきます。実際にはもうちょい複雑ですが単純化した解説です。
　通常の不透明(Opaque)描画と半透明描画の最大の違いは半透明で描画されることです（ナントカ構文）
　不透明描画は色と同時に深度情報も書き込まれます。そして深度情報を比較しながらポリゴンの重なりを判定し、より深度が浅い、つまり手前のピクセルを更新していきます。

 :::note info
 実際には描画負荷を軽減するために先に深度情報だけ描画しておいて重なりの情報を作成しておき、実際の色を描画する際には重なっていない一番手前のピクセルだけが描画される仕組みになっています。Depth Prepassという手法です。
:::
　一方、半透明は深度情報を描画しません。深度情報は画面内のピクセルと一対一なので、複数のピクセルが重なり合い透けて見える半透明の場合では深度情報を持つことができません。そのためあらかじめポリゴン単位やメッシュ単位でソートされ、奥から順番に描画されることになります。結果重なり合ったすべてのポリゴンが描画されるため描画効率が悪く、描画負荷が高くなりがちです。オーバードローと言ったりします。また、半透明で描画するということは描画ターゲットを読み込んで合成してまた書き込むという読み書き両方の動作が必要というのも描画負荷を高める要因です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/9d463584-8408-ef0f-c186-27ee88233862.png)

　UEの半透明描画は不透明なプリミティブの描画が完了した後に半透明なプリミティブだけまとめて描画されるのですが、描画ターゲットに直接描画される場合と一旦別バッファに描画して、その結果をまとめて描画ターゲットに合成する場合と２種類の描画タイプがあります。後者はSeparate Translucencyなどと呼ばれていて、このときに使用される別バッファの解像度を下げることで描画負荷を下げる事ができたりします。どのタイプで半透明を描画するかはマテリアル単位にTranslucency Passを設定することで選択可能です。
　半透明の描画問題はゲームグラフィックス黎明期からずっと存在し、実はあまり進歩していません。基本的に制約が多く不完全な描画になります。OITなど新しい技術は生まれてきていますが、実際には描画が重くなったり大量のメモリが必要になったりでリアルタイム描画ではあまり使われることが無いのが実情です。半透明とレイトレとの相性も悪いですし。
 ## 半透明マテリアル
 　UEの半透明マテリアルはBlend ModeにTranslucentを設定することで使用可能になります。基本的にこれだけ設定すれば半透明で描画されるのですが、注意するべきパラメーターが他にもいくつかあります。
　特に注意が必要なのは前述のTranslucency Passです。このパラメーターは半透明がどのタイミングで描画されるかを設定します。デフォルトではAfter DOFに設定されていますが、Before DOFとAfter Motion Blurを設定することができます。それぞれどのような違いがあるかを説明します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/3a4e24d0-9f0e-f03b-a981-5dbe1eb9f14b.png)
　半透明のモードが分かれているのは主にDOF（被写界深度）と半透明の関係を設定する目的です。被写界深度処理はフォーカスポイントから離れた深度のピクセルをぼかすことでピントがあった部分はくっきりと、ピントが外れた領域はボケて奥行きのある、カメラで撮影したような画像をつくるのが目的です。
　ここで半透明が問題になるのは、半透明なピクセルは深度情報を持たないため、半透明が描かれる前の不透明ピクセルの深度情報をもとにした被写界深度処理がかかってしまうことです。この場合半透明プリミティブはピントがあった距離に存在したとしても、ボケてしまいます。それを避けたい場合は被写界深度処理のあとに半透明を描くしか無いのですが、それはそれで今度はピント位置にかかわらず全くボケが無い状態で描画されます。
　以上を踏まえてTranslucency Passの種類を説明します。
 ### After DOF
 　DOF（被写界深度）処理の後に半透明を描画します。このマテリアルにはカメラの距離にかかわらず被写界深度がかからないということになります。

### Before DOF
　被写界深度処理の前に半透明描画します。半透明で描画される前の深度に従って被写界深度がかかるため、このマテリアルにピントが合っていたとしても容赦なくボケる可能性があります。
　またこのモードのみ、ディストーション（屈折）の前に描画されます。半透明領域と屈折領域を一致させたい場合はこのモードを使う必要があります。また、このモードは通常は別バッファを使用せずに描画ターゲットに直接半透明描画されます。

### After Motion Blur
　被写界深度処理のあと、モーションブラー処理の後に半透明として描画されます。このモードにはいろいろ注意が必要です。以下に問題を挙げていきます。
- エディタのViewと実行時で見た目が違う
- アップスケール後のバッファに合成されるので処理が重くなる
- TSRなどのアップスケーラーが適用されずに拡大処理で合成されるのでアップスケール倍率によっては見た目が汚くなる
- 深度情報無視して合成されるのでシーン中の不透明プリミティブの後ろに描画することができないし、貫通して見える
　などなど。これらの問題を踏まえたうえで使用する必要がありますし、基本的には特殊な場合を除いて使用するべきでは無いのかなと思います。
　エディタのViewと実行時に見た目が変わるのは、エディタView画面は通常TSRなどのアップスケーラーが適用されないため、描画順などが実行時と違ってしまうのが原因です。その関係でエディタViewではこのモードのマテリアルにBloomがかからなかったりしますが、実行時にはBloomが適用されます。
　深度情報を無視するのはアップスケール後に描画されるのでアップスケールされた深度情報というものが存在しないので仕方ないのかな。
　ではAfter Motion Blurを使うのはどんなときでしょうか。上記の問題を無視してもどうしてもモーションブラーをかけたくない半透明ということになります。格闘ゲームのヒットエフェクトとかUMGで描画したくないHUD系UIとかでしょうかね。あまり思いつきません。

:::note info
半透明描画バッファのサイズはr.SeparateTranslucencyScreenPercentageで変更可能です。50を設定すると縦横半分サイズのバッファが使用され、半透明描画の負荷を大きく下げることが可能です。しかし、当然縮小バッファを引き伸ばして描画されるのでクオリティは下がります。
また、この設定を100以外に設定するとBefore DOFのマテリアルも縮小バッファ経由で描画されるようになります。
:::

## 半透明とディストーション（屈折）

　ディストーション（屈折）にも色々トラップがあります。まず正しく屈折を表現できてるかというと、あくまでも擬似的で、屈折率と法線の向きに従って画面の別の部分を参照するものなので、屈折先が画面外だったりするとその部分が欠けたりしますし、重なり合った屈折などは正しく表現できません。まああくまでも擬似的なものとして使うのが良いです。レイトレーシングを使えば正確な屈折を表現できますが、まだまだゲームには気軽に使えません。
　もうひとつ大きな問題は、Before DOFの場合は半透明を描画してから屈折処理が入るので描画領域と屈折領域がずれてしまいます。
　Before DOFの方は半透明で暗くなった上から屈折処理が入るせいで半透明領域が屈折によって歪んだ状態になっていますが、After DOFの方は屈折した領域と同じ範囲に半透明が描画されているのがわかります。
 
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/2dfb29d3-e177-a909-2f43-83d6381a1684.png)

## 半透明と屈折と被写界深度
　もうひとつ、半透明と被写界深度で注意することがあります。被写界深度の焦点距離(Focal Distance)より遠くにあるActorが自動的にBefore DOFとして描画される機能があります。そのため前述のように屈折が設定された半透明マテリアルの半透明と屈折の領域が一致しない問題が意図せずに発生してしまいます。この機能はr.Translucency.AutoBeforeDOFを-1など負の値にすることで無効化することができます。Experimentalな機能みたいですが、デフォルトで有効にするのはやめて欲しいと思ったり。


# 半透明に関する結論
　UE5最新になっても半透明描画はいろいろ問題と制約があります。仕組みをよく理解して計画的に使用することが重要です。そしてある程度の割り切り（あきらめ）も必要です。とくにAfter Motion Blurは色々厄介な問題を孕んでいるので注意して使いましょう。

# おまけ

## ポストプロセスマテリアルとBlenable Location
　半透明とは直接関係無いかもしれないのですが、DOFやBloomのタイミングの話のついでにポストプロセスマテリアルのBlendable Locationについてもお話しておきます。
　Blendable Locationとはポストプロセスマテリアルを描画するタイミングです。これが5.4で大きく変わりました。
UE5.3までのBlendable Location
  ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/f1a3f0ae-c9c3-f4be-f69a-e7431582c00b.png)

UE5.4のBlendable Location
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/147177/080d1bbd-9f0d-ca64-b372-d57ed0f39b64.png)

UE5.4での半透明とポストプロセスマテリアルとDOFとBloomなどの描画順はこんな感じみたいです。

半透明(Before DOF)
ポストプロセスマテリアル(Scene Color Before DOF)
DOF
半透明(After DOF)
ポストプロセスマテリアル(Scene Color After DOF)
ポストプロセスマテリアル(Translucency After DOF)
TSR(アップスケール）
Motion Blur
半透明(After Motion Blur)
ポストプロセスマテリアル(Scene Color Before Bloom)
Bloom
Tonemap
ポストプロセスマテリアル(Scene Color After Tonemapping)

Scene Color After DOFとTranslucency After DOFの違いはInput0の入力が違うみたいです。以下ソースコードの引用です。

```BlendableInterface.h
	/** Post process material location to modify the scene color, between DOF and AfterDOF translucency.
	 * Always run at rendering resolution.
	 * Inputs and output always in linear color space.
	 *
	 * Input0:former pass scene color, excluding AfterDOF translucency
	 * Input1:AfterDOF translucency.
	 */
	BL_SceneColorAfterDOF = 1 UMETA(DisplayName = "Scene Color After DOF"),

	/** Post process material location to modify the AfterDOF translucency, before composition into the scene color.
	 * Always run at rendering resolution.
	 * Inputs and output always in linear color space.
	 *
	 * Input0:scene color without translucency, after DOF,
	 * Input1:AfterDOF translucency.
	 */
	BL_TranslucencyAfterDOF = 5 UMETA(DisplayName = "Translucency After DOF"),
```

　UE5からTSR(Temporal Super Resolution)という技術が入ってきました。超解像などとも呼ばれますが、低い解像度で描画した結果をあたかも最初から高い解像度で描画したように高精細に画像を拡大する技術です。コンソールゲーム機やミドルクラスのPCで4Kでゲームをプレイするためには当たり前の技術になってきています。類似の技術にNVIDIAのDLSSやAMDのFSR、IntelのXeSSなどがあります。これまではフルHDで作られてきたゲームが4K対応となると縦横２倍の４倍の面積を描画する必要があるのですが、まじめに描画しようとすると数十万円するハイエンドのGPUが必要になってしまいます。それをそこそこのGPUでも4K相当で描画できてしまう素晴らしい技術です。
　素晴らしいのですが、描画途中で解像度が変わるというのは色々難しい対応が必要だったりもします。UE5.4ではアップスケールの後にBloomが描画されるようになりました。4K出力の場合Bloomも4Kで描画される事になるので、これに気をつけないとけっこう描画パフォーマンスの低下につながりそうです。
　要はTSRの後は描画解像度が高くなるのでその分描画負荷も高くなるので、どうしても必要でなければTSRの前に描画処理を行うべきだということです。

# 終わりに
　駆け足でUE5.4の半透明事情をまとめてみました。半透明は本当に厄介です。とは言え使わないわけにはいかないのでなるべく仕組みを理解しつつ慎重に使いたいものです。特にどの設定でどのタイミングでどの解像度で描画されるかはとても重要です。
　実際にどういうふうに描画されているかをRenderDOCとかdumpgpuで見てみるのが一番です。特にdumpgpuオススメです。