
Amplify Shader EditorでのUnity用トゥーンシェーダーの作り方
▼諸注意
◇免責事項
本記事に書かれている内容や、BOOTHで配布されている変換器は自己責任で利用してください。使用に際して発生したトラブル・不利益に関して記事の執筆者である射当ユウキは一切の責任を負わないものします。
◇制作物の配布先
今回の解説に使用したサンプルのトゥーンシェーダーはBoothにて配布します。
またBoothの同ぺージには上記のサンプルシェーダーの機能を拡張したバージョンも置いています。
【URL】https://tar.booth.pm/items/4376066
◇著作の引用に関して
本解説記事は著作権法が認める範囲での引用が可能です。
本記事の内容を元に新たな解説記事や動画を制作・発表する、あるいは新規アセットの制作や配布や販売をしても問題ありません。
◇制作に使用したソフトとアドオン
本作を制作するにあたって使用したUnityのバージョンは2019.4.29f1です。
また、アドオンのAmplify Shader Editorはバージョン1.9.1.2を使用しました。
▼01:基本設定
◇ASEの基本的な使い方
Amplify Shader Editor(以下ASEと呼称)は、ノードを使ってUnity用のシェーダーを制作できるアセットです。なお有料のアセットです。
ノード編集とは、以下の図のように演算などを線でつないでプログラムしていく方法をいいます。

ASEは他のUnityアセットと同じくアセットストアで購入した後にアセットストアからインポートして使います。
ASEでシェーダーを作る場合は、以下の図のように『Create』から『Amplify Shader』内にある『Surface』を選択します。

◇基本設定
シェーダーを追加したら通常は自動でASEの編集画面が立ち上がります。
今回の記事ではトゥーンシェーダー(影が階調化されているシェーダー)を作ります。
主にキャラモデルでの使用を想定したトゥーンシェーダーを作るために、以下のように設定を変更しておきます。
【Light Model】
『Unlit』を使います。
【Cull Mode】
『Off』にします。これでポリゴンが両面表示になります。

▼02:ベースカラーの表示方法
◇テクスチャを反映させる
トゥーンシェーダーを作るにあたって、まずメインカラーを設定できるようにする必要があります。
多くの場合、モデルのメインカラーはテクスチャの割り当てによって実現するかと思います。なので、テクスチャを設定できるノードを追加します。
スペースキーを押すとノード一覧が表示されるので検索バーに『Texture』と入力して『Texture Sample』のノードを追加しましょう。ノードを追加したらそれを『Emisson』に繋ぎ、inspectorエリアでテクスチャ画像を設定します。

◇色味の変化方法
以上でメインカラーの設定は完了ですが、モデル全体の色味を変更したい場合には色味を追加する用のノードを追加します。
『Color』ノードを追加します。このとき『Color』ノードのタイプをpropertyに変更しておきましょう。Propertyに変更することでinspectorエリアで任意の色に変更可能になります。Propety名はここでは『Tint』としておきます。

『Multiply』ノードを経由して先ほどの『Texture Sample』と合成します。これは何をしているかというと、ペイントソフトで言うところの乗算レイヤーを使っているみたいなものです。『Texture Sample』で設定したテクスチャ画像に『Color』で設定した色を載せているのです。

なお、property(inspectorで変更できる項目)の順番は、編集画面の何もないところをクリックした状態で現れる以下のパネルで変更できます。

◇見やすいノードレイアウトにするための機能
ここまで組んだノードの先に『register』ノードを追加します。これは言うなればセーブポイントみたいなもので、それ以前のノードを『Get』ノードを使って呼び出せるようにできます。


また、作ったノードはレイアウト用のフレームで括っておくと一塊のグループとして見やすくなります。フレームはキーボードのCキーを押すと追加できます。

