テラシュールブログ

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

【Unity】複数パーツで構成された半透明のSpriteを違和感無く表示する…際に出るであろう問題の対策

前回の続きで、複数パーツで構成された半透明のSpriteを違和感なく表示する方法の続きです。
前回はSpriteMaskを使用して重複部を表示しないようにしました。今回はこのアプローチで発生する問題を回避する方法について紹介します。

SpriteMaskを使えば、半透明を違和感なく表現できる

https://cdn-ak.f.st-hatena.com/images/fotolife/t/tsubaki_t1/20171124/20171124002131.jpg

少しユニークなアプローチですが、SpriteMaskを用いてスプライトが重なる部分を非表示にすれば、スプライトを半透明にした場合でも違和感なく透明似できます。

tsubakit1.hateblo.jp

ただしこのアプローチだけではまだ完全ではなく、幾つかの問題が発生するかもしれません。ソレを行うことでも負荷的な問題が発生しない訳ではないので、ソレを使うかどうかは十分に考えて検討する事が望ましいかなと。

別キャラクターのマスクが影響を及ぼす問題

f:id:tsubaki_t1:20171124015527j:plain

SpriteMaskの影響範囲は特に何も設定をしないのであれば、全てのスプライトが影響を受けます。このため、奥のキャラクターの顔が手前の体より手前に描画される…といったケースもあるかもしれません。

この問題は、Sorting Groupで解決します。

 

何故こんな事が起こるのか。

手前にピンクのSpriteを配置し、ピンクのSpriteRendererのMask InteractionをVisible Inside Mask(マスキングされてるところだけ表示)とすると、下の画像のように顔の部分だけが表示されるようになります。
要するに、SpriteMaskは初期設定だと他のSpriteの描画順に関わらず影響を与えてしまうという事です。

f:id:tsubaki_t1:20171125181116j:plain

実際、Frame Debuggerで確認するとマスクの描画は一番最初に行われていることが確認出来ます。

1がマスクの描画、2が体の描画、3で顔の描画です。

f:id:tsubaki_t1:20171125182155j:plain

Sorting Groupで解決

今回はスプライト単位で動かせるので、Sorting Groupを使用します。

Sorting Groupを使用すると、SpriteMaskの影響範囲がSortingGroup範囲内に限定されるようになります。
これをうまく活用すれば、重ねたときに発生する問題を回避出来る訳です。

f:id:tsubaki_t1:20171125183134g:plain

tsubakit1.hateblo.jp

複数のSpriteに挟まれているスプライトを表現する場合の場合

影響範囲を自分だけに抑えたい場合はSortingGroupで良いですが、影響範囲が自分で複雑な階層構造を持つ場合には、少し異なる手法が必要になります。
具体的には、複数のSpriteに挟まれているようなケースです。

 

例えばドラゴンの顔を考えてみます。
この顔は「頭」「下顎」「首」の3つのパーツで構成されています。

f:id:tsubaki_t1:20171125184453j:plain

 

コレに対して今まで通り「頭」にSpriteMaskをセット、頭のマスクを「下顎」と「首」が受けるように二つのSpriteRendererにVisible Outside Maskをセットします。
「頭」と「顎」の間では問題は無くなりましたが、今度は「顎」と「首」で重複部が見えてしまっています。

f:id:tsubaki_t1:20171125184535j:plain

そのため今度は「顎」にSpriteMaskを設定しますが、「顎」はVisible Outside Maskが既にセットされているため表示されなくなりました。

つまり必要なのは、「顎」はVisible Outside Maskの影響を受けてマスクを受けるが、「顎」が発行するマスクの影響は受けない…そんなアプローチです。

f:id:tsubaki_t1:20171125184940j:plain

SpriteMaskのCustomRangeを使って解決

この問題はSpriteMaskのCustomRangeを使用すれば解決です。

f:id:tsubaki_t1:20171125185604j:plain

この機能で、SpriteMaskの影響範囲を限定します。

例えば今回の場合「頭」のSortingLayerは「2」、「顎」のSortingLayerは「1」、「首」は「0」なので、レイヤーの影響範囲を0以下に収まるように設定すれば、首のみが影響を受けるように出来る訳です。

tsubakit1.hateblo.jp

 

マスクの間のスキマが見えてしまう問題

SpriteMaskでくり抜く手法ですが、SpriteMaskがアルファ乗算ではなくカットオフなので、パーツがアルファで抜かれている場合には、少しスキマが見えてしまう事があります。

f:id:tsubaki_t1:20171125190900j:plain

Alpha Cutoffを調整

この問題は、SpriteMaskのAlphaCutoffを調整することで解決します。

要するにアルファでエッジを表現している部分が、少し広めに範囲を取っているだけなので、それをなんとかすれば解決する訳です。

f:id:tsubaki_t1:20171125191032g:plain

ただし、Cutoffの値を上げすぎると半透明にしたときに重複部が見えてしまうので注意が必要です。

f:id:tsubaki_t1:20171125191339j:plain

パフォーマンス的にはどうなの?

さて、ゲームを作る人が気になるワードNo1の一つは、パフォーマンス云々でしょう。

今回紹介したSpriteMaskの手法ですが、パフォーマンス上で気になる点は幾つか存在します。

まず、通常の描画に追加で描画を1回、もしくは2回発行する点です。
特に、SortingGroupやCustomRangeを使用した場合、SpriteMaskで影響範囲を限定すると「締め」の描画が発生するみたいです。

f:id:tsubaki_t1:20171125190121j:plain

またCustomRangeで影響範囲を絞った場合、描画順に割り込むのでバッチを壊す事があります。これはUIのMaskも同様です。

あとInstancingでスプライトを描画してる場合はSpriteMaskが効きませんでした。シェーダー的にソコはユニークな値に含めて無いんでしょうね。

必要な時のみSpriteMaskを有効にする

とまぁ色々とリクスは有るのですが、SpriteMaskの良い点として「有効・無効が割りとシンプル」である点が上がります。

要するに「半透明が必要な時だけSpriteMaskを付けて、ソレ以外は無効化しておけば良い」といった感じです。

SpriteMaskはSpriteRendererとは完全に独立しているので、ON/OFFしてもオブジェクトが描画されるかどうか…程度の違いしか無いかなと。

全てはトレードオフ

今回はSpriteなので複数の描画がある前提で、SpriteMaskを紹介しました。

 

シノアリスやLive2Dのような1キャラしか表示していない場合は、RenderTextureに描画して再表示…が手っ取り早いケースもあるかもしれません。実際、UIを透明にするようなケースではコレの方が楽です。
ただし複数体のスケルタルアニメーションを表現する場合は、RenderTextureのアプローチでは高解像度なRenderTextureをキャラクター分用意するといった事になるかもしれないので、余り良い結果にならないかもしれません。

もしくは、もっとシンプルに透明の方法をセガ式透明でやるのも有効かもしれませんし、割り切って何もしないのも有りです。
あるいは最近流行りの半透明を使用しない、燃え上がる的な退場法式も。

f:id:tsubaki_t1:20171125195003g:plain

透明は中々悩ましいですね

関連

SortingGroupはコレにも影響が出るので、そこんとこ注意

tsubakit1.hateblo.jp

パフォーマンス云々以前にプロファイリング。まずはそこから

tsubakit1.hateblo.jpボトルネックになりやすいポイント

tsubakit1.hateblo.jp