規制解除
外出自粛やマスク制限などコロナ禍による様々な規制が解除されていくのを見て、そろそろ自社の飲み会が復活しそうで戦々恐々な皆さんこんにちは。これまでは自粛という名のサボタージュで逃れていたのですが今年の忘年会にはそれが通用しなさそうで怖いです。
マテリアル
Unityでは表示する3Dオブジェクトには必ずRenderer (MeshRenderer /SkinnedMeshRenderer)コンポーネントが付与されていますが、このコンポーネントでは複数のマテリアルを紐付け(アタッチ)することができます。
アタッチされたマテリアルはリスト化され、重ねて描画(レンダリング)をすることができます。
例えばメッシュにテクスチャを表示するマテリアルと
メッシュを地面に投影して影のように表示するマテリアル
この二つのマテリアルをリスト化してアタッチすると、
影付きの3Dモデルとして表示できます。
これを利用して壁等に遮られた部分のシルエットを表示するマテリアルを作りリストにアタッチすることで、キャラクターが物陰に隠れてもシルエットは表示されて操作ができる、アクションゲーム等でよく見られる演出が実装できそうです。
ステンシルバッファを利用する
遮蔽物に対して表示、非表示等の操作を行う場合、ステンシルバッファを利用した手法がよく取り上げられます。
ステンシルバッファは描画の際に当該のピクセルを画面に表示するかしないかを決める「マスク」を設定する役目を持ちます。
なのでこれを利用して
- 障害物オブジェクトの描画領域のステンシルバッファに「1」を書き込む
- シルエットを表示させる側の描画領域でステンシルバッファが「1」となっている部分を黒く塗りつぶす。
とすれば、オブジェトのシルエットとして表示することができそうです。
お試し
障害物用として以下のシェーダーを用意
通常のUnlitシェーダーにステンシルバッファに1を入れる処理を追加しています。
シルエット用のシェーダーは以下の通り
このシェーダーを使ったマテリアルを作成、キャラクターのマテリアルリストに追加すると・・・
障害物(Cube)に隠れた部分がシルエットとして表示されます。
だが、しかし・・・
これで無事OKかと思ったのですが、障害物を設置したステージでキャラクターを動かしてみると
・・・ああ・・・
キャラクターが手前にあるのにシルエットが表示されています。
よく考えるとシルエット部分はZ方向(奥行方向)の順序関係なく描画エリアに書き込んでいるので、障害物と位置が重なれば手前/奥に関係なくシルエットが表示されてしまう結果になります。
う~ん、どうしたものか、と悩んでいたらnan氏のブログにて素敵な解決法が紹介されてました。
解決法
上の記事で提示されている解決法では、障害物側のシェーダーでステンシルバッファに1を設定することはそのままで、シルエットを表示させたいオブジェクト側のシェーダーで以下の処理を行っています。
- シルエットを表示させる側の描画領域でステンシルバッファが「1」となっている部分を「2」に変える
- シルエットを表示させる側の描画領域で隠れていない(普通に描画される)領域を「3」に置き換える
- ステンシルバッファが「2」となっている部分を黒く塗りつぶす。
つまり、障害物の描画時に書き込んだステンシルバッファをキャラクター側の描画時に見えている部分だけを更新、残った領域をシルエットとして判定する、という手法です。
この手法を考慮してシルエット用のシェーダーを変更します。
■Pass1
■Pass2
■Pass3
このシェーダーを使って実際に動作させると
物陰に隠れた部分だけシルエットを表示できるようになりました。