だらけ者だらけ

だらけ者だらけの遊び場

【UE4】被写界深度半透明物体共生戦略手法零式(Type-0)

※UE4フォーラムに本記事のスレッドを立ち上げております。
半透明描画とDOF(被写界深度)を両立させる方法について(2014 Advent Calender)



わかりにくいタイトルでごめんなさい。つまり、

半透明描画にDOF(被写界深度)を適切にかける手法を、色々試してみました。
というのが、今回の記事内容です。ソース拡張や独自ポストプロセスは入っていません。エディタ内でできることの調査です。
(被写界深度は文字が硬い感じがするので、英語のDepth Of Fieldを略した"DOF"と以後表現します。レンダリング界隈の人々はDOF(ドフ)と呼んでいるみたいです。)

前もって自分が試した手法の結論を言っておきます。

半透明描画にDOFの効果を与えるために2つ程手法を紹介します。が、本手法は多大な手間がかかりますし、色々制約があります。実用的ではありません。

自分なりの検証結果をまとめときますので、議論の入り口になれば幸いです。
そんな意味での零式(Type-0)です。。
ということで本日のアジェンダです。

アジェンダ


  1. 発端: 半透明とDOFの相性の悪さの例
  2. 原因: なんで半透明のDOFはうまくいかない?
  3. 解決案1: DOFに対応した半透明描画を作成する
  4. 解決案2: DOFをかけたい描画にデプスを与える
  5. まとめ: なぜ実用的じゃないか

発端: 半透明とDOFの相性の悪さ


まずは、DOF(被写界震度)と半透明描画がうまく行かないシーンを見てみます。
非常にシンプルで恐縮ですが、空間にパーティクルを配置しただけの以下のシーンを考えます。

DOF(被写界震度)OFF

これにDOFかけたシーンがこちらです。

DOF(被写界震度) ON

わかるでしょうか?地面の切れる地平線にそって、DOFの効果に差が出て、境界線がでています。

また、半透明物体を別のバッファに描画する"Separate Translucency"という機能がマテリアルにあります。


マテリアルのSseparate Translucency設定箇所


この機能はProjectSettingで、そもそもSeparate Translucencyを使うかを設定できます。

プロジェクトのSseparate Translucency設定箇所


これらをOnにしてる人も多いと思いますが。。。
"Separate Translucency"が設定された半透明マテリアルにはDOFがかかりません。
これを設定したのが以下の画像です。

被写界震度ON & マテリアルのSeparate TranslucencyをON


一見良さそうに見えますが、右の遠くにある煙はDOFが入っていません。繰り返しになりますが、Separate TranslucencyがONのマテリアルをアサインされた物体には、DOFがかからないのです。
このように、半透明描画とDOFは非常に相性が悪いです。
ひどく言えば、バグのような描画結果になってしまいます。
半透明描画は、実は様々なエフェクトと併合性がありません。
これらの検証はs k (@monsho1977) | Twitterさんが以下の記事で丁寧にまとめています。ので、参考にしてみてください。
もんしょの巣穴blog [UE4] 半透明マテリアル

 

原因: なんで半透明のDOFはうまくいかない?


ではなんで半透明描画がうまくいかないのか?答えは単純で、
半透明描画がDepthを書き込まないから
ということに集約できます。

前章の画像のDepthを見てみます。

デプスバッファ(Scene Depth)


半透明の煙のデプスは全く書き込まれていません。
UE4の半透明物体(≒マテリアルがTranslucentに設定されたもの)はデプスに描画されません。
しかし、DOF(被写界深度)はデプスバッファを見て遠近を判断するので、半透明物体の正確な位置を考慮することができないのです。そのため、先に見せたようなエフェクトの不具合が発生してしまうのです。一連のレンダリング流れをまとめたのが下図です。

半透明とDOFの描画の流れ(Separate Translucencyを使わない場合)


また先にも述べた通り、UE4には、マテリアル設定にSeparate Translucencyという機能があります。そのSeparate TranslucencyをONにした際の挙動が以下です。Separate Translucencyが設定されたオブジェクトは別のバッファに書き出され、DOFの後に合成されます。半透明描画がやたら真っ赤なのは、単にこの画像がアルファを考慮していない画像なだけでアルファを考慮すると合成画像のように馴染みます。

半透明とDOFの描画の流れ(Separate Translucencyを使う場合)

DOFの後に合成するので、そりゃあ半透明にはDOFがかからないわけです。

解決案


では、どのような解決策があるか?今回、検証した以下2つの手法を晒します。

  1. 半透明の描画時に自力でぼかす
  2. DOFがかかるように遠くの物体にはデプスを書く

両方共Separate TranslucencyがOnになっていることを想定しています。

炎は複数のEmitterで作られており設定が大変なので、ここからはEmitterが一個のシンプルな煙で検証します。



検証用の煙

煙が赤いのはわかりやすくするためです。


解決案1: 半透明の描画時に自力でぼかす(マテリアルのDephtOfFieldFunction)


この解決案は、半透明のマテリアルを改変し、自作のDOF効果をぶち込んでやろうという方針のものです。

