実践ドット絵シェーダ

f:id:mizuooon:20180602191044p:plain:w200 f:id:mizuooon:20180602175242p:plain:w280

はじめに

自分はドット絵が好きだが、描くのはあまり好きではない。 正確に言うと、1,2 枚を描くのはよいがひとつのゲームに使用する大量のドット絵を全部自力で描くのは大変つらい。

例えばアクションゲームかなにかを作る場合を考えてみると、 キャラクターについてだけでも、必要なドット絵枚数は (登場キャラクター数) × (各キャラができるアクションの数) × (各アクションに必要な枚数) × (リテイクのコスト) で相当なものになるのがわかる。

そこでドット絵っぽいものをレンダリングできるシェーダを用いることで横着をしようというのが今回の目標である。 手描きと見紛うクオリティとまでは行かなくとも、 ドット絵っぽいものがヌルヌル大量の枚数で動くという高コストな表現をそこそこリーズナブルにできれば色々面白いことができるだろうと期待している。

先行事例と課題

既存のドット絵シェーダとしては以下のようなものがある。

エッジ検出だったりピクセル選択だったりは大体上記で話が済んでいるが、 ドット絵としてのクオリティや量産時のコストを考慮した場合、 上記手法については以下のような課題があると思われる。

  • パレットの設定が自力なのでめんどくさい
  • ドット絵的な 2D 特有の (3D 的には嘘の) シェーディングが出来ない
  • ドット絵にありがちな、髪の毛やスカートがたなびくような表現が難しい
  • ドット絵の文脈を考慮したモデルやアニメーションを使ってない (これは手法自体とは関係ないが)

ということで本項では上記の課題に対処してそこそこ production-ready なシェーダを作っていきたいと思う。

陰影と色彩

まずはパレットとか色周りの話についてである。 ドット絵シェーディングにはドット絵的な色選びというのが必要であるが、 マテリアルごとに陰影に応じたパレットを自力で用意する方法は結構面倒なのでその辺を用意せずにそれっぽい色味でレンダリングできるようにしたい。

そもそも何故色選びが必要なのかという話をすると、 3DCG 一般において、 ベーシックなレンダリング方程式は情緒に欠けるので例えば愚直にランバートモデルで描画を行ったりすると大変情緒の無い結果になるのである (下図参照)。

f:id:mizuooon:20180409235857p:plain

これは物理的に正しいが、あまりに絵的には微妙である。 ドット絵的な表現を行うなら、こうしたシェーディングに対して絵的な手心を加えなければならない。

とりあえずやたら見た目が暗いのが良くないだろうということで明度の減衰を和らげてみると以下のようになる。

f:id:mizuooon:20180410133724p:plain

さらにこれを階調化すると以下のようになる。

f:id:mizuooon:20180410133730p:plain

さっきの暗いのよりはましだが、まだどことなく情緒が足りないように感じられる。 特に青色の変化が分かりづらいのと黄色の暗い部分の色がきたないのが気になる。

なぜ上図のシェーディングが微妙に見えるかについて結論を言うと、陰影の表現が明度だけで行われているからである。 そのため、対策としては陰影の表現を明度だけでなく色相の変化でも行うようにすればよい。 この辺の話については以下のページが大変参考になる。 https://ofo.jp/blog1132881740.phtmlofo.jp

ということで、以下のような変更を加えて陰影表現を行ってみる。

  • 明度の減衰を和らげる
  • 明度の減衰の一部を色相の変化に置き換える (暗いところほど青色に近づける)

明度の減衰をゼロにすると表現できる色の範囲が狭くなるので、あくまで明度の減衰は幅を小さくするだけにしている。 また、明度の減衰をどの程度色相の変化に置き換えるかについては、 輝度ベースで考えると等値な変化が求められるのでそれを使えばどことなく正しそうな気がするが、 あくまで絵的な情緒を求める場合はそれが正解とも限らないので、適当に決める。

結果は以下のようになる。

f:id:mizuooon:20180410134037p:plain

さらに階調化を行うと以下のようになる。

f:id:mizuooon:20180410134046p:plain

逐一パレットを用意しなくてもドット絵で使われてそうな色味が得られている事がわかる。 あとは好みで明るい部分の彩度を落とすなどしてもそれっぽくなると思う。

今回のドット絵シェーダでは、面の陰影表現にこの方法を用いるほか、 縁取り(エッジ)部分の描画についても、こうして得られた暗い部分の色味を用いる。

シェーディングマップによる嘘陰影

例えばドット絵でツルツルしている物体を表現すると以下のような感じになる。

f:id:mizuooon:20180602161232p:plain

しかし、同じようなものを3Dで描画してみたものが以下である。(Unityのデフォルトシェーダ使用)

f:id:mizuooon:20180602163430p:plain:w155

ハイライトの位置が違っていたり影になってる範囲が案外狭かったりと、 いかにもドット絵的な陰影表現をしている上の絵と下の3Dのレンダリング結果とでは差があるのがわかる。 ドット絵的なシェーディングを行うには、こうしたドット絵的な嘘の表現を保ったまま3Dモデルを描画したい。

今回は、環境マッピングの考え方を応用したシンプルな方法でこれを実現してみる。 環境マッピングについてはこの辺を参考にするといい気がする。

床井研究室 - 第10回 スフィアマッピング

