テラシュールブログ

旧テラシュールウェアブログUnity記事。主にUnityのTipsやAR・VR、ニコニコ動画についてのメモを残します。

【Unity】特定のモデルにのみImageEffectがかからないようにする

f:id:tsubaki_t1:20151122235512j:plain

今回は特定のモデルにのみImageEffectがかからないようにするアプローチについてです。
例えば上のように、他のユニティちゃんはGrayscaleがかかっているのに特定のモデルにはGrayscaleがかからない…といった表現に使えます。

なおForwardレンダリングのみ動作します。Defeerd?聞かれたら調べます。

目次

やり方

今回はカメラ2つを使用します。

ImageEffectの対象から外したいキャラクターを他とは異なるレイヤーに所属させます。例えばHighlightとか。

f:id:tsubaki_t1:20151123001018j:plain

2つのカメラを準備

二つ目のカメラ、SubCameraを用意します。
SubCameraのCameraコンポーネントはMainCameraと同じ座標・同じFOVに、MainCameraよりDepthは高い値にします。
出来れば動かすのが楽なのでMainの子として置くと楽です。

SubCameraのCulling Maskは、エフェクトを外したいレイヤーのみ残します。

f:id:tsubaki_t1:20151123001350j:plain

f:id:tsubaki_t1:20151123001356j:plain

f:id:tsubaki_t1:20151123001509j:plain

Sub Cameraの設定

Sub CameraのClear Flagを「DontClear」に設定します。
この設定ならばDepthOnlyだと消してしまうデプスバッファを残してくれるので、モデルの奥にキャラクターを表示する事が出来ます。

f:id:tsubaki_t1:20151123001731j:plain

f:id:tsubaki_t1:20151123001925j:plain

ちなみにDepth Onlyだとこんな感じで表現されます。

f:id:tsubaki_t1:20151123001954j:plain

ImageEffectの設定

後はMainCameraにImageEffectを設定します。
ImageEffectを適応後にDepthでくり抜いたImageEffectを適応しない絵を描画するため、Subカメラの描画した絵にはImageEffectが適応されません。

f:id:tsubaki_t1:20151123002211j:plain

全体に対するエフェクト設定

その後に「全体にImageEffectを設定」したいならば、SubCameraにImageEffectを設定します。

最後に描画した内容にエフェクトがかかるので、こうするとシーン全体にエフェクトが付与出来ます。

f:id:tsubaki_t1:20151123002415j:plain

問題と対策

このやり方を利用した場合、いくつかトラップのようなものが出るかもしれません。というか出ました。

その辺りも紹介しておきます。

MainCameraとCulling Mask

今回の紹介ではMainCameraからHighlightを削除していません。

この理由は単純に、HighlightをMainCameraから削除してしまうと影が塗られなくなる為です。

キャラクターをハイライトで表示するために後から描画している訳ですが、実はこの方法を取ると、キャラクターの影を受け止める板がSubCameraで描画されていないため、キャラクターが落とすべき影も描画されません。

f:id:tsubaki_t1:20151123003017j:plain

f:id:tsubaki_t1:20151123003025j:plain

解決策としては「影のみを表現する地面」等を用意すれば良いのですが、面倒くさいのでカリングせずハイライトするモデルを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;
	}
}

f:id:tsubaki_t1:20151123005015j:plain

Depthを確認する

FrameDebuggerで確認した図です。
上がMainCamera、下がSubCameraのDepthを描画した状態です。ShadowOnlyを指定したモデルのデプスが書き込まれていないことが確認出来ます。

この処理は、今回のアプローチにおけるZファイティング(同じDepthを持つ二つのオブジェクトがあるため、どちらを前面に表示して良いか分からずパタパタする現象)対策に使えます。

f:id:tsubaki_t1:20151123152529j:plain

透明なオブジェクト

もう一つの問題はデプスバッファに自分の奥行を描かない、Transparent(透明)なオブジェクト群の存在です。
このアプローチはMainCameraが書いたデプスバッファをクリアせず再利用する所がミソなので、デプスバッファが書き込まれないオブジェクトがあった場合、このアプローチは破たんします。

例えばStandardShaderのFadeやTransparentはデプスバッファに書きません。

f:id:tsubaki_t1:20151123005914j:plain

どうなるのかと言えば、Clear FlagがDepth Onlyだった時のような挙動です。つまり、ハイライトしたモデルが手前に表現されてしまいます。

f:id:tsubaki_t1:20151123005942j:plain

f:id:tsubaki_t1:20151123010100j:plain

f:id:tsubaki_t1:20151123010106j:plain

もし透過な背景がある場合、Cutoutを使用する…や、ステンシルバッファによるImageeffectのくり抜きを行うのが良さそうです。

ちなみに2Dはほぼ100%Transparentなので、抜き取るならステンシルです。

 

パフォーマンス的注意点

レイヤー切替の対象となるオブジェクトの量が多く、対象を常に切り替えているような場合、余計なCPUコストが発生するかもしれません。

 

関連

tsubakit1.hateblo.jp

tsubakit1.hateblo.jp

20141021 image effects (pro only) 描画順とか しそそふと 開発&日記ブログ

http://unity-chan.com/images/imageLicenseLogo.png