simplestarの技術ブログ

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

Unity:VRMのMToonマテリアルをLightweightRPのShaderGraphで、その3で公開

# はじめに

内容は玄人向けですが、はじめての方にもわかりやすく説明します。

  • Unity とは、ゲーム開発するときに、共通で必要になるものをだいたい用意してくれるツールです。(基本無料です。)
  • VRM とは、Virtual Reality における3Dモデルデータを扱うためのファイルフォーマットです。(世界に先駆けて日本が作っているので応援してる)
  • MToon とは、VRM の標準の Toon シェーダーです。(イラスト調の見た目を作る陰影計算ロジック)
  • UniversalRP とは、本記事まで LightweightRP と呼んでましたが、Unity がこれからメインとして扱うシェーダを書く場所
  • ShaderGraph とは、UniversalRP のシェーダーをノード(要素)とエッジ(連結線)で描くデザイナ脳で作れる Unity 内のツール

# 公開しました

細かい事抜きにして、動くコードを公開しました。

github.com

README の手順に従って LightweightRP でも描画確認できた方は続けてお読みください。

置き換えたファイルが何しているかをイメージするための資料を以下に書き留めていきます。

# 差分をとって PBR シェーダーとの差異を確認する

今回移動してもらったこちらの C# ファイルは、シェーダーコードを構築する仕事を行います。
Packages\com.unity.render-pipelines.lightweight@5.16.1\Editor\ShaderGraph\LightWeightToonSubShader.cs

ベースとなる実装は最初から存在する物理ベースレンダリング(PBR)の実装です。
差分を Visual Studio Code のコマンドパレットの差分チェックなどで見てみてください。
基本的な部分は全部一致していて、Shade カラーとテクスチャ、Outline の幅の情報を渡す口だけが増えていることを確認できます。
ほんと、差異はこれだけです。

f:id:simplestar_tech:20190721101843p:plain
差分を取ると、スロットが追加されているだけ

シェーダーコードを構築するのが先程紹介した SubShader.cs ファイルの仕事ですが、これをもう少し具体的に解説すると
ある .template テキストファイルをベースに、文字列置換しながらシェーダーコードを完成させます。

具体的にどの .template ファイルを利用しているかはこちらのコードから確認できます。

f:id:simplestar_tech:20190721102148p:plain
.template ファイルのテキスト読み込み部分

ここまで理解が進めば、あとは .template で陰影計算ロジックを書いているんだろうと予想がつくでしょう。そのとおりです。

こちらも、PBR のものをベースに作成しています。
差分をとってみるととても納得できると思います。

差分を見るのは次のファイルです。
Packages\com.unity.render-pipelines.lightweight@5.16.1\Editor\ShaderGraph\lightweightToonForwardPass.template

f:id:simplestar_tech:20190721102618p:plain
.template を Toon(左)と PBR (右)で比較したときの様子

f:id:simplestar_tech:20190721102803p:plain
.template を Toon(左)と PBR (右)で比較したときの様子(色決定部分)

差異をもう少し注意深く追うと、Toon の方は outline pass と書かれたもう一つ、とても似たレンダリングパスの記述が確認できると思います。
こちらです。

//
// outline pass
// 

Pass
{
	// Material options generated by graph
${Tags}
${Blending}
Cull Front
ZTest Less
${ZWrite}

Unity シェーダーでこの記述は、ポリゴンの裏面を、表面よりちょっとだけ奥に配置するようにして正しい奥行きで描画という意味になります。(イメージできますか?)
もう一つ重要なのは、スクリーン座標における頂点位置の拡張です。(位置を法線方向にずらす処理)

        o.clipPos = TransformOutlineToHClipScreenSpace(v.vertex.xyz, v.normal.xyz, vd.OutlineWidth);

脳内でイメージを作って読んでいた方はこれで納得できると思いますが、この処理により、輪郭線が表現されています。

f:id:simplestar_tech:20190721103827p:plain
顔の輪郭線が表現されたときの様子

# 肝となるシェーダーロジック

ここまでの解説を要約すると PBR シェーダーをベースに、影色と輪郭線の太さパラメータを利用することと輪郭線を描くパスが追加されたというものでした。
気になるのは要所で出てきた次の2つの関数ですね。

  • LightweightFragmentToon
  • TransformOutlineToHClipScreenSpace

実装は次のファイルに書かれています。

Packages\com.unity.render-pipelines.lightweight@5.16.1\ShaderLibrary\Lighting.hlsl

コメントで // for Toon Shading と書かれている行より上は置き換える前のファイルとまったく同じコードです。手入れていません。
その下に続く関数については License 表記にあるとおり、こちらは
LiliumToonGraph をベースに Fragment 関数を簡略化したものです。
github.com
ToonyIntensity の計算はオリジナルの MToon と全く同じロジックを使いました。
github.com

あとは Normal と SphereAdd (MToon 特有のモデル周辺が黄色く光る表現)は次のとおり Shader Graph で表現しました。
こちらは simplestar の完全オリジナル版

f:id:simplestar_tech:20190721111531p:plain
MToon の SphereAdd の座標計算をグラフ表現してます

# カスタム Master Node

次のクラスが上記の Toon Master という名前のノードを定義しています。直感で理解します。
Packages\com.unity.shadergraph@5.16.1\Editor\Data\MasterNodes\ToonMasterNode.cs

# まとめ

解説は以上になります。
なんかすぐに Universal RP 対応が入りそうなのですぐに古い情報になってしまいそうですが
勘所としておさえ、新しい環境にも容易に移植できる技術者が増えることを期待します。

ここまで読んでくださりありがとうございました。