UnityでToonなシェーダーを自作して自分が満足できるまでいじった話。
書いてたら長くなって疲れたので前後編に。
書いてたら長くなって疲れたので前後編に。
はじめに。
今回はモデル作成からの流れでUnityのシェーダを自作した話です。
最初は適当にネットで落ちているものを参考にコピペすればいいやと思っていたんですが、触っているうちにあれもしたいこれもしたいとなって、気づいたら色々いじってました。
どんなことをしたのか、備忘録的に書いておきます。
後編はこちら
最初は適当にネットで落ちているものを参考にコピペすればいいやと思っていたんですが、触っているうちにあれもしたいこれもしたいとなって、気づいたら色々いじってました。
どんなことをしたのか、備忘録的に書いておきます。
後編はこちら
Toon(とぅーん)
まずはベースになる「Toon」から。
「Toon」とは...つまりアニメっぽい感じです。
たとえばこんなかんじ左:Standard
右:Toon

明暗がハッキリクッキリとします。
これについてはこちらのサイトを参考に作成
Rampテクスチャの色を白〜黒にして使用しました。
【Unityシェーダ入門】トゥーンシェーダを自作してみる
「Toon」とは...つまりアニメっぽい感じです。
たとえばこんなかんじ

明暗がハッキリクッキリとします。
これについてはこちらのサイトを参考に作成
Rampテクスチャの色を白〜黒にして使用しました。
アウトラインの付け方
アウトラインは、絵でいうところの主線ですね。線画情報。
「法線方向に押し出す」がよく使われている手法らしいんですが、それだとカクカクしたモデルで隙間ができる問題が付いて回るらしいですね。
でもこの記事でデレステがこの方法だと書いてあったのでとりあえず試してみました。
こちらを参考に実装しました
Unityでシェーダー描いてみたい
しかし、これだとなんか物足りない...。
そこで色々探した結果(それまで目を反らしていたともいう)ポストプロセスという手法も検討することにしました。
ポストプロセスとは...なんかカメラ側で撮影した画像をあとから調整する感じ?
撮影後加工?そんなかんじだと思ってる。
で、色々探してこちらを参考にさせていただくことに。
【Unity】セルシェーディングを1から作ってみるメモ その3 脱Post-process【Shader】

これはいい感じなので、アウトラインはこれで決定。
ちなみにアウトライン色は単に黒ではなく、RGBをHSV変換して少し暗くした色にしています。
参考はこちら
【Unity】RGBをHSVに変換して明るさとかを変えるシェーダー
正直、シェーダーの実装、ここまででもいいんじゃないかな?とか思ってました。
この時点でよくわかってなかったし。
「法線方向に押し出す」がよく使われている手法らしいんですが、それだとカクカクしたモデルで隙間ができる問題が付いて回るらしいですね。
でもこの記事でデレステがこの方法だと書いてあったのでとりあえず試してみました。
こちらを参考に実装しました

しかし、これだとなんか物足りない...。
そこで色々探した結果(それまで目を反らしていたともいう)ポストプロセスという手法も検討することにしました。
ポストプロセスとは...なんかカメラ側で撮影した画像をあとから調整する感じ?
撮影後加工?そんなかんじだと思ってる。
で、色々探してこちらを参考にさせていただくことに。

これはいい感じなので、アウトラインはこれで決定。
ちなみにアウトライン色は単に黒ではなく、RGBをHSV変換して少し暗くした色にしています。
参考はこちら
正直、シェーダーの実装、ここまででもいいんじゃないかな?とか思ってました。
この時点でよくわかってなかったし。
法線マップ
色々あって、法線マップを適用したくなっちゃいました。
法線マップとは...まっ平らな面に凸凹の情報を与えられる魔法のような仕組みです。
参考はこちら
Unity シェーダーチュートリアル ノーマルマップ
この何もない壁が

法線マップ適用で突然それっぽくなります

個人的に、途中でふざけてレンガを適用したゴブリンがけっこう気に入ったり。
法線マップとは...まっ平らな面に凸凹の情報を与えられる魔法のような仕組みです。
参考はこちら

法線マップ適用で突然それっぽくなります

個人的に、途中でふざけてレンガを適用したゴブリンがけっこう気に入ったり。