▼03:トゥーンシャドウの設定
◇法線ベクトルとライトベクトルの内積
次にトゥーンシェーダーの肝であるパッキリと別れた影を表示できるようにします。
そのためにはまず、オブジェクトに影が表示できるようにする必要があります(この段階ではまだ普通のグラデーションがかった影で大丈夫です)
オブジェクトに影を出すためにはオブジェクトの法線ベクトルとライトの向きの内積をとります。以下のようなノードを追加します。

これにより以下のようにライトの向きを変更することで影の表示が変わるようになります。

今のままだと影が濃すぎるので、明るさを足すために『Add』ノードを使用します。

なお、ここまで作った法線ベクトルとライトの向きの内積の値の先に『register』ノードを追加します。

また、フレームを追加して見やすくしておきます。

◇Stepノードでトゥーンシャドウ用のマスクを制作
ここから影を階調化させていきます。
一番シンプルな影を階調化させる方法は『Step』ノードを使う方法です。

すると以下のように影の境目がハッキリした状態の表示になります。

◇Remapノードで影の縁をぼかせるようにする
上記の方法でもトゥーンシャドウは実現可能です。
しかし使用するモデルの仕様によっては影の境界を適度にぼかしたい場合もあるかもしれません。また影の範囲を広く取りたいのか、少し影が出ればいいのかも設定できるとデザインする上で融通が利きます。
そこで『Remap』ノードを使い、以下のようにノードを組みます。


これにより以下のgif動画のように影の出る範囲や、ぼかし具合を制御できるようになります。

完成したコメントフレームを使ってグループ化しておきます。

◇影マスクを使って影に色を設定する
上記の説明で黒っぽい影をモデルに割り当てることが可能になりました。しかし、トゥーンシェーダーの影の色は任意に変更できないとデザイン上で不便です。
そこで以下の用にノードを繋ぎます。ネックは『Lerp』ノードを使って影にしたい部分とそうでない部分を切り替えている点です。

◇ライトが無いワールドのための措置
さて、これで影の階調化と影色の変更は実装できました。
しかし、このシェーダーをVRChatなどワールドにライトが設置されていない状況で使う場合に不具合が出るときがあります。というのも、このシェーダーはあくまでワールドに設置されているライトの向き(ライトベクトル)を影の制御に使っています。なので、ワールドにライトがない場合はベクトルの値が0となり、シェーダーを割り当てたオブジェクト全体に影がかかってしまい強制的に暗い色になってしまいます。

ライトがないワールドというのはVRChatには意外とあって、デフォルトのホームワールドもその一例です。
オブジェクトの全てが強制的に影になるのを防ぐために、ライトがない(明るさの値がゼロ)の場合には影の表示をしないように切り替えられる仕組みが必要です。
結論から言うとライトの有無による影を使うか否かの切り替えは以下のようなノードで実現します。

上記のノード組みについて解説しておくと、『Light Color』ノードを使ってライトの値(色)を入力して、『Sign』ノードによって値がゼロでないなら強制的に値を1に、値がゼロならばゼロのままに変換します。その上でLerpを使って『Sign』を通したライトの色が1ならば影付きの色を使用し、ゼロならば影無しのメインカラーを使用するという仕様です。
実際にライトの有無で影の出力を切り替えている例が以下のgif動画になります。

▼04:シャドウマップの設定方法
◇いつでも影が出ていてほしい場所を指定する
シャドウマップとは、いつも影が出ていてほしい場所に強制的に影を追加する機能をいいます。
例えば、以下のUVマップ上の左上(顔のUVマップの目元)には『W』と『A』という文字が描かれています。

以下のようにノードを追加して、このシャドウマップ用のテクスチャの『W』と『A』を強制的に影として表示させます。

すると以下のように例え光が当たっている状態でも『W』と『A』を常に影として表示させ、ライトが当たらなくなると他の影に埋もれるような表現を実現できます。

▼05:輪郭線(アウトライン)の設定方法
トゥーンシェーダーの特徴的な機能として階調化された影の他にも、アニメやイラスト風の輪郭線(アウトライン)の表示があります。
ASEでのアウトラインの表示を実現するノードは以下の通りです。

▼EX:ライトとの角度でシャドウマップを切り替える
◇キャラモデルの顔の影は奥が深い
ここまでがトゥーンシェーダーの影表現での主要機能の解説となります。ここから先は応用的な内容についての解説です。
キャラモデルにトゥーンシェーダーを適応する場合に、工夫が必要となる箇所に顔の影があります。というのも、トゥーンシェーダーはアニメやイラスト的な表現を実現するための機能なのですが、キャラモデルの頭部というのは基本的にそのままの状態ではライトを当ててもアニメっぽい影の形にはなりません。

例えば下記のリンク先の資料の60ページ目~84ぺージ目の内容のように、キャラモデルの顔の部分については法線編集という処理を施してアニメっぽい影にするといった手法などがあります。
【リンク】
GUILTY GEAR Xrd開発スタッフが送るアニメ調キャラモデリングTIPS
https://www.slideshare.net/ASW_Yokohama/guilty-gear-xrdtips-124324946
法線編集の他にも前述のシャドウマップを応用してライトとの角度をベースに影の形を切り替える方法もあります。実際にやってみたものが以下のgif動画になります。

以下にその方法を解説します。
◇ライトとの角度でのシャドウマップの切り替え:下準備
まず上記の方法を実装するためにテクスチャ作りに一手間加えます。
テクスチャを作る際に、顔部分のUVマップを左上に寄せます。

その上で、影の位置を指定するマスクテクスチャを用意しておきます。左上の顔UVのある部分を白く塗りつぶしておきます。

そして、顔の影として使いたい形状を16パターン描いておきます。この顔の形を切り替えて顔の影にするわけです。

◇ライトとの角度でのシャドウマップの切り替え:ノード
次に、上記の下ごしらえしたテクスチャを反映するためにノードを組んでいきます。
先にノードを載せておきます。

上記のように割と長いノード組みなので、以下のいくつかのパートに分けてアップにした画像をば。



それぞれのパートが何をしているのかざっくり解説していきます。
◇ライトとの角度でのシャドウマップの切り替え:補足
◎ノード序盤
顔のエリアがどこかを指定するテクスチャを読み込みます。このとき、ついでに顔部分の法線が真正面を向くように指定しています。

◎ノード中盤
顔の影の形となるテクスチャを切り替えられるようにしています。
もう少し詳しく書くと、ノード序盤で顔の法線を正面に向けているので、それに対してライトの向きとの内積をとっています。内積をとることにより顔に0~1の値のマスクを生成しています。ただし、それだけでは顔の形の指定テクスチャをステップ的に切り替えられません。(ただスライドするだけになってしまう)。UVマップのU方向とV方向でステップ移動できるようにしています。
また、顔部分の疑似法線ベクトルとライトの向きの内積をとっただけではライトが右から当たっているのか左から当たっているのかまでは判別できません。それを解消するために顔の疑似法線ベクトルとライトの向きの外積を取り、ライトの当たっている向きが右か左かを判定しています(『Sign』ノードの使用)

◎ノード終盤
シャドウマップの切り替え用に使う顔の影の形となるテクスチャを呼び出しています。
このとき、『FaceAreTiling』(2次元ベクトルノード)を使い、メインカラーのテクスチャの顔エリアが16分の1に出来なかった場合のための調整を行っています。


◇ライトとの角度でのシャドウマップの切り替え:例外処理
以上がライトとの角度でシャドウマップの切り替える方法の実装解説となります。
とはいえ、この方式にはいくつか問題があってその一つがライトが真上から垂直に設定されているワールドではそもそもシャドウマップを切り替えようがないという点です(なぜならライトが垂直に当たっていると垂直軸方向にキャラモデルを回転させてもライトとの角度が変化しないので)。
ライトが垂直に設定されているワールドも稀にあるので、もしそうならばそもそも影が表示されないように自動設定されるノードを組んでおきます。
