UE4のDecalを掘り下げる

　この記事は[Unreal Engine 4 (UE4)その２ Advent Calendar 2018](https://qiita.com/advent-calendar/2018/ue4_zwei)の３日目の記事です。

# はじめに
　この記事ではUE4のデカールについて掘り下げてみたいと思います。
　デカールのブレンドモードにはたくさん種類がありますが、実際の描画がどのように行われているかを知ることで、それぞれの利点欠点が見えてきます。
　使用しているUE4のバージョンは4.20ですが、後で4.21にも触れます。正確には4.20で検証した結果をもとに4.21で書き始めた記事です。

# ディファードデカールとDBufferデカール

　UE4のディファードデカールには多くのブレンドモードがありますが、内部的には大きく分けて２種類のデカールが実装されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/b368a5a0-0c65-9146-167a-9856718effa6.png)
　後ろの３つはちょっと特殊なデカールなので、こちらには触れません。
 公式ドキュメントは[こちら](http://api.unrealengine.com/JPN/Resources/ContentExamples/Decals/1_2/)

　前の４つが通常のディファードデカール、DBufferで始まるものがDBufferデカールです。ここでは通常のデカールを「ディファードデカール」DBufferを使うものを「DBufferデカール」と呼ぶことにします。それぞれについて簡単に特徴をまとめます。

## ディファードデカール

- GBufferにデカールを貼り付けます。なのでディファードライティングでのみ使用可能
- 特別なバッファを使用しないのでメモリにも負荷にも比較的優しい
- 間接光の描画後に描画されるので、間接光はデカールを貼る前の状態のままになる
- メタリックやスペキュラを書き換えることができる

## DBufferデカール

- フォワードでもディファードでも使用できる
- DBufferという専用のバッファを作成し、それを使って描画する。なのでスクリーンサイズの３枚のバッファを必要とするので、比較的コストが高い
- 間接光が正しく影響する
- メタリックやスペキュラは変更できない。というかメタリックは0、スペキュラは0.5に設定される

　このように、ディファードデカールとDBufferデカールにはいろいろ違いがありますが、この違いはUE4の描画の順番に大きく関わります。このあたりはUNREAL FESTでも触れましたので、こちらも参考にしてください。
[UE4のライティング解体新書](https://www.slideshare.net/EpicGamesJapan/ue4festeast2018lighting-119947119)

## ディファードデカールを貼ってみる

　オーソドックスなディファードデカールを作成してみました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/ddaeaa8c-39a0-5a8a-c132-64de38532129.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/f0872699-7761-6ffd-428f-91eb0ffc23c3.png)

　で、実際にレベルに貼ってみると・・・
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/913b3f7c-ff34-4f37-9b5a-c9f95fb00c2c.png)

　うん？なんだか半透明？

　バッファの状況を見てみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/4f65b2d0-00bb-2817-7081-f1a8b17c46e9.png)

　カラーも法線もラフネスも書き込まれてますよね・・・

　種明かしをすると、これ、間接光が透けて見えてるのです。
RenderDocでBasePassが終了した状態のバッファを見てみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/bfeb1644-bd8c-32b4-1f4f-3a8788c82ceb.png)

　グレーの床に青い空が反射した青い色になっています。これが間接光だけの状態。つまりは直接光、動的ライティングが当たっていない影の中です。

　ディファードデカールは間接光の描画が終わったあとでGBufferに格納されている動的ライティングが使用するパラメーターを書き換えます。つまり、デカールが貼られる前の床の色で計算された間接光の上にデカールの色でライティングした結果を合成するので半透明のように見えてしまうのです。

　具体的にはこんな順番でシーンが描画されています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/b745e7df-caf9-77bf-ae29-9958e98f6d4c.png)

　ディファードデカールが期待したように描画されない原因はほぼここに集約されます。つまり、**間接光と直接光の計算時の色が違う**という問題です。

　地面にテクスチャを貼るとよりわかりやすいですね。模様が透けてます。そして影の中はそもそも動的ライティングがされない、間接光のみの状態なので、色も付きません。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/2e718a97-b3b5-1aaa-36b0-f98b6d6947a4.png)

　・・・でもなんかディテールは見えてますね。ライティングされないなら影の中には何も描画されないのでは？

![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/aecf1ad8-e1e6-6ff2-0a9d-8f4b2f923d42.png)

　SpecularとAOを切ると、はい、完全に何もなくなりました。
　Specularは影の中では計算されないのですが、Reflectionは影の中にも適用されます。そしてReflectionはSpecularをもとに計算されます。Specularが無ければReflectionも描画されません。Specularを有効にしてReflection EnvironmentとScreen Space Reflectionを無効にしても同じ結果になります。
　またAO、アンビエントオクルージョンはライティングとは別に法線情報をもとに計算されるので、デカールによって更新された法線の影響を描画します。
　AOだけ有効にするとこんな感じ
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/25ea24d4-a223-1d05-5b09-775d4513c4ed.png)

　スペキュラを切ると、デカールの光があたってない部分がデカールを貼る前の状態の影と同じ状態になっているのが良くわかります。

## ディファードデカールのまとめ

　結論を言うとディファードデカールはかなり使えません。色が半透明状になること、影の中で正しく描画されないことを理解した上でなら使えます。GBuffer情報を書き換えるためのものと割り切って、法線、メタリック、ラフネス、スペキュラーを書き換えるものとして使うか、半透明オーバーレイ的なものとして使うと良いでしょう。
　ただし、後述のDBufferデカールと違って、MetallicとSpecularにも書き込めるので水に濡れた表現を足したい場合には便利です。

# DBufferデカール
　前述のとおり、ディファードデカールにはいろいろ弱点があります。それを解消するべく追加されたのがDBufferデカールです。
　DBufferという別バッファに一旦描画して、BasePass時に適用されるため自然な描画が可能になっています。その反面、フルスクリーンサイズのバッファを３枚使用するので、メモリ、描画負荷にインパクトがあるので注意が必要です。低スペックなハードではDBufferのクリアだけでも無視できないコストになるかもしれません。

　それでは先程のデカールマテリアルのブレンドモードをDBuffer Trancelucent Color,Normal,Roughnessにしてみましょう。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/4c007503-1cb6-2fbc-163e-d55944291a8e.png)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/d22cfeed-24bd-9734-e8a5-e66bec68d8c5.png)

　透けずにちゃんと色が出ています。影の中も正しく描画されています。完璧ですね。

　RenderDocで描画の過程を見ていきます。

　描画順はこんな感じです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/43dbc08c-e8cf-ff0a-9f72-099ce4b8756a.png)

- BasePassの前にDBufferをクリアしてDBufferにデカールを描画
- BasePassでDBufferを参照しながらデカールを合成しつつGBuffer構築、間接光を計算
- ライティング

　という流れです。

　DBufferを見てみましょう
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/84a563c8-2013-8bda-1176-9447d2248c7b.png)

　DBufferCにカラーとオパシティ、DBufferBにノーマル、DBufferAにラフネスとオパシティが書き込まれています。一昔前のハードだと「なんて贅沢な」と思ってしまいます。
　BasePassが終わってから描画されて動的ライティングにだけ影響を与える通常のディファードデカールとは違い、BasePassの前に描画され、BasePassで合成されてから間接光、直接光の計算が行なわれるので正しい描画結果が得られるわけです。

　ですが、ひとつ不満があります。DBufferデカールのブレンドモードにはRoughnessはあってもSpecularとMetallicがありません。では実際の描画ではSpecularとMetallicはどうなってるんでしょうか。シェーダーコードを覗いてみましょう。ちょっと事情があってまずは4.20のコードから。

```DBufferDecalShared.ush
// 4.20
	BaseColor = BaseColor * DBufferData.ColorOpacity + DBufferData.PreMulColor;
	Metallic = Metallic * DBufferData.ColorOpacity + 0;			// decals are always no metallic
	Specular = lerp(0.5f, Specular, DBufferData.ColorOpacity);		// most non metal materials have a specular of 4% which is 0.5 in this scale
```
　Metallicは0に固定、Specularは0.5に固定になっています。
　なるほど・・・まあ、無難な実装だよね・・・と思わなくも・・・
　でも、「水たまりとかつくりたいし、Metallicもいじりたい」と思って、仕事で使用しているUE4 4.20は勝手に拡張してMetallicもSpecularもいじれるように改造していました。

　で、この記事にその改造の仕方を書こうかな・・・と思ってたのですが、4.21のシェーダーコードを見てびっくり。ブレンドモードにはRoughnessしか記載が無いのにMetallicとSpecularも反映されるようになってました。

```DBufferDecalShare.ush
// 4.21
	Roughness = Roughness * DBufferData.RoughnessOpacity + DBufferData.PreMulRoughness;
	Metallic = Metallic * DBufferData.RoughnessOpacity + DBufferData.PreMulMetallic;
	Specular = Specular * DBufferData.RoughnessOpacity + DBufferData.PreMulSpecular;
```
それぞれDBufferに書き込まれた値をもとに計算されています。

　Metallicに1を入れてみました。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/147177/331afb59-8fee-d4ff-283b-e82c00b9da8b.png)

　ちゃんと金属っぽくなってます。

## DBufferデカールのまとめ
- 正しい色で描画できる
- 4.21からはMetallicとSpecularも変更できる
- 専用のバッファを使用するのでメモリ消費と負荷が高い

　MetallicとSpecularが使えるようになったのは良いのですが、実はそのためにDBufferのサイズが大きくなっています。4.20までは32bit,32bit,16bitの構成で、３枚目のDBufferはRoughnessとOpacityだけの16bitバッファになっていましたが、4.21ではMetallicとSpecularも格納され、32bitバッファに変更されています。プラットフォームによってはそれなりに影響があるかもしれません。

　ちなみにDBuffer Decalの描画ですが、視界に入っていなければ描画は行われませんが、シーンにDBuffer Decalを使用するActorが存在するとDBufferのクリアは毎フレーム行われるので注意が必要です。

# デカールまとめ
　UE4のディファードデカールは便利ですが、通常のデカールは描画に制限があります。4.21でMetallicとSpecularも使えるようになったので、「じゃあDBufferデカールで良いじゃない」と言いたいところですが、DBufferデカールはメモリと負荷を心配ながら使う必要があります。
　フルHDでの描画を考えると1920x1080の32bitバッファ3枚を確保してクリア、描画、そしてそれを参照しながらBasePassとなかなか無視できない負荷になります。
　それぞれの仕組みを理解して上手に使い分けましょう。
　
　改造の記事書こうとした矢先に4.21で実装されちゃってたのでちょっと尻すぼみ感でしたが、以上、「UE4のDecalを掘り下げる」でした。

明日はスミオさんの記事です。いつもごちそう様です（こら）
どんな記事か楽しみですね。