simplestarの技術ブログ

目的を書いて、思想と試行、結果と考察、そして具体的な手段を記録します。

Unity:VRMのMToonマテリアルをLWRPのShaderGraphで、その1

自分だけの3Dキャラクターをあらゆるゲームタイトルに登場させるというのは、ゲームが始まった頃からの全人類の悲願でした(二回目)
長い間、キャラクターエディットという、ゲーム開始時のアバター選びという機能の提供が続いていましたが、ついに一つ作れば、どのゲームにも入れる時代となりました。(なろうとしています)
https://vrm.dev/images/vrm/vrm_app.png
画像は 「VRM」って何?どんなことができる? - VRM より

Unity で LWRP の時代が到来しようとしているところ
VRM の Unity インポートまわりは MToon マテリアルを使用していて
そのまま使うとシェーダーエラーになります
simplestar-tech.hatenablog.com
簡易対応は絵を破壊してしまうので、本対応が待たれていますが…あれから三ヶ月
対応にはもう少し時間がかかりそうという雰囲気を感じたので、資料をもとに再現する Shader Graph を作って、作り方を記録します。

まずは Unity の LWRPについて理解しましょう。
simplestar-tech.hatenablog.com

…何が、Unity 最高!だよ(最高だけど)

Shader Graph について理解はこちら
simplestar-tech.hatenablog.com

…いったい、何に勝つんだか…

そして、MToon について理解を深めます。
github.com

具体的にはこちらを読み解きました
https://niconare.nicovideo.jp/watch/kn3485

ここで、これから作りたいものが形になり始めます

# 草案

パラメータ全ては移動できそうにないですが、いくつか代表的なものを引っ越ししたいと思います。

  • Lit & Alpha
  • Shade

のカラーとテクスチャの両方は必須で引っ越しするとして

輪郭線の太さとカラー
幅制御テクスチャは可能なら調べて対応してみたい

Shadeing Shift
Shading Toony
は考えどころ、Shader Graph についてもっと深い理解が要求されますね…

リムライトテクスチャは、機能を探して対応してあげたいが

そんなところですね。

Shader Graph で Toon レンダリング

まずは数万人の開発需要と数億人のユーザー需要がある中
新しい Shader システムに Toon が存在しているかどうかを確認してみましょう。

まさに MToon をリファレンスして LWRP で Toon 表現を行うカスタムマスターノードの例が見つかりました。
github.com

まずは Shader Graph でカスタムマスターノードを用意する方法について確認してみましょう。

# Shader Graph 入門

まずはこちらを読みます。
Home · you-ri/LiliumToonGraph Wiki · GitHub

なるほど、Packages フォルダ以下へ Cache 下にあった Shader Graph コード一式を移動し、勝手に初期化される問題を回避し
まずは CreatePBRShaderGraph.cs を真似て、CreateToonShaderGraph.cs を作る

using System.IO;
using UnityEditor.ProjectWindowCallback;

namespace UnityEditor.ShaderGraph
{
    class CreateToonShaderGraph : EndNameEditAction
    {
        [MenuItem("Assets/Create/Shader/Toon Graph", false, 208)]
        public static void CreateMaterialGraph()
        {
            ProjectWindowUtil.StartNameEditingIfProjectWindowExists(0, CreateInstance<CreateToonShaderGraph>(),
                string.Format("New Shader Graph.{0}", ShaderGraphImporter.Extension), null, null);
        }

        public override void Action(int instanceId, string pathName, string resourceFile)
        {
            var graph = new GraphData();
            graph.AddNode(new PBRMasterNode()); // ←ここをカスタムノードクラスに置き換える!!
            graph.path = "Shader Graphs";
            File.WriteAllText(pathName, EditorJsonUtility.ToJson(graph));
            AssetDatabase.Refresh();
        }
    }
}

まずはこんな感じの作業で、UI に Toon Graph の項目を作り、既存の PBRMasterNode が作られるところまで確認できました。

f:id:simplestar_tech:20190715030756j:plain
Toon Graph がメニューに追加されていることを確認

コメントで強調したとおり、PBRMasterNode クラスを真似て、ToonMasterNode クラスを作ってみます。

PBR を一括でドキュメント内 Toon リプレースしてみました。
IToonSubShader, ToonSettingsView クラスの2つを用意しろと怒られますね。

元の PBRのインタフェースと View クラスの方もリネームして用意しましょう。

IPBRSubShader は ISubShader しか継承してなかったので、元の IPBRSubShader を利用するように戻しました。
ToonSettingsView はクラス名だけ変えた引数を受け付けるだけなので、作る必要はあるけど、内部のロジックは PBRSettingsView.cs クラスをそのまま持ってきました。

ここをカスタムノードクラスに置き換える!!のコメントを ToonMasterNode に置き換えて
一旦ここでコンパイルが通る状態

試験的に Toon Graph を作ってみると…
あれ、エラーに…仕方なく IToonSubShader を IPBRSubShader に似せて作って再チャレンジ

f:id:simplestar_tech:20190715093106p:plain
ノットコンパチのエラーが出るけど、カスタムマスターノードとして UI 操作ができる状態に

ToonSubShader を用意する

参考にしているプロジェクトでは LightWeightToonSubShader : IToonSubShader クラスを用意している
このインタフェースが実装されると、Shader Graph で有効になるのかな?

Unity オリジナルを参照するにはここまで Shader Graph の中を見てきましたが、ここからは LightWeightRP の Package の中を見ていくことになります。
そこには確かに
LightWeightPBRSubShader.cs
lightweightPBRForwardPass.template
lightweightPBRExtraPasses.template
がありました。

仕方ないので、LightWeightRP もカスタムする目的で Library\PackageCache から com.unity.render-pipelines.lightweight@5.16.1 を Packages フォルダへ移動します。

上記3つのファイル名や中身について、あらゆる PBR を Toon に置き換えたものを同フォルダへ配置してみましょう。

こんな感じ

f:id:simplestar_tech:20190715102406p:plain
LWRP の Pacage 配下へ Toon リネーム系を配置

Shader Graph を Toon から作ってみると、ちょっとだけエラーメッセージが変わりました。

f:id:simplestar_tech:20190715103056p:plain
変わったエラー

該当箇所について周辺を読んでみます。
おそらく LightweightFragmentPBR 関数がどこか hlsl として記述されていて、これをインクルードして使えるようになっていて
Toon に置き換えたものがどこにも無いのだと思われる

grep で探すとヒット

ShaderLibrary/Lighting.hlsl

///////////////////////////////////////////////////////////////////////////////
//                      Fragment Functions                                   //
//       Used by ShaderGraph and others builtin renderers                    //
///////////////////////////////////////////////////////////////////////////////
half4 LightweightFragmentPBR(InputData inputData, half3 albedo, half metallic, half3 specular,
    half smoothness, half occlusion, half3 emission, half alpha)
{
    BRDFData brdfData;

そこに関数を複製して Toon サフィックス付けて配置するとどうでしょう?(以下のように endif の手前に挿入した)

}

half4 LightweightFragmentToon(InputData inputData, half3 albedo, half metallic, half3 specular,
    half smoothness, half occlusion, half3 emission, half alpha)
{
// 省略
}
#endif

これで Toon Graph を作成したら、うまくいきました。

f:id:simplestar_tech:20190715105715p:plain
カスタム Master Node で正しく LWRP シェーディングできている様子

f:id:simplestar_tech:20190715105937p:plain
見た目は PBR そのままです。そっくりそのままですもんね…

続いて Toon カスタムしていきます。
長くなったので2つに分けます。

続き
simplestar-tech.hatenablog.com