今回はドット絵にライティングを追加するアプローチについてです。
ドット絵 は 光の演出 を手に入れた!!!
今回はドット絵に光の演出を試してみました。
例えば暗い洞窟や完全自動で動く工場など、ステージ全体が明るくない場所での表現において、光による演出は中々に楽しそうです。
なお、今回使用するのは殆どTilemapかSpriteRendererで構成されたものです。ライティングを施さなければ大体こんな感じになります。
この辺り通常の3Dであれば、この辺りは最初からうまい感じに表現してくれるのですが、ドット絵はあくまでも2Dのため、一工夫が必要になってきます。
さて、光表現は大きく分けて4つの要素が考えられます。
- 光の届く範囲が見える
- 光を向いてる面が明るくなる
- 光るパーツは暗闇でも明るく見える
- 光を遮り影ができる
この4つの要素を全て入れる事も可能かもしれませんが、手間とか色々な要素を考えて入れるかどうかを判断する必要があります。
光が届く範囲が明るくなるだけで良い
まず「光が届く範囲が明るくなる」…という考えです。スーパーニンテンドーのRPGのような物でよくある表現で、目的は「視界を狭めること」です。
やり方は二通りあって、一つ目はSpriteMaskのようなステンシルを使う方法、2つ目は普通にライティングの機能を使う方法です。
まずSpriteMaskの方法を考えてみます。
- 画面前面に黒いSpriteを描画します。この時、Mask InteractionはVisible Outside Mask(マスクの外側だけ表示)に設定しておきます。
- GameObject > 2D Object > SpriteMask で光の形状のスプライトをマスクに登録すれば、マスクの範囲がくり抜かれます。
マスクの形は変形しないので若干雑な感じの切り抜きに見えるかもしれませんが、個人的には暗闇表現なら大抵はコレで良い気もします。
普通にライティングを利用する方法は、Spriteに使用するシェーダーを変更します。Spriteが光の影響を受けないのは、光の影響を受けない(代わりに高速)シェーダーを使っているからなので、コレを変更してやればOKという話です。
- DiffuseSpriteという名前のMaterialを作る
- DiffuseSpriteのシェーダーはSprite>Diffuseにする
- TilemapRendererやSpriteRendererのMaterialをDiffuseSpriteに変更する
- ポイントライトやスポットライトを適当に配置
これで光の陰影をピクセルベースで計算したものを使用できます。
光の陰影や光るパーツなどを表現するのも欲しい
光の陰影や光るパーツなどを表現してみます。
なお光の陰影が追加される事で光の演出とテクスチャの光表現が重複する可能性があることには注意する必要があります。
(Unityゲームでよく見た「残念な感じの絵」の一つは、テクスチャに光表現を焼き込んだ上でシェーダーでも光表現を…という物があります)
まず考えることは、陰影を2Dであるドット絵で表現する方法です。
これにはノーマルマップ(法線マップ)やエミッションなど表現に対応したテクスチャ用意します。
なおエミッションは全体を暗くして光らせたい所に色を付ければ良いのですが、ノーマルマップの方はどうやって作ればよいのか…
RGB のカラー値は X,Y,Z のベクトルの方向を保存するために使われます。Z が“上方向”になります(逆に、Unity では慣例的に Y が“上”)。加えて、テクスチャの Z 値は、0.5 が足されて半分になります。これは、すべての向きのベクトルを保存するためです。そのため、RGB カラーをベクトル方向に変換するには、2 倍してから 1 をひかなくてはなりません。
法線マップ(Normal Map)(Bump mapping) - Unity マニュアル
あとはノーマル(法線)マップに対応したシェーダー…例えばStandardShaderなどをセットしたマテリアルを用意し、NormalやEmissionに設定、SpriteRendererやTIlemapRendererに登録すれば、光の表現が出来るようになります。
Want to add cool lighting effects to your 2D sprites? Simple apply a material with normals (and whatever else you want) to your Sprite Renderer. Works with Tilemaps too! #UnityTips #Unity3D #GameDev pic.twitter.com/TnGr06NPIh
— Mike Geig (@mikegeig) May 29, 2018
なおSpriteRendererもTilemapRendererも、メインのテクスチャはレンダラが使用したいものが自動でセットされ、そのUVを使用してノーマルもエミッションマップも設定されます。つまりノーマルマップとエミッションのマップのレイアウトはスプライトに使用したものと同一である必要があります。
この何が問題かといえば、SpriteAtlasのようにパッキング後のレイアウトが確定せずスプライトをレイアウトする機能を使用すると、ドット絵とノーマルマップのレイアウトがずれてしまう可能性があるという事です。
つまり、この手法を使用する場合はドット絵側でスプライトを詰めてやる必要があります。
ドット絵に影もつけたい
最後にドット絵に影を付けてみます。
この時の考え方は単純で、ドット絵の上に見えない影だけを落とすモデルを配置するという力技を使います。
建物の高さはCubeの高さで、実際3Dで見たらどの辺りに配置されてるのかな…的なものを見たら上手くいきます。
配置後CubeはのMeshRendererのCastShadowの設定をShadowOnlyに設定しておけばゲーム画面では表示されなくなります。
キャラクターの影くり抜きの場合、Cutoutシェーダーでキャラクターのシルエットにくり抜いた影を使うというアイディアもありますが、Spriteに対応してないのでその辺り保留
ただ残念な事に、背景…というか地面にTilemapを使用している場合このアプローチは使えません。TilemapはRecieveShadowを無視するみたいです(例えスクリプトでONにしても、影対応シェーダーを使用しても効果はありませんでした)
なので影の手法を使う場合はTilemap以外の方法で背景を表示する必要があります。上の図では背景は普通にQUADを使用しています。
まぁ建物の構造を強調するための影という観点で言えば、2DDLのような2D影を使ったほうが良いかもしれません。
感想
ということで照明効果についてでした。
オールインワンで無条件で突っ込めるという感じでは無いので、用途によって分けました。必要に応じて追加していくのが良さそうです。