【Unity】特定のモデルにのみImageEffectがかからないようにする
今回は特定のモデルにのみImageEffectがかからないようにするアプローチについてです。
例えば上のように、他のユニティちゃんはGrayscaleがかかっているのに特定のモデルにはGrayscaleがかからない…といった表現に使えます。
なおForwardレンダリングのみ動作します。Defeerd?聞かれたら調べます。
目次
やり方
今回はカメラ2つを使用します。
ImageEffectの対象から外したいキャラクターを他とは異なるレイヤーに所属させます。例えばHighlightとか。
2つのカメラを準備
二つ目のカメラ、SubCameraを用意します。
SubCameraのCameraコンポーネントはMainCameraと同じ座標・同じFOVに、MainCameraよりDepthは高い値にします。
出来れば動かすのが楽なのでMainの子として置くと楽です。
SubCameraのCulling Maskは、エフェクトを外したいレイヤーのみ残します。
Sub Cameraの設定
Sub CameraのClear Flagを「DontClear」に設定します。
この設定ならばDepthOnlyだと消してしまうデプスバッファを残してくれるので、モデルの奥にキャラクターを表示する事が出来ます。
ちなみにDepth Onlyだとこんな感じで表現されます。
ImageEffectの設定
後はMainCameraにImageEffectを設定します。
ImageEffectを適応後にDepthでくり抜いたImageEffectを適応しない絵を描画するため、Subカメラの描画した絵にはImageEffectが適応されません。
全体に対するエフェクト設定
その後に「全体にImageEffectを設定」したいならば、SubCameraにImageEffectを設定します。
最後に描画した内容にエフェクトがかかるので、こうするとシーン全体にエフェクトが付与出来ます。
問題と対策
このやり方を利用した場合、いくつかトラップのようなものが出るかもしれません。というか出ました。
その辺りも紹介しておきます。
MainCameraとCulling Mask
今回の紹介ではMainCameraからHighlightを削除していません。
この理由は単純に、HighlightをMainCameraから削除してしまうと影が塗られなくなる為です。
キャラクターをハイライトで表示するために後から描画している訳ですが、実はこの方法を取ると、キャラクターの影を受け止める板がSubCameraで描画されていないため、キャラクターが落とすべき影も描画されません。
解決策としては「影のみを表現する地面」等を用意すれば良いのですが、面倒くさいのでカリングせずハイライトするモデルを2回描画しています。
とはいえ2回もピクセル書くのは馬鹿らしいのでSubカメラ描画時以外は影だけ登場にしてもらいます。SubCameraにアタッチしたコンポーネントに以下のような感じで書いておくと、SubCamera記述時のみ何かします。
void OnPreRender() { foreach (var renderer in renderers) { renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.On; } } void OnPostRender() { foreach (var renderer in renderers) { renderer.shadowCastingMode = UnityEngine.Rendering.ShadowCastingMode.ShadowsOnly; } }
Depthを確認する
FrameDebuggerで確認した図です。
上がMainCamera、下がSubCameraのDepthを描画した状態です。ShadowOnlyを指定したモデルのデプスが書き込まれていないことが確認出来ます。
この処理は、今回のアプローチにおけるZファイティング(同じDepthを持つ二つのオブジェクトがあるため、どちらを前面に表示して良いか分からずパタパタする現象)対策に使えます。
透明なオブジェクト
もう一つの問題はデプスバッファに自分の奥行を描かない、Transparent(透明)なオブジェクト群の存在です。
このアプローチはMainCameraが書いたデプスバッファをクリアせず再利用する所がミソなので、デプスバッファが書き込まれないオブジェクトがあった場合、このアプローチは破たんします。
例えばStandardShaderのFadeやTransparentはデプスバッファに書きません。
どうなるのかと言えば、Clear FlagがDepth Onlyだった時のような挙動です。つまり、ハイライトしたモデルが手前に表現されてしまいます。
もし透過な背景がある場合、Cutoutを使用する…や、ステンシルバッファによるImageeffectのくり抜きを行うのが良さそうです。
ちなみに2Dはほぼ100%Transparentなので、抜き取るならステンシルです。
パフォーマンス的注意点
レイヤー切替の対象となるオブジェクトの量が多く、対象を常に切り替えているような場合、余計なCPUコストが発生するかもしれません。