今回は主にポストプロセスで利用するCustomDepthについて解説し、簡単なサンプルを作成してみました。
ついでに、みんな知ってると思いきや意外と理解されてない深度バッファについても簡単に解説しようと思います。
深度バッファくらい知ってるよ、って人は多いと思うのですが、開発者の中でも知らない、正確に理解してない人が意外といます。
たいてい問題になるのは半透明関連なのですが、その際に、こんな風になるんだけどなんで?とか言われて、また説明しなきゃいけないのか?どうやって説明したら納得してくれる?と頭を抱えることが結構あります。
なので、不要な気もするけど説明しようかと考えた次第です。
では、深度バッファについて最初に解説しましょう。
深度バッファはカラーを描画するフレームバッファと同じ大きさのバッファで、各ピクセルのスクリーンに対する奥行きを保存しておくバッファです。
これを保存しておく主な理由は遮蔽されているポリゴンを描画しないようにするためです。
下の図は2枚のポリゴンがスクリーンに表示されている状態ですが、赤と緑のどちらが手前に存在するポリゴンでしょうか?
左であれば赤が、右であれば緑が手前側のポリゴンと判断されるはずです。
ここまではいいですよね?
このような状態を正しく処理する方法として、ポリゴンを奥から順番に描画するという方法があります。
しかし、その方法ではポリゴンとポリゴンが突き抜けている状態は表現できません。
そこで出てくるのが深度バッファです。
深度バッファには各ピクセルごとの深度を0.0~1.0に変換した値が格納されます。
格納されるのはカラーピクセルが描画されるときだけで、カラーピクセルが書き込まれない場合は深度バッファも同様に書き込まれないのが基本です。
カラーピクセルが描画されない場合というのが深度バッファによる遮蔽カリングが動作した場合です。
つまり、すでに書き込まれている深度バッファの値が新しい深度の値よりも小さい(手前側にある)、と判断された場合です。
上図の左側のような状態を、赤→緑と描画された場合を例に処理の流れを追ってみます。
1.深度バッファは最大値(1.0)ですべて初期化される
2.赤ポリゴンが描画され、このポリゴンの各深度値は1.0より小さい、つまり一番奥より手前にあるのですべて描画される
3.緑ポリゴンが描画された場合、赤ポリゴンとオーバーラップしていない部分は普通に描画される(赤ポリゴンと同じ理由)
4.オーバーラップしている部分は緑ポリゴンの深度値とすでに書き込まれている深度値(=赤ポリゴンの深度値)と比較され、赤ポリゴンより奥にあると判断されるので描画されない
と、こんな感じの流れになります。
不透明描画の場合はこの処理の流れで何も問題なかったりするのですが、半透明の場合はまたちょっと特殊です。
ただ、UE4を改造せずに使っている限り、この辺の問題は何も気にしなくてOKです。
もしも修正が行われてこの辺の設定が細かくできるようになったら、その際に別の記事を作成しようと思います。
ここでGPUの話。
深度バッファの機能は結構昔から存在しますが、そのころからあまり機能的な拡張は行われていません。
先の説明では深度バッファの比較、書き込みが不透明の際には常に行われている、という話になっていましたが、実際のGPUではこの辺の設定はそれなりに細かく行えるようになっています。
少なくとも以下の3点は現在のGPUならほぼどれでも設定が可能です。
1.深度バッファとの比較を行うか否か
2.深度バッファへの書き込みを行うか否か
3.比較を行う際の比較関数
1については深度バッファ全体をDisableにする方法が最も一般的です。
この場合は比較も書き込みも行われなくなるので、比較はしないけど書き込みたい、というかなり特殊な事例(どういう状況か想像もつかないけど)の場合は2と3を併用する必要があります。
2はUE4の半透明オブジェクトがこの機能を利用して常に書き込まないようになっています。
ただ、個人的には書き込みを可能にする機能を追加してほしいとは思います。
3は比較する際の比較方法です。
設定項目は決められた方法の中から選択するしかありませんが、>、<=、==などの他に、必ず描画する、必ず棄却するなどの設定もあります。
これらは特殊な描画方法で使用されることはありますが、UE4では設定が不可能になっています。
UE4では半透明オブジェクトに関して1の設定は行うことが可能だったりします。
マテリアルのDetailsタブの[Translucency] -> [Disable Depth Test] をONにすると深度の比較を行わずに常にピクセルが描画される状態になります(ただし、深度バッファは書き込まれない)。
ただ、[Enable Separate Translucency] がONの場合はなぜか一切描画されなくなるので注意してください。
[Disable Depth Test] がON/OFFの場合は以下のような結果になります。
OFFの場合はきちんと手前のオブジェクトで遮蔽されていますが、ONの場合は遮蔽されなくなっています。
静止画だと配置している位置が違うだけにも見えますが、動かしてみるとかなり気持ち悪いです。
特にOculusなどの立体視ではこの設定はできるだけ使わないようにしましょう。
深度バッファの簡単?な解説はここまでにして、本題のCustomDepthについて続きから解説します。
というわけで、今回の本題であるCustomDepthです。
CustomDepthはもう1つの深度バッファで、特定のメッシュのみ描画することが可能です。
元々深度バッファを描画しない半透明オブジェクトはこちらも描画はできません。
CustomDepthに描画を行いたい場合はメッシュの設定である[Rendering] -> [Render Custom Depth] をONにする必要があります。
Blueprintに登録されているメッシュコンポーネントにも同様の設定がありますが、どちらもデフォルトでは隠されているので注意してください。
ここにチェックを入れたメッシュは通常のカラーバッファ、および深度バッファとは別にCustomDepthにも描画が行われます。
下の図はブルーマンのメッシュにCustomDepthをONにした場合の各バッファの状態です。
Scene Color、およびScene Depthはカラーバッファと深度バッファの結果、Custom DepthはそのままCustomDepthの描画結果です。
見ての通り、Scene ColorとScene Depthにはブルーマンを含むすべてのオブジェクトが描画されていますが、Custom Depthにはブルーマンしか描画されていません。
さて、このCustomDepthは何に使われるのでしょうか?
実は、これだけでは何も行われません。
描画されたことによって速度低下は発生しますが、それ以上の何かは発生しません。
Customという名前の通り、ユーザが自由に使用することができる深度バッファ、それがCustomDepthです。
まあ、端的にいうとポストプロセスで利用します。利用方法はいくらでもあるでしょう。
ポストプロセスで利用するには[Scene Texture]ノードから [CustomDepth] を選択すればOKです。
では、今回はCustomDepthを利用した敵のサーチエフェクトを作ってみます。
最近のゲームではたまに見かけますが、プレイヤーから飛ばされた電波的な何かによって壁の裏側に存在する敵を透過するようなエフェクトです。
エフェクトとしては、深度方向に走査線が走り、走査線より手前に配置された、CustomDepthに描画されているオブジェクトをオレンジ色で表示するというものです。
これを実現するポストエフェクトは以下のようになります。
(クリックで拡大)
隠れたメッシュをオレンジ色で表示する部分はあくまでも背景オブジェクトで遮蔽された部分のみにしています。
隠されていない部分も表示したい場合は[Scene Depth]との間で比較する必要はないのでより簡単になります。
今回のサンプルはサードパーソンテンプレートを利用していて、プレイヤーキャラクタを透過させないようにするためにこのような処理を行いました。
敵を遮蔽しているものが背景オブジェクトなのかプレイヤーキャラクタなのかを判断する手段が実はなかったりします。
通常であればステンシルバッファを利用したりするのですが、UE4は現段階ではステンシルバッファが使えないのでその手段が利用できません。
ですので、プレイヤーキャラクタもCustomDepthに描画し、壁に遮蔽されていない部分はオレンジ色にしないことで対処しています。
それでもプレイヤーキャラの一部が壁に隠れたりすることはあるわけで、そういう場合はプレイヤーキャラもオレンジで表示されてしまったりします。
早くステンシルバッファが使えるようになってほしいものです。
なお、プレイヤーキャラはレベルエディタに配置されているわけではないので、プレイヤーキャラのBlueprintであらかじめ設定しておくか、LevelBlueprintのBegin PlayイベントでONにするかしなければなりません。
今回はLevelBlueprintで対応しました。
(クリックで拡大)
また、ポストプロセスマテリアルではコレクションパラメータを用いてサーチ深度を設定しています。
通常のメッシュ用マテリアルと違ってポストプロセスマテリアルはDynamicMaterialInstanceを利用できないっぽいので。
いや、できるのかもしれませんが、ポストプロセスパラメータが超巨大な構造体になっていて、ここに動的に設定を行うのは無理だと思います。
下図はポストプロセスの設定を作成する[Make PostProcess Sedttings]命令を全体表示した結果です。
(クリックで拡大)
冗談だと思うでしょ?でも、Epicさんは本気です。
そのうち修正されると思いたいのですが、現段階でポストプロセスをON/OFFするにはPostProcessVolumeを有効/無効にして対応するしかなさそうです。
話は逸れましたが、ポストプロセスマテリアルにサーチ深度を設定してやらなければいけません。
ゲームであればプレイヤーが特殊能力やアイテムを使用したときにサーチ深度を動かすようにするのが一般的でしょう。
ただ、今回は普通にLevelBPのBeginPlayイベントでTimelineを用いて対応することにします。
2秒で深度が0~1000まで移動するようなTimelineを作成し、これをBeginPlayイベントでコレクションパラメータに設定してやります。
(クリックで拡大)
という感じで作成した結果は以下のようになります。
走査線が走ったあとには壁に隠れたブルーマンがオレンジ色で表示されます。
今回作成したプロジェクトは以下にアップしてあります。
https://dl.dropboxusercontent.com/u/39588440/CustomDepth.zip
何かの参考にでも。
もちろん、CustomDepthの利用方法はこれ以外にもあります。
特定オブジェクトのみアウトラインを表示するとかですね。
あと、知らない人用のおまけですが、GBufferやCustomDepthなどの描画バッファの結果をレベルエディタに表示するには、レベルエディタ左上の [Lit] と書かれたボタンをクリックし、[Buffer Visualization] から見たいものを選択すればOKです。
[Overview] を選択すると通常の結果を囲むようにして複数のバッファが表示されます。
ポストプロセスマテリアルを作成する場合など、各描画バッファに何が描画されているか正確に知りたい場合に活用してください。
というわけで今回はここまで。