この記事は KLab Engineer Advent Calendar 2022 24日目の記事です。
Qiitaには毎年このアドベントカレンダーの12/24に投稿させて頂いていて、今回で4年目になります suzuna-honda といいます。よろしくお願いします。
はじめに
Unity / Universal Render Pipeline(以下URP)上で独自のシェーダを作成する際、標準で提供されている選択肢はおおよそ、「HLSL+ShaderLab (コードベース)」か「ShaderGraph (GUIベース)」のどちらかになります。
HLSL+ShaderLab (コードベース)
古くから存在する、Unity標準のシェーダ記述手段です。
ShaderLabと呼ばれる専用言語を用いて、HLSLによるシェーダ実体を取り巻くさまざまを管理できます。
コーディングが必須となりますので、ある程度プログラミングに慣れている人間でないとハードルが高いです。
また、Built-in Render Pipeline(以下BRP)とURPとの互換性が足りておらずコードの修正が必要になったり、URPのバージョンアップによっても更なる書き換えが必要になったりします。要は色々面倒くさいです。
ShaderGraph (GUIベース)
Unity2018から登場したURP/HDRPといった新しいレンダーパイプラインに付随して提供されている、GUIにてシェーダを作成する手段がShaderGraphです。
新しめのUnityであれば、BRPにもShaderGraphが用意されています。ので、古くから開発/運営を続けていてURPに移行するのがしんどいタイトルであってもShaderGraphを利用することは出来ます。Unityのバージョンアップという地獄さえクリアできれば、という重たい条件はありますけれども...
ShaderGraphって実際どうなの?
このエントリでは、Unity / URP環境においてShaderGraphをどのように扱っていくのかについて、実際にタイトル開発を行って得られた知見から「今後もし新規タイトルを開発するならこうする」というスタイルで色々語ってみたいと思います。(HDRP?知らないしどうでもいい!ハイエンドグラフィックスやりたかったらUnrealEngine使ったほうが良いですよ!)
ShderGraphのメリット
ShaderGraphを使うことによる大きなメリットとして、以下の2つが挙げられます。
アーティストさんでもシェーダを作ることができる
こちらに関しては説明不要ですよね。
これまでシェーダはグラフィックスエンジニアが作るものであって、アーティストさんがシェーダを利用して何かを表現したくとも
「アーティストさんがエンジニアに用意して欲しいシェーダの雰囲気を伝える→それっぽいものをエンジニアが作ってアーティストさんに見せる→イメージと違うな……と首を傾げ別の伝え方をする→お互いに融通が効かねえなと陰で愚痴る→以下無限ループ」
といった作業効率の悪いワークフローを必要としていました。
ShaderGraphを使えば、ShaderGraphで表現できる範囲内であればアーティストさんサイドだけで作業を完結させることが出来ます。
URPの仕様に沿った煩雑なShaderLabを書かずに済む
こちらはあまり話題になりませんが、プログラマがシェーダを用意する状況に於いてもShaderGraphは役に立ちます。
ノードベースのGUIでシェーダが作れること自体は特に嬉しくはなく、むしろ回りくどい手間が必要になることはマイナスですらあります。では何が嬉しいのかというと、シェーダ実体以外のShaderLabによる周辺情報のセットアップを全てShaderGraphが自動生成してくれること、なんですね。
URPには数多のレンダーパスが存在します。普通のポリゴンメッシュをフォワードで描画するUniversalForward、ディファードでのGBufferへのフィルを行うUniversalGBufferといったメイン用途となるパスだけではなく、シャドウマップを構築するShadowCasterパスや、DepthTextureを構築するDepthOnlyパスなど、それぞれのパスに合わせたシェーダを一つ一つ、コードベースでシェーダを用意する場合には .shader の中に記述してやる必要があります。
付け加えると、このレンダーパスはURPのバージョンアップによって追加されることがあります。その都度、コードベースのシェーダは書き換えに迫られるリスクがあります。
ちなみに少々余談になりますが、このパス毎のシェーダ表記に抜けがある状態でその抜けたパスをシェーダで利用しても、何のエラーも出ません。エラーは出ず、Editor環境では特に問題なく動いたりしますが、実機ビルドすると特定のGPUでのみ変な挙動を巻き起こしたりします。私は以前このバグにだいぶ苦しめられました。Unityがエラーメッセージ一つ出してくれれば一瞬で修正できたようなバグなのですが、UnityはGPU/描画周辺について「バグっていても何も言わずただ絵が壊れる(ことがある)」という不可思議なポリシーで実装されており、そのデバッグは困窮を極めます。そういうもの、なので受け入れましょう。
閑話休題...URP用にコードベースのシェーダを用意する際には、SRP Batcherに対応する為の条件を揃えたりといった、今までBRPでシェーダを書いていた人にとっても改めてURP用のShaderLabの書き方を学び直す必要があります。またBRP用の過去のシェーダをURPに使い回す際にも、URPに対応できるようがっつりとShaderLabを書き直す必要があります。
今まで内製エンジンやUnrealEngineなどでシェーダを書き慣れている人が初めてUnityを触るような場合にも、コードベースだとShaderLabを覚えなければなりません。このようなシチュエーションであっても、ShaderGraphを使えば改めてShaderLabを学習する必要はないので入りが楽です。まあいずれ最終的にはShaderLabと多かれ少なかれ付き合うことにはなるのですが...
以上のような、不毛で退屈なShaderLabとの付き合いをShaderGraphは肩代わりしてくれます。
「いやー、でも俺プログラマだし、いちいちマウスでポチポチしてシェーダ作りたくないんだけど...」
はい、分かります。この件についてはすぐ後述します。
ShderGraphのデメリット
ShaderGraphを採用することには、残念ながら、メリットだけではなくデメリットももちろん存在します。
ここではそのいくつかの代表例とその対処/改善方法について解説します。
ShderGraphのデメリット1: Diff(差分比較)がまともに機能しない
GUIベースである以上どうしようもないのですが、ShaderGraphで書き換えた内容の差分を確認することが非常に難しいです。シェーダを修正しGit等でPRレビューする/されるなんてのは日常風景ですが、Diffでどこの何をどう書き換えたのかを判断するのはほぼ無理ゲーです。そこそこ致命的な問題点です。
この問題についてはUnity社としてももちろん把握はされているようで、ShaderGraphにて出力されるjsonをDiffで確認しやすくなるようなフォーマットに変更したり...といった細かい修正は行っているようです。とはいえ根本的な改善には至っていません。将来的にはUnityEditor上でShaderGraphの差分を抽出してハイライト/差異を表示、またはそれに類する機能が必要になるかと思いますが、当面は望みは薄いでしょう。
で、今現在の具体的な対処法ですが、まずグラフィックスエンジニアが用意するシェーダと、アーティストさんが用意するシェーダでそれぞれルールを分けます。
グラフィックスエンジニアが用意するシェーダの場合
こちらでは、ShaderGraphを使う状況においてもノードベースなGUIによるシェーダ開発はしません。
ちょっと何を言っているのか意味不明ですが、つまり、実体となるシェーダコードは基本的に全てHLSLとして別ファイルにコードベースで記述し、CustomFunctionにて参照する、という実装をレギュレーションとして取り決めします。
このようにシェーダを作成すれば、ShaderGraphの恩恵を得つつ、シェーダの実体はコードベースなのでDiffも正しく働きます。ShaderGraphを本質ではないガワの自動生成をしてもらう便利機能として利用させてもらうわけです。
またこれには、URPのバージョンアップによってShaderLabの仕様変更が加わったとしても、ShaderGraphがその差異を吸収してくれるというメリットもあります。
そして前述した「プログラマなのにGUIでシェーダ作る不毛さ」を回避することも出来るわけです。一石三鳥ですね。プロパティ追加したりノードに繋げたりといった最低限のGUI操作は必要になりますが、そこまでの手間ではないので許容範囲でしょう。
(一応の例外として、わざわざHLSLとして記述するまでもないくらい単純な、具体的にはマスターノードを除いてノード数が3つくらいまでの単純なShaderGraphであればそのままノードベースで書いてもいいよ、というルールも用意しています。まあこの辺りはギチギチに縛るより柔軟に動いたほうが良いんじゃないかなと思っています)
アーティストさんが用意するシェーダの場合
アーティストさんが用意したシェーダに関しては、そもそもレビューをしません。レビューをしないので、Diffが機能しない云々の問題も影響ありません。
当然、「レビュー無しで大丈夫なの...?」となりますよね。そこはレギュレーションとバリデーション用ツールで縛ります。
例えば、以下のようなシェーダの命令数によるレアリティの概念の導入です。
シェーダ命令数によるレアリティ縛り
グラフィックスエンジニアは常にシェーダの負荷を意識して、可能な限り軽いシェーダを作ろうと無意識下に刷り込みが完了してしまっています。しかしそれをアーティストさんに理解してもらうのは難易度が高いです。現実問題として、好きに作ってもらったShaderGraphを確認したら、びっくりするような重たいシェーダが出来上がっていて頭を抱えたことがあります。ということもあり、アーティストさんがShaderGraphでシェーダを作ってもらう際には、レギュレーションにてシェーダの負荷に縛りを入れます。
ただし「シェーダの負荷」を定量化することは、これはこれで難しいです。特定のハードウェアに特化して良いのならば不可能ではありませんが、今はそんな時代ではなく、モバイルなり据え置きなり様々な環境をマルチに対応する必要があります。ので、まずは「おおよそ」として、以下のような手段で「シェーダの負荷」を数値化します。
1) シェーダのインスペクタから"Compile and show code"の右の逆三角形を押し、
"Custom->D3D"と"Skip unused shader_features"にチェックが入った状態にします。
2) "Compile and show code"を押すとテキストが出力されます。
3) テキストの序盤に出てくる"Stats for Vertex/Fragment shader"の欄を確認します。
要はD3Dの中間コードのシェーダ命令数を擬似的に「シェーダの負荷」として扱うわけです。もちろんこの数値がそのまま負荷とリニアに繋がっているわけではありません。厳密な描画負荷管理にはならず、あくまでおおよその指標にしかなりませんが、それでも十分に判断基準としては働くはずです。
Vertex shader側の数値が問題となることは滅多にありません。重要なのはFragment shader側の数値です。
branch数やtexture数も非常に重要な指標になり得ますが、シンプルでないと運用が面倒くさくなるだけなので、math(avg)の数値をシェーダの負荷と見做し、この数値が幾つ以上かでシェーダの「レアリティ」を決めます。
レアリティは例えば「コモン」「レア」「エピック」「レジェンド」のような3-4段階ほど用意すれば十分でしょう。
レアリティの低いシェーダは、何回も画面を塗りつぶされるようなVFXや一般的なUIなどで使われる、普段使いとして好き勝手に用いて画面を塗りまくることが出来ます。逆にレアリティの高いシェーダは、画面のほんの一部だけ、あるいは一瞬だけ使われて消えるエフェクトのような派手で重たい表現用途でしか使うことを許可しません。といったように、シェーダの負荷/フィル面積/生存時間にて使い道を縛ることで、描画負荷のコントロールを行うのがこのレアリティの目的です。
このような仕組みを用意することで、アーティストさんに描画負荷を意識したシェーダの実装についてある意味ゲーム感覚で体感/学習してもらう良い機会になると思っています。
余談ですが、ShaderGraphを書かれる皆さんは"Simple Noise"ノードが大好きです。確かに使い勝手の良い便利なノードですが、Noise系ノードは非常に大量の命令数を消費します。使い方によりますが、適当なノイズテクスチャ1枚用意すればそれで十分、なケースも多かったりします。ですので、「Noise系、特にSimple Noiseの多用には気をつけてね」とは口酸っぱく言い続けた方が良いでしょう。
Overdraw Checker
シェーダとは直接関係のない話になりますが、カットシーン等のピクセルフィル負荷計測ツールとして、画面のオーバードロー率を計測するチェッカーを作成し導入しています。Unity公式のRendering Debuggerなどにもオーバードローの確認モードが用意されていたりしますが、こちらは画面の何%がフィルされているかを明確に数値化し、x%を超えていたら一発でレギュレーション違反、それよりは軽くともy%以上がzフレーム以上続いたらこれも違反、という雰囲気の実装になっています。
(多少不正確でも良いので、はっきりした「数値」でレギュレーションを示すことが非常に大事だと私は考えています。ぼんやりとした雰囲気の言葉だけのレギュレーションよりも意識しやすいですからね)
で、そしてこれはまだ未実装で妄想の域を出ていないのですが、このオーバードロー計測ツールにてシェーダのレアリティ別にカウントを行い、正しくレアリティの運用がされているか判定する機能を組み込もうかな、と考えています。後述するTarget機能を使ってレンダーパスを自動追加してやれば手間も最小限に抑えられた上で、明確な数値としてバリデーションを用意できるんじゃないかな?と...上手くいくかどうかは実装してみないことには分かりませんけれども。
NaN/Inf Checker
ShaderGraphをレビューを通さずに勢いに任せて作っていると、0除算やpow(a,b)のaにマイナス値を突っ込むなどの破綻した演算によってNaN (Not a Number / 非数)が発生したり、極端に大きな数値を扱ってしまってInf (Infinity / 無限)が発生したりは日常茶飯事です。そこで、アプリのPlay中にNaN/Infが発生していないか毎フレームRenderTexture/フレームバッファの監視を行い、見つかったら警告を出す仕組みを用意します。
最終的にどのシェーダがNaN/Infを出しているかは手動で調査しなければなりませんが、コードレビューで予めNaN/Infが出ないか血眼になって調べるよりは、トータルではこちらの方が安上がりだと考えています。
以上のような、シェーダのレアリティ概念の導入やオーバードロー率計測ツール、NaN/Infチェッカーといったレギュレーション/バリデーションツールによって、アーティストさんが用意したシェーダをエンジニアのレビューなしでそのまま運用できるようにします。
ShderGraphのデメリット2: シンプルに機能が足りていない
ShaderGraphはコードベースのシェーダと比べて、様々な機能が全く足りていません。
ShaderLab各種機能の欠落
例えばステンシル操作。ステンシルに関する一切が、標準のShaderGraphからは触ることができません。
Unity社の言い分によると、ステンシル操作に制限のあるHDRPの仕様が、何故かURPにも同じように縛りとして発生しているそうです。いや、HDRPなんて知らねえよ勘弁して?って話なんですけれども...
(ステンシル操作自体は、RendererFeatureのRenderObjectsにてオーバーライドすれば不可能ではありません。しかし、描画パスとしては特殊扱いになってDepthTextureの生成等に問題が出たり、そもそもパスが新規追加されることでのCPUオーバーヘッドが気になります。普通に、シェーダ単体でステンシル操作が出来るようになっているべきです)
例えば(ROPの)ブレンドモード。理解不能なことに、数パターンのプリセットから選択する方式しか用意されていません。「基本は加算合成だけど、アルファチャンネルには何も書き込みたくない」としても、ShaderGraphのデフォルト状態ではそのようなシェーダは用意できません。
例えばColorMask。存在すら消されています。えっ?
ShaderLabで記述するような各種機能に関して、一事が万事こんな感じで対応不足が目立ちます。ですがこちらはまだ序の口。
シェーダ実体への干渉方法不足
本丸はシェーダの実体部に関してです。
ゲームグラフィックスの世界では、ダメージを受けたオブジェクトを赤く点滅させたい、なんてことがよくあります。このとき、最終的なライティング結果に対してtintColorを指定してこのカラーで乗算する、なんてことをやりますよね。ですが、標準のShaderGraphにて作成するLit-ShaderGraphで、ライティング結果に適当なカラーを乗算するといった実装は不可能です。こんな簡単な処理ですら塞がれています。(Unlit-ShaderGraphを持ち出してライティングを自前で実装してEmissiveにそのライティング結果を流し込む、みたいに、無理をすれば出来なくはないですが、バカバカしくてうんざりしてきますね)
何故か?というのは見当はつきます。URPのLit-ShaderGraphは物理ベースレンダリング(PBR)で作られており、そのライティングにおける反射モデルはエネルギー保存則に満たすことになっています。その光量計算の流れにいきなり正体不明の謎のカラーで乗算、なんてしてしまったら、エネルギーはどこに消えたの?闇?それPBRじゃなくない?ということなのでしょう。
でも私たちゲーム屋はシミュレーション屋さんではありません。ダメージを受けたオブジェクトは赤く点滅させたいんですよ。
これは単純な例ですが、もっと深く、ライティングモデル自体を差し替えたい、シャドウイングを独自の形式にしたい、なんて時にも、コードベースならどうとでもなりますが、ShaderGraphでは融通がなかなか効きません。マスターノードの先に関与することが出来ないんですね。
ShaderGraphを拡張する
以上のように、ShaderGraphはそのままの状態で運用するには機能が圧倒的に足りていません。
ではどうすればよいのでしょうか?簡単ですね。ユーザー側でShaderGraphを拡張をするしかありません。
そして(おそらく)Unity社もそこは認識しているようです。まさにShaderGraphを拡張する手段が実はこっそり用意されています。それが「Target」です。
Targetとは
まず前提知識として、ShaderGraphはUnity内部的に、ユーザーが作成したノードの塊から生成されるシェーダと、予め用意されたライティング用やシャドウイング用等の各種HLSLコード実体を混ぜ合わせ、最終的にコードベースの.shader(HLSL+ShaderLab)を自動生成するツールであるといえます。原理的に「ShaderGraphで出来て、コードベースで出来ないこと」は存在しません。
このとき、.shaderを自動生成する際のルールがTargetという形で一箇所にまとまって記述されています。
フォルダでいえば、Unity2022.1.23f1 / URP13.1.8であれば"com.unity.render-pipelines.universal@13.1.8\Editor\ShaderGraph\Targets"が該当します。
ここには"UniversalTarget"というTargetと、SubTargetとしてLit/Unlit/Decalが含まれています(環境によってこの構成には違いはあるかと思いますが)。
このTargetはバリエーションを用意することが出来て、それぞれのShaderGraph単位でどのTargetを使うかを選択することが出来ます。つまり、独自のTargetを用意してやればShaderGraphの機能拡張が可能になるというわけです。
しかし問題がいくつかあります。
まず上に書かれたフォルダ名から分かると思いますが、これはURPの内部実装に相当します。書き換えるにはURPをCustomPackage化してやる必要があります。
そしてTarget機能は現在internal、つまり隠し機能扱いです。遠い未来に一般公開される予定らしいのですが、遠い未来の話なのでそれまで待ってはいられません。そのため、ドキュメント/リファレンスは何もありませんし、もちろんUnity社のサポートは受けられませんし、ググってもまともに情報は手に入りません。手探りの状態でURPの中身を書き換えるというリスクを犯すことになります。
更に、URPは頻繁にバージョンアップして中身がころころ変わります。このTarget機能を拡張することはShaderGraphの書き換えと同義ですので、URPのバージョンアップ時にはもう一度同じように書き換えを再度やり直す手間が発生する可能性が高いです。(おそらくこのポイントが公開機能としていない理由なのだと思います。URP/ShaderGraphの根幹が安定して、Targetの拡張に対してStableになればユーザーに開放されるようになる、と...おそらくですけど...)
最後に、URP/ShaderGraphの内部実装を書き換えるわけですから、Litシェーダを構成するPBRやシャドウイングなど現代的なレンダリングテクニックであったり、URPのある程度の実装概要であったりといった、幅広いグラフィックスに関する知識が最低限必要となります。下手な弄り方をするとURPが壊れゲームが動かなくなるでしょう。
以上、警告はしましたからね...?
事前知識
以下の話は前述した Unity2022.1.23f1 / URP13.1.8 に限定して話をしています。他のバージョンでは構造や各種名称が変わっていたりしますが、基本的な仕組みは共通となっていますので適宜環境に合わせて読み替えを行ってください。
まず、Targetには親となる"Target"とその子となる"SubTarget"があります。
初期状態ではTargetとしてUniversalTargetクラスがあり、そこにぶら下がるSubTargetとしてUniversalSubTargetクラス、とその派生クラスであるUniversalLitSubTarget / UniversalUnlitSubTarget / UniversalDecalSubTargetクラスが実際に使われるSubTargetとして存在しています。
(2D用のSub-Targetが3つほどフォルダ"com.unity.render-pipelines.universal@13.1.8\Editor\2D\ShaderGraph\Targets"下にも存在しますがとりあえずスルーします)
これらがShaderGraph上では、↓のように
Graph Inspector上にて選択可能となっています。"ActiveTargets"がTarget、"Material"がSubTargetです(SubTargetなのかMaterialなのか、名称統一せえよ!そういうとこだぞ!って思うのですがいかがでしょうか)。
ActiveTargetsには標準で"Universal"か"Built-in"の2つしか選べず、後者はBRP用ShaderGraphなのでここでは無関係です。前者の"Universal"がまさにUniversalTargetクラスで定義されたTargetです。
そしてMaterial = SubTargetですが、こちらはTargetに馴染みのない方々でもShaderGraphを使っていれば触ったことがあるのではないでしょうか?Lit/Unlitなど、ShaderGraphのコア挙動を変更するのがこのSubTargetになります。
Targetの複製
ここで既存のUniversalTargetクラスやUniversalLitSubTargetクラスを直接書き換えてしまうことも出来ますが、これらは標準のShaderGraph全てが参照していますので全てのShaderGraphの挙動が変わってしまいます。ですので、必要なシェーダにのみ影響させるよう、UniversalTarget.csなど一連のソースコードを複製/リネームし、実際の中身も別のクラスであるかのように書き換えます。名前はUniversalTarget_Patchedとか適当になんでもいいです。GUID等もあるのでそちらもmetaが生成されたら書き換えましょう。多重定義なenumは削除し、class類は全てリネームし参照先も差し替えます。泥臭くて辛いですが我慢です。
書き換えが完了すると、↓のように
Active Targetsに新しく追加したTargetが選べるようになり、
SubTargetも複製したものを選択できるようになります。
これでTargetを複製出来ました。ここから先、複製先のTargetのみを書き換えるようにすれば、これを参照しているShaderGraphの挙動のみを調整出来ます。標準のUniversalTargetをActive Targetsとしているその他のシェーダへの副作用はありません。
後はご自由に
後はTarget/SubTargetを必要に応じてゴリゴリ書き換えていくだけです。
例えばLitシェーダのフォワードレンダリングパスは"com.unity.render-pipelines.universal@13.1.8\Editor\ShaderGraph\Includes\PBRForwardPass.hlsl"がエントリポイントになっていますので、このファイルを(また他シェーダの影響がないように)複製/リネームし、書き換えます。さらに参照方法であるincludeがUniversalLitSubTarget.cs(の複製先)の"LitIncludes"クラスにありますので、こちらを書き換えればOKです。
同じように、ライティングのコア処理は"com.unity.render-pipelines.universal@13.1.8\ShaderLibrary\Lighting.hlsl"にあり、参照方法はUniversalTarget.csの"CoreIncludes"クラスにあります。
シェーダに含まれるレンダーパスやShader Keywordsを追加/削除することも難しくはありません。前述したような、Stencilやブレンドモード、ColorMaskといったShaderLab機能の追加/拡張も頑張れば自由自在です。どこまで頑張るかは工数とやる気に相談しましょう。
Targetまとめ
Target機能を使うことで、ShaderGraphに足りていない機能追加やカスタムライティングの実装など、ShaderGraphを自由に拡張することが出来ます。とはいえ抜け道的な実装になり、URP/ShaderGraphのバージョンアップに対して非常に弱くなるという弱点がありますので、どこまで手を入れるかはよく考える必要があります。
ただまあ、これくらいやらないと厳しいんですよねShaderGraph...
ShderGraphのデメリット3: 最適化が足りていない
他のデメリットに比べたら些細な問題ですが...
ShaderGraphで出力されるシェーダには、使われている/いないに関わらずいくつかのインターポレータが必ず配置される作りになっています。致命的と言えるほどの効率悪化ではありませんが間違いなく無駄なので、使われていないインターポレータは削除されるような仕組みが欲しいのですが、現状では用意されていません。
要望はUnity社に投げているので、いずれ改善されることを祈りましょう。
ほかにも「ShaderGraphではGeometryShaderやComputeShaderが書けない」などありますが、まあ別にコードベースで書けばよくない?って話なので、これはどうでもいいかなと思います。
まとめ (ShaderGraph or コードベースの使い分け)
if ( グラフィックスエンジニアが用意する? )
{
if ( 3Dモデルサーフェス用シェーダ? )
{
ShaderGraph ( + HLSLによるCustomFunction )
}
else // UI / VFX / PostProcess用シェーダ?
{
コードベース
(ShaderLabのややこしい管理は不要で、決まったパターンのコピペでだいたい片付くため)
}
}
else // アーティストさんが用意する?
{
ShaderGraph ( + レギュレーションによる縛り )
}
今後私が関わるタイトルでは、このような分類でShaderGraphとコードベースを使い分けしていこうと考えています。
全てのシェーダをShaderGraphで用意する必要はありません。
UIやVFX、ポストプロセスといった、いわゆる3Dメッシュに貼り付けるようなものではないシェーダはURP用にゴチャゴチャしたShaderLabの相手をしなくても、その辺の適当な.shaderをコピペしてちょっと弄るだけで動きます。であれば、わざわざShaderGraphを挟まなくてもコードベースで何の問題もありません。
逆に3Dメッシュに貼り付ける、PBRなどを用いるようなシェーダは、多少無理をしてでもShaderGraphで統一した方がトータルでのメリットは大きいと感じています。完全にどちらか、ではなく、使い分けした方が良いということですね。
さいごに
ShaderGraphは便利ですが、残念ながら実用とするには様々な工夫(K.U.F.U.)がどうしても必要となります。
いつか遠い未来、こんな手間を掛けず何も考えなくてもイメージ通りのシェーダがShaderGraphにて作成できる、ような時代が来ることを期待しましょう。まあUnityですし、おそらく10年後でも無理でしょうけど...
それではまた来年の12/24にお会いできたらいいですね!
(しかし、ざっくりシェーダ周りのレギュレーションについて語ろうと思っただけなのに、なんでこんなに長くなってしまうんだろう...)