ハイライト...?
とてもアニメっぽい表現になったし、法線マップも適用できたし、もういいかな?と思ったところで気づきました。
ハイライトがない。
これまで色々シェーダーをいじりながら調べたのでだんだんわかってきてます。
ハイライトを入れるなら、スペキュラーとグロスをいじればいいかな?
ということで適用したのがこちら

・・・なんか物足りない。(二度目)
光り方が自然すぎて、逆になんか変というか、アニメっぽくなくなってる気がします。
アニメっぽいハイライトってなんだ。
視点がどうこう関係なく、なんかこう白くなるアレ。アレがほしい。
(注:R18的なアレを隠す魔法の光ではない)
ということで、ここはRampで対応することにしました。
(壁の色が変わったのは、前回撮影までColorを適用するのを忘れていたから...。あまり影響ないので気にしないでください)

The 露骨!
うん、これくらいの方が好み。いいかんじ。
ちなみに実装方法はRampテクスチャをこうしておいて

取得できた値を2倍して適用してます。
一番右がRGB(172,172,172)。右から2番目がRGB(128,128,128)。 これにより255以上の数値を取得してめっちゃ明るくして光らせてます。
一番左はRGB(0,0,0)なので2倍しても0。一番濃い影は真っ黒のままです。
手法として正しいのかはわかりません...。
しかし、見た目は好みなので、ヨシ!
ハイライトがない。
これまで色々シェーダーをいじりながら調べたのでだんだんわかってきてます。
ハイライトを入れるなら、スペキュラーとグロスをいじればいいかな?
ということで適用したのがこちら

・・・なんか物足りない。(二度目)
光り方が自然すぎて、逆になんか変というか、アニメっぽくなくなってる気がします。
アニメっぽいハイライトってなんだ。
視点がどうこう関係なく、なんかこう白くなるアレ。アレがほしい。
(注:R18的なアレを隠す魔法の光ではない)
ということで、ここはRampで対応することにしました。
(壁の色が変わったのは、前回撮影までColorを適用するのを忘れていたから...。あまり影響ないので気にしないでください)

The 露骨!
うん、これくらいの方が好み。いいかんじ。
ちなみに実装方法はRampテクスチャをこうしておいて

取得できた値を2倍して適用してます。
一番右がRGB(172,172,172)。右から2番目がRGB(128,128,128)。 これにより255以上の数値を取得してめっちゃ明るくして光らせてます。
一番左はRGB(0,0,0)なので2倍しても0。一番濃い影は真っ黒のままです。
手法として正しいのかはわかりません...。
しかし、見た目は好みなので、ヨシ!
影が出ていない!
ハイライトを入れた後に気づきました。
この人たち、影が落ちていないんです...。おばけかな?

まずはなぜ影が落ちていないのかの調査から・・・も何も、Toonで参考にさせてもらったサイトではCustom Lighting Modelでatten(=attenuation, 減衰)を無視してますね...。
これを適用してあげれば、影が落ちました。

(なんかさらっと書いてますが、これに気づくまでには結構時間がかかりました。だってattenが何かなんて気にもしてませんでしたから。使ってないなら要らないんだろ、くらいに。)
この人たち、影が落ちていないんです...。おばけかな?

まずはなぜ影が落ちていないのかの調査から・・・も何も、Toonで参考にさせてもらったサイトではCustom Lighting Modelでatten(=attenuation, 減衰)を無視してますね...。
これを適用してあげれば、影が落ちました。

(なんかさらっと書いてますが、これに気づくまでには結構時間がかかりました。だってattenが何かなんて気にもしてませんでしたから。使ってないなら要らないんだろ、くらいに。)
影の単純化(前編)
さて、ここまでやれば今度こそもういい感じかな?と思ってじーっと画面を眺めていて、ふと思いました。
画 面 が 濃 い