自作のDOF効果でパーティクルをぼかす


そのために、マテリアルのDepthOfFieldFunctionという機能を使います
Unreal Engine | Utility 表現式

マテリアルのDepthOfFieldFunctionノード


これの効果を見てみます。例えば、以下のマテリアルをスフィアに設定します。

DepthOfFieldFunctionテストマテリアル


DepthOfFieldFunctionはピントがあっている場合は0の値を、ボケている場合は1の値を返してくれます。しかもポストプロセスのDOF設定と自動で対応付けしているので、ピントがあう位置では赤に、ピントが合わないぼやけた位置では緑色になります。(下図)


DepthOfFieldFunctionテストシーン


このように、DepthOfFieldFunctionを使うことで、マテリアル内でDOFへの遷移が可能となるのです。後は、この値に基づいてマテリアルを構築するのみです。単純にぼかしテクスチャを用意して、オリジナルと遷移するようにします。

結果の画像が以下です。左側がオリジナルの煙で、近寄っても遠のいてもぼけません。右側はDepthOfFieldFunctionを用いた煙で、近寄るとボケているのがわかるかと思います。


このように、半透明物体に対して、独自にDoFの設定を行う事が可能です。今回厳密に調査していませんが、まとめるとこんな感じです。

  • 長所
    1. 各エフェクトに対してカスタマイズしたDoFの効果を作成することができる。
  • 短所
    1. シェーダ負荷があがる。
    2. 自身で一つ一つ調整しないといけない。




解決案2: DOFがかかる(ぼける)物体にはデプスを書く(Particle LOD)


こちらは簡単に紹介だけ。
もう一つの手法も、Separate TranslucencyがOnのときにのみ成り立つ手法です。「どうにかして既存のポストプロセスのDOFをパーティクルにかけられないか?しかし、エンジンの拡張などはしたくない。。。」という要求を成り立たせるために、”ぼける距離にあるパーティクルのマテリアルは、(デプスを書き込む)Maskedに設定する。”という手法を考えました。距離に応じてパーティクルの挙動を変える。。。つまりLOD(Level Of Detail)を使ったパーティクル制御です。

LOD Particleの細かな設定方法は公式に載っていますので、こちらを参考にしてください。
Unreal Engine | パーティクル システムの詳細度 (LOD)

ボケる範囲のマテリアルをBlendModeをMaskedに変更します。(下図)

左:ピント合うLODのマテリアル。右:ぼやけるLODのマテリアル


確認すると、Maskedに設定したパーティクルは確かにDepthを書き込むことができています。

右のLODがかかったパーティクルのデプスが書き込まれる。


この状態でぼかしたのが、下図、右のパーティクルです。この手法だと、前手法のようにマテリアルを拡張しないため、処理負荷は増大しないという利点があります。が。

LOD&Maksedマテリアルを用いたパーティクルのブラー


まあまあな感じもしますが、実は全然ダメです。Gif画像にしてみました。


LODの切り替え


LODの問題として、切り替わりが滑らかにならない問題があります。また、(DepthOfFieldFunctionのように)ポストプロセスのDOFと連動してくれたりはしません。なので、LODが切り替わる距離をDOFのぼやける距離と合う様に自分で設定しなければいけません。上記の煙もちゃんと設定すればもっとうまくいくかもしれませんが、そもそも、半透明に板のデプスを与えるということも無理やりすぎるので、ここで断念しました。

まとめ: なぜ実用的じゃないか


半透明+DOFを実現できないか、簡単ですが自分の試行錯誤を説明させていただきました。がしかし、がしかしだ。これの最大の欠点は、処理負荷や調整よりも修正項目の多さです。やることが非常に多い。今回の検証では煙一個、つまりエミッター一個だけの修正ですみました。ですが、例えばStarterContentsの爆発と炎のパーティクルを見てみます。


爆発のParticleはエミッターが5個


炎のParticleはエミッターが6個


このように一つのパーティクルにも複数のエミッターがあります。DOFのためにこんなに沢山の修正してられっか!というのが正直なところです。
ということで、ここまで説明してきて身も蓋もないのですが、実用的じゃない例を紹介させていただきました。
誰かがより良い手法を教えてくれるのをお待ちしております。

まとめ
問題点
半透明物体はDepthを描画しないため、Depthを用いるポストエフェクトのDOFと非常に相性が悪い。
解決策の検証
半透明描画自身にDOF機能を持たせるか、デプスを書き込むことによってポストエフェクトのDOFがかかるようにしないか?この2点を軸に考えた。どの案も、処理負荷の増大、自身での細かなパラメータ設定と一長一短がある。
最大の欠点
説明した手法では、一つのパーティクルにDOF効果を与えるためだけでも、使われる全種類のマテリアル/ParticleLODモデルを拡張しなければならない。




読んで頂ありがとうございます。


2014 UE4 Advent Calender. ウサギからウサギへバトンを託します。
次回は関西で勢力的に活動なさってる栗坂こなべさんの「UE4でOculus Riftコンテンツを作る入門記事(を予定)」です。自分もOculus+UE4で色々試してるので、非常に楽しみです!