【UE4】ポストプロセスで作るアウトライン

# 最初に

どうも、ろっさむです。**

今回は以下のUE4の学習コースでUE4でのポストプロセスを用いたアウトラインの作り方を学んだので自分用に残しておきます。
https://www.unrealengine.com/ja/onlinelearning-courses/stylize-renders-with-post-process-materials

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/95c2ebcc-3b83-2bec-680d-1b1efb6cde52.png)



# シーンの深度の確認方法

作成には部分的に**シーン深度Gバッファ**からの描画を利用するため、まずはゲームシーンの深度が確認できる方法を見ておきましょう。。

以下のように設定すると、**暗い値は近くのもの、明るい値は遠くのもの**を表すようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/fcf345bd-3bef-db38-9401-7774d8cd3148.png)


# マテリアルで行う

まずはマテリアルの `Material Domain`を`Post Process`に変更しておきます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/65e039e7-c297-91a6-0ce7-a889b4f21ab6.png)

次に`Details > Post Process Material > Blendable Location`を`Before Tonemapping（トーンマッピング前）`に設定します。

ちなみにここの項目の設定一覧は以下の通りです。

- `After Tonemapping（トーンマッピング後）`がデフォルトGバッファをポストプロセスパイプラインの最後にローダイナミックレンジ空間から抽出する。
- `Before Tonemapping（トーンマッピング前）`はベースカラーをハイダイナミックレンジ空間から抽出する。
- `Before Translucency（透過処理前）`は画像に透過処理が適用される前に行われる。
- `Replacing the Tonemapper（トーンマッパーの置き換え）`はトーンマッパーを完全に無効にする。
- `SSR Input（スクリーンスペース反射入力）`はGバッファなら何でもスクリーンスペース反射として使用する。

## SceneTexture:SceneDepth

<img width="227" alt="image.png (23.3 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/b563bbb0-0baa-42b7-9400-1914a5e3b43b.png">

- Size:画像の解像度
- InvSize:エッジ検出やソーベル演算を可能にして、アウトラインを描画するためのもの。

UE4の画面空間ではUV座標が赤と緑のベクターに格納されています。InvSizeにRGだけの2ベクターを乗算すると、ピクセルのレンダリング先を操作できるようになります。この2成分ベクターがシーン深度とワールド法線情報を何ピクセル分オフセットするのかを変更できます。

<img width="361" alt="image.png (44.3 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/20462b84-766d-4464-9064-c4a5cd7891c5.png">

**UV座標として認識させるためにはテクスチャ座標ノード(`TexCoord`)を追加**します。また変更前のシーンデプスから減算する必要もあります。繋げると以下のようなノードになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/72d519f7-bfbf-34dc-d876-08e23d109706.png)

この状態のマテリアルを適用すると以下のような状態になります。

<img width="463" alt="image.png (255.2 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/6f173c3f-e7fb-47d2-a832-20054b543def.png">

他にも、ベースカラーを黒い値の場所にレンダリングする必要があります。そのために線形補間ノードを作成します。

## 法線マップで行う場合

ワールド空間法線を使用する方法です。法線マップを選択する理由は、**ワールド空間の法線マップはオブジェクトの移動ではなくカメラとともに移動するので、正しいエッジ輪郭を取得できる**からです。

`Lit > WorldNormal`で表示される画像は全法線マップの合計となります。ワールド空間法線が表しているのはマテリアルに適用されているタンジェント空間法線マップを計算した後のワールド内で特定のピクセルが向いている方向（つまりベクター）で、**RGBはXYZ**となります。

このデータはレンダリング段階で計算され、ディファードレンダラによってライティング計算のGバッファとして使用されます。このGバッファデータをポストプロセスマテリアルで使用してシーンの法線に効果をレンダリングできます。

ロジックとしてはシーン深度のものと全く同じものを使用する。

`SceneTexture:WorldNormal`の`Scene Texture Id`を`WorldNormal`に変えることで法線の指定部をシェーディング可能となります。

アウトラインは黒なので最後に彩度を下げる処理を入れましょう。

<img width="232" alt="image.png (25.6 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/352e0fd1-c829-4628-bee5-922561045124.png">

`Fraction`が0だと彩度は低下せず、フルカラーになります。1だと完全に低下します。

## 画像のちらつきに対処