なんか影が邪魔。すごく邪魔。画面的に煩い。
なければ文句を言い、あったらあったで文句を言う。わがまま。
ここで思いつくのは、影を落とすのをやめて丸影にしようという考え。
頭の中ではRagnarokOnlineの影を想像しています。
「足元に丸い影さえあればいい」。
じゃあどう実装しよう?と考えた時に思いついたのは次の2パターン
影を落とすのをやめて、足元に半透明の黒い丸を置く
影を落とすのをやめて、代わりに影を落とすオブジェクトを置く
同じようでいて影の実現方法としてはちょっと違います。
実際に影を落とすか、そもそも影をオブジェクトにしてしまうかの違い。
1つめをやるとジャンプとかした時に影オブジェクトだけ床に置いてくるの面倒くさそうなので却下。影が落ちる位置に段差があると影が浮く不具合もありそう。
2つめもうどうかなぁ...なんて思いながら調べてみると、同じことを考えた人がいたらしく以下の記事を見つけました。
【Unity】影の表現を軽量化するアイディア
ふむふむ。カプセルを利用。
そしてどうやらこの方法は処理の軽量化にも一役かうようですね。
ついでにProjectorなんて方法があることも知ったけど無視。
ただ、「SkinnedMeshRendererのCast ShadowsとReceive ShadowsをOFFにする」が気になりました。
「Receive ShadowsをOFFにする」とどうなるか。
せっかく前項で影を落としたのに、建物の影に入ってもキャラクターが影を受けなくなってしまいます。
左:Receive ShadowsをON
右:Receive ShadowsをOFF

これなら左がいいに決まってます。
ということは、なんとかして自分に影が落ちないようにしながら影用にMeshRendererなオブジェクトをおけばいいということ...なのかな?
ということで試しました。とりあえずカプセル。
キャラの影描画をOFFにして、カプセルはShadow onlyに。
サイズと位置を調整して...Cull Frontすればいいじゃん、と専用のshaderを作成。
結果がこちら
左:カプセルそのまま配置。足元にカプセルの影が落ちて暗い
右:Cull Frontした図。前面がないせいで影の開始位置が遠い

だめだこりゃ...。
とりあえずこの時点ではカプセルサイズを小さくして、なるべく違和感を無くして妥協することに。
最終的にはカプセルをやめて影用オブジェクトを自作してますが、長くなってきたので続きは後編に。
画 面 が 濃 い

なんか影が邪魔。すごく邪魔。画面的に煩い。
ここで思いつくのは、影を落とすのをやめて丸影にしようという考え。
頭の中ではRagnarokOnlineの影を想像しています。
「足元に丸い影さえあればいい」。
じゃあどう実装しよう?と考えた時に思いついたのは次の2パターン
同じようでいて影の実現方法としてはちょっと違います。
実際に影を落とすか、そもそも影をオブジェクトにしてしまうかの違い。
1つめをやるとジャンプとかした時に影オブジェクトだけ床に置いてくるの面倒くさそうなので却下。影が落ちる位置に段差があると影が浮く不具合もありそう。
2つめもうどうかなぁ...なんて思いながら調べてみると、同じことを考えた人がいたらしく以下の記事を見つけました。
ふむふむ。カプセルを利用。
そしてどうやらこの方法は処理の軽量化にも一役かうようですね。
ただ、「SkinnedMeshRendererのCast ShadowsとReceive ShadowsをOFFにする」が気になりました。
「Receive ShadowsをOFFにする」とどうなるか。
せっかく前項で影を落としたのに、建物の影に入ってもキャラクターが影を受けなくなってしまいます。
左:Receive ShadowsをON
右:Receive ShadowsをOFF

これなら左がいいに決まってます。
ということは、なんとかして自分に影が落ちないようにしながら影用にMeshRendererなオブジェクトをおけばいいということ...なのかな?
ということで試しました。とりあえずカプセル。
キャラの影描画をOFFにして、カプセルはShadow onlyに。
サイズと位置を調整して...Cull Frontすればいいじゃん、と専用のshaderを作成。
結果がこちら
左:カプセルそのまま配置。足元にカプセルの影が落ちて暗い
右:Cull Frontした図。前面がないせいで影の開始位置が遠い

だめだこりゃ...。
とりあえずこの時点ではカプセルサイズを小さくして、なるべく違和感を無くして妥協することに。
最終的にはカプセルをやめて影用オブジェクトを自作してますが、長くなってきたので続きは後編に。
まとめ
作業日
2020/04/28 〜 2020/05/05
- 2020.05.06 Wednesday
- Unity
- Newer: 自作シェーダー 後編
- Older: モデル作成 + 表情アニメーション