環境マッピングは、環境マップというテクスチャから、シェーディング点の法線に対応したテクセルをサンプリングしてシェーディングに利用する手法である。 今回はそれと同様に、 陰影用のテクスチャ(シェーディングマップと呼ぶ)から、シェーディング点の法線に対応するテクセルをサンプリングしてその点の『明るさ』とすることにする。 ただし、この際の法線はワールド座標系ではなくスクリーン座標系で考える。 すると法線は可視半球のみについて考えればよいことになり、シェーディングマップは半球のみで定義できる。

ややこしいことを言っているようだが、要するにまず以下のような画像を用意して、

f:id:mizuooon:20180602171526p:plain

シェーディング点が手前を向いているときは上記のシェーディングマップの中央あたりから、 右上を向いている場合は右上あたりから、といった感じで『明るさ』を取ってきて適用してやればよい。

これで球をレンダリングすると以下のような結果が得られる。

f:id:mizuooon:20180602171652p:plain

『明るさ』を適用する、という曖昧な言い方をしているのは、 使い勝手を考えると単純に輝度を反映するのが必ずしも最適とは言えず、バリエーションが考えられるからである。 ここではとりあえず明るさ0.5を基準として、その変位をもとの色に加えている。

これに対して解像度を落としてエッジ描画を加え、 先述の色相変化と組み合わせて描画すると以下のようになる。

f:id:mizuooon:20180602173734p:plain f:id:mizuooon:20180602173739p:plain f:id:mizuooon:20180602173747p:plain f:id:mizuooon:20180602175242p:plain

単純ながらもそれっぽい結果が得られている気がする。

適当にシェーディングマップを置き換えてやってもそれっぽくなる。

f:id:mizuooon:20180602175451p:plain f:id:mizuooon:20180602175420p:plain

この方法自体は非常に簡単なため、 マテリアルごとにシェーディングマップを設定してやることで例えば以下のように色々と応用が効く。

  • キャラクターの顔には影がかからないようにしたいので明るめのシェーディングマップを用意してやる、などができる。
  • メッシュの形状が単純な場合は、髪の毛のような異方性のある表現もある程度可能。
  • 波打ったシェーディングマップを用意し、サンプリング位置をアニメーションさせることで、たなびいているスカートなどが表現できる。

その他、シェーディングマップは手で描くことができるため、 結果を見ながらシェーディングマップを修正することもできアーティスティックな調整がしやすいという利点もあったりする。

頂点シェーダアニメーション

ドット絵アニメーションにおいて、髪の毛やスカートなどはフワフワたなびいている表現をされることが多い。 これについては、先程も少し述べたがシェーディングマップのサンプリング位置をアニメーションさせることで、ある程度表現できるが、 そもそものメッシュの形状を変化させることでより表現力を高めたい。

ということで上記に加えてさらに頂点シェーダによって形状の変化も行うことにする。 これは単に時間で波打って法線方向にメッシュを膨らませるだけの実装をした。 ただし、メッシュの破綻を抑えるためにカメラ座標系でZ方向の移動は無くした。

もちろんメッシュがごちゃごちゃしている場合には頂点移動させると容易に破綻するので こうした操作を行うことを考えたメッシュにする必要はあるが、 案外ちょっと位なら破綻していても気にならない。と思う。

これを使用した結果は後に載せる。

ドット絵的なモデルとアニメーション

以上を使えば3Dモデルをドット絵っぽく表示することができるが、 さらにドット絵らしく見せるためには適切なモデルとアニメーションの文脈があるので、 これについて少しまとめる。

  • ドット絵のキャラクターは通常の3Dキャラクターモデルよりも頭が大きく、手足が太く、装飾品は大きい。
  • ディティールは1ピクセル以下のサイズにならない。モデリングの際にはピクセルのサイズを意識した方がよい。
  • ドット絵のアニメーションは、ただの立ちモーションであっても、3Dキャラクターでやるとやや不自然に見えるほど揺れてる。

結果

現在作っているゲーム用のモデルでの結果を載せる。 3Dモデルは以下のようなものである。顔はまだない。

f:id:mizuooon:20180602184128p:plain:w300

肌、服、髪用のシェーディングマップは以下を用意した。 結構適当なのがわかるが案外これでなんとかなる。

f:id:mizuooon:20180602184309j:plain f:id:mizuooon:20180602184313j:plain f:id:mizuooon:20180602184318j:plain

結果は以下。

f:id:mizuooon:20180602184533p:plain

頂点シェーダによって髪やスカートにアニメーションを加えて動かしたものが以下。

www.dropbox.com

(一部ドットが潰れて長方形になってるのは単にエディタ上で見てるからなだけなので許してほしい)

問題点とか

キャラクターが横倒しになった場合、理論上シェーディングがおかしくなる。 が、現状では何故か気にならなかったのでとりあえず放置している。

その他テクニックとか

  • アニメーションのコマ数はある程度落とした方がドット絵っぽい。
  • 頂点シェーダのアニメーション周期をモデルのアニメーションと同期させるのはめんどくさいがしなくてもあんまり気にならない。
  • 8bitを名乗ると8bit警察に突っ込まれるので名乗らない。

今後

ドット絵的表現のためにはまだ色々足りない部分があるので追加したい。

  • 目鼻口のための専用シェーダ
  • 残像の描画
  • 服のフリルなどの綺麗な描画、アニメーション