ちらつきは**ポリゴンライン**が原因となります。

ライティングのみにすると諸々確認しやすくなります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/36899/333ecfbb-a19e-d8de-1215-5023e65a7c17.png)

ポリゴンラインの数値に対して、乗算を行い、画面に対してどの程度効果をかけるかを決めることができます。（掛ける値をパラメータに変換することで、リアルタイムでレンダリングの確認を行うことができます）

<img width="626" alt="image.png (56.0 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/9459ce70-5e53-4741-9755-d25a7e3c8ab9.png">


### コントラストを追加する

アウトラインをクリーンアップすることができます。**コントラストは`Power（累乗）`ノード**を使用しましょう。これは指数関数演算で、基数を受け取り指数乗します。このノードがコントラストの処理に最適な理由は数学演算の方法にあります。どのような値が基数に設定されても指数によってどのていど基数が暗くなるのか明るくなるかが決まるようです。基本的に暗い値はより暗く、明るい値はより明るくなります。程度は指数によって決まってきます。

<img width="349" alt="image.png (28.7 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/56b5f330-117f-4d80-9981-34d7912a2848.png">

### 太さを調節する

Gバッファのルックアップ時のオフセットの大きさを操作します。現在の状態だとGバッファをそれぞれの方向に1ピクセル移動してポリゴンと法線の上下左右にラインを描画します。この移動数値を変更することでアウトラインの太さを変更できます。

<img width="603" alt="image.png (76.2 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/0b2c5a85-7db3-461c-8989-a45a41456493.png">

### 指定のアセットにのみ効果を適用する

キャラクターをマスクしてベースカラーだけをレンダリングし、環境にはPBRライティングでレンダリングします。

ここでは`SceneTexture:CustomDepth`を使用します。

<img width="247" alt="image.png (24.2 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/45b189c4-c203-4d37-9f18-6141a0dc74c4.png">

このノードで可能なのは`Custom Depth`トグルがオンの状態でレンダリングされるオブジェクトを検索してそのアセットだけをレンダリングすることです。トグルをオンにするにはステージ上のスケルタルメッシュの`Details`から`Custom Depth`を検索します。

<img width="375" alt="image.png (12.6 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/4958b1da-5ce7-4acd-8248-3962bf7d3c28.png">

このノードを使って分離し、選択的にレンダリングできます。

カスタム深度からシーン深度を減算するとキャラクターのマスクができあがります。

深度を逆にするには`OneMinus`を使用しましょう。このノードは基本的に反転に使います。

<img width="561" alt="image.png (70.8 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/c62ac32c-e82c-4313-a515-8977a08ed4ad.png">


マテリアルの`Blendale Location`を**`Before Translucency（透過処理前）`に変更する**必要もあります。被写界深度はレンダリングに透過処理を必要とするようです

アウトライン処理の最後にこの処理を付け足します。

<img width="622" alt="image.png (111.1 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/6ea84349-b075-4917-b3b5-556f1e101944.png">

アウトラインの太さを変えた場合、輪郭部分は変わらずメッシュ内部の境界線だけが太くなります。

<img width="321" alt="image.png (84.7 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/cf0ad634-c559-4082-977b-b236d6234d97.png">

理由としてはマテリアルに張り付けて効果上にオーバーレイするアルファとして使用しているマスクがポリゴンアウトラインのオフセットと連携していないため。連携させると、オフセットの値（アウトラインの太さ）に応じてキャラクターのマスクが収縮したり増幅し、アウトライン自体も変化します。

<img width="448" alt="image.png (211.6 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/c5a9fed1-8ee2-40bb-a8b4-2084d05b31f8.png">

アウトラインを被写界深度の下にレンダリングしたい場合は、マテリアルの`Blendable Location`から`Before Translucency`に変更しましょう。

<img width="461" alt="image.png (194.4 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/188958e6-e5c7-4ab8-8d91-e941b7a01191.png">

これでキャラクターだけにアウトラインを実装するマテリアルができました。

またこのポストプロセスではマスク外の周囲の環境が暗くなってしまうのでその場合にはマスク外の環境を乗算して明るくしてください。

<img width="528" alt="image.png (47.2 kB)" src="https://img.esa.io/uploads/production/attachments/10908/2020/04/27/72406/f9969907-0498-4891-bddb-b235579df027.png">