はじめに

この記事は KLab Creative Advent Calendar 2019 の 2日目の記事になります。

こんにちは。テクニカルアーティストグループの fo-ta です。
去年のアドベントカレンダー では Shader のことを書かせて頂きました。
今回は半透明の前後関係(描画順)について書かせて頂きます。

概要

半透明のオブジェクトはなぜ見た目の破綻が起こるのか、具体的に Unity ではどう改善できるのかを紹介します。

クリエイティブブログですので、デザイナーさん、アーティストさん向けにわかり易さを重視しています。そのため、省略した書き方も取り入れています。ところどころ「正確には違う」点があるかと思いますが、ご了承ください。

難しい話はいいから実例だけ知りたい!という方はここまでスキップしちゃいましょう。

ざっくり座学編

深度

そもそも、なぜ3DCGは前後関係が正しく見えるんでしょうか。

「手前にあるんだから手前に見えて当たり前」。現実世界ではそうですが、CGである以上はちゃんと仕組みがあります。

その仕組とは、深度情報です。

image15

Z深度、深度マップ、デプスマップなどの呼び方をされる、上図の様な白黒画像を見たことがある人も多いでしょう。映像制作で被写界深度をかける時などにも使いますよね。この情報を利用します。
すでに手前にレンダリングされている場合はレンダリングしない、そうでなければレンダリングして深度情報も更新する、と言う流れで正確な前後関係になっています。

このように、深度情報を用いて正確な前後関係でレンダリングする手法をZバッファ法と呼びます。

Zバッファ法の弱点

そんなZバッファ法にも弱点があります。

手前にレンダリングされた半透明オブジェクトの深度情報により、奥のオブジェクトがレンダリングされない、ということが起こる点です。

image11

この様な理由から、半透明オブジェクトは奥のオブジェクトから順番にレンダリングすることで前後関係を解決しようとします。

この順番のことを「描画順」と呼びます(レンダリング=描画)。

描画順の決定方法

描画順はいくつかの要素を元に決定されます。ここでは代表的な例を紹介します。

オブジェクトの位置

オブジェクトの位置がよりカメラから遠いと先、近いと後の描画順になります。つまりより遠くにあるオブジェクトから順に描画されます。
ここでいう「オブジェクトの位置」は、オブジェクトの中心座標が基準になります。ツールハンドルの切り替えボタンを Center にするとその位置がわかります。

image3

image16

通常の Mesh の場合は中心位置が自動的に算出されます。
SkinnedMesh の場合は Bounds の Center の位置が中心座標です。これは Unity 上で編集することができます。

image4

image12

Render Queue

マテリアルの Render Queue の数字が小さいと先、大きいと後の描画順になります。

image1

これはオブジェクトの位置よりも優先されるため、奥にあるのに手前に描画する、と言うトリッキーなことも可能です。

※ Render Queue に使用する値はゲーム全体を考慮して設計すべきです。必ず担当のエンジニアさんと相談しましょう。

頂点番号

上記2つはオブジェクト単位ですが、1つのオブジェクト内のではポリゴンの頂点番号が小さいと先、大きいと後の描画順になります。

image8

Maya ではコンポーネントエディタ、 Houdini では SceneView や GeometrySpreadsheet で確認することができます。

実践編

実際に制作の現場で遭遇しそうな例を2つほどご紹介します。
(今回は Unity 2018.4 を使用しています。)

斬撃エフェクト編

image2

剣で攻撃する時などに使用する斬撃エフェクトです。

このように厚みのあるエフェクトを作成する際、ポリゴンの両面を描画する Shader を使用すると破綻が発生します。

角度を変えて見た際に、奥にあるはずのポリゴンが手前に描画されてしまうのです。

このような場合は、両面にポリゴンを用意し、 片面描画の Shader を使用すると解消できます。
両面にポリゴンを用意する際の注意として、前述の頂点番号を整える必要があります。頂点番号が小さい方が先にレンダリングされるので、内側が小さく、外側が大きくなるようにします。
複製&法線反転で内側と外側のメッシュを用意したら、 Maya では内側、外側の順メッシュを選択して結合、 Houdini であれば内側、外側の順で Merge ノードに接続すると意図した順の頂点番号になります。

Unity でポリゴンを片面のみ描画する Shader を設定します。

image5

例えばプリセットの Particle/Standard Ulit Shader の場合、 Two Sided のチェックを外すと片面描画になります。

無事、破綻を解消できました。

竜巻編

より多くのポリゴンが重なる場合、もう一歩工夫が必要です。

左が破綻したもの、右が整理したものです。

破綻している方では、大きな竜巻の内側ポリゴンが小さな竜巻よりも手前に描画されてしまっています。また、記事冒頭の画像のように視点を変えると前後関係も変わってしまうこともあります。
大きい竜巻の内外と小さい竜巻の内外、複数のポリゴンがちぐはぐな順番で描画されて起こる破綻です。

では、どのような順番で描画されればうまくいくでしょうか。

image18

①大きい竜巻の内側、②小さい竜巻の内側、③小さい竜巻の外側、④大きい竜巻の外側の順番で描画されれば大丈夫そうです。

内側の竜巻は斬撃の際と同様に内側、外側の順番で頂点番号を整理します。
外側の竜巻は、 Render Queue で順序付けします。メッシュを分割し、それぞれ別のマテリアルをあてましょう。

image7

負荷の増加について

描画順を整えるためにアセット数や描画負荷が上がってしまうことがままあります。
例えばポリゴンを両面作ると、当然ながらポリゴン数が倍増します。マテリアルを増やすことでドローコールが増えることもあります。

見た目のためなら何を犠牲にしても構わない!…なんてことはありません
見えなかったり目立たない部分は妥協し、目立つ場所は整えると言った、ほど良い落とし所を探ることが重要です。

その他

今回は詳細な説明を省略しますが、この他の半透明描画に関係するトピックスをいくつか紹介します。機会があれば別の記事で詳しくご紹介しようと思います。

複数マテリアル

image6

1つのメッシュにQueueが同じ複数のマテリアルを割り当てた場合、上から順に描画されます。これを利用して、竜巻の例でメッシュを1つにすることができます。

ZTest

ZTest という仕組みを利用すると、そのオブジェクトを描画する際、すでに書き込まれている深度情報を無視することができます。全画面エフェクトなど、最前面に表示したい場合に有効です。
ON / OFF の単純な切り替えではないことと、他の要素との兼ね合いがやや難しいため、今回は省略しています。

加算合成

半透明(アルファ合成)と比べ、加算合成同士は前後関係が破綻していてもバレにくいです。加算はその名の通り色の計算がシンプルな足し算のため、どちらが先でも結果が同じになるためです。

ディザ合成

不透明を格子状にパンチ抜きして描画することで擬似的に半透明にする技術です。不透明で描画するので深度情報を利用できるため、前後の破綻が起きません。
解像度が低いと不透明だとバレること、パンチ抜きの処理がモバイル端末と相性がわるいことなどから今回は省略しています。

最後に

エンジニアさん向けのこの手の情報は結構あるんですが、アーティストさん、デザイナーさん向けの記事ってあまり見ないな、と思ったことが今回のきっかけです。
描画順の破綻と日々戦っている人達に、少しでも解決のヒントになれれば幸いです。

なお、もし要望があれば、他の実践例もご紹介したいと考えています。

image10

この記事はユニティちゃんライセンス条項の元に提供されています。