LessonEX シェーダーグラフ
EX-1 シェーダーグラフとは
シェーダーグラフとはノードを繋げることで視覚的にシェーダーを作ることができる機能です。
3D脱出ゲーム編3-8や5-2でも使用していますが、ここではシェーダーグラフについてサンプルを用いながらもう少し詳しく紹介していきます。シェーダーグラフは書籍が丸々1冊出るほど奥が深い世界なので全ては解説できませんが、この教材を足掛かりにして理解を深めていってください。
シェーダーグラフはURPの機能なので、2Dコアや3Dコアでは使用できません。実際に使用する際はURPのプロジェクトを使うようにしてください。
EX-2 画像をフェードしてみる
まずは2Dの画像を使ってシェーダーグラフを書いてみましょう。2DはZ軸がないため計算がシンプルになり、3Dにも応用が利くので最初の練習に最適です。
まずは画像を下からフェードするシェーダーを書いてみましょう。イラスト作成ソフトで作ることもできますが、シェーダーを書くと調整がスムーズで画像の変更にも強くなります。

適当なスプライトを追加して、好きな画像を貼り付けてみてください。使用する画像は一部が透過されているものにしてください。

「Create」→「Shader Graph」→「URP」→「Sprite Unlit Shader Graph」を選択してください。
シェーダーグラフの名前はわかりやすいものでOKです。

シェーダーグラフを編集する前に、シェーダーグラフを先ほど作成したスプライトに適用してみましょう。
新しいマテリアルを作成して、シェーダーグラフをドラッグ&ドロップしてみてください。

マテリアルを選択して上部のShaderからプルダウンメニューを開くことで、シェーダーを適用することもできます。お好みの方法でOKです。
混同されがちですが、シェーダーとマテリアルは別物です。
シェーダー:描画の計算をするためのプログラム
マテリアル:シェーダーを使ってオブジェクトの見え方を制御するための設定

Unity Tips!

先ほど作成したスプライトのSprite Render内にあるMaterialに、作成したマテリアルをドラッグ&ドロップしてください。

マテリアルを適用すると表示が灰色になり、不透明度もおかしくなってしまいます。
まずは表示が正常になるようにシェーダーグラフを改造しましょう。

シェーダーグラフをダブルクリックしてください。シェーダーグラフ編集ウィンドウが開きます。

左側にある+ボタンをクリックして「Texture 2D」を選択してください。
シェーダーグラフではパラメータ(変数)を追加することで、インスペクターから操作できる値を実装することができます。

作成したパラメータの名前を「MainTex」にしてください。
この名前はUnity側で指定されている特殊な名前で、メインのテクスチャを指定することを想定しています(詳しくは後述)

作成したMainTexをドラッグ&ドロップして、ノードを追加してください。

何もない場所を右クリックして「Create Node」を選択してください。

検索欄から「Sample Texture 2D」ノードを追加してください。
これはUV座標を使って、テクスチャの任意の場所の色を抽出することができるノードです。

次にノードを繋げます。
MainTexをSample Texture 2DノードのTextureへ繋いでください。これで抽出に使用するテクスチャを指定しています。
抽出した色をFragmentのBase Colorに出力して、AをAlpha(不透明度)へ出力してください。

シェーダーグラフができたら保存してください。
シーンビューを確認すると、灰色になっていた画像が正常に表示されるようになっているはずです。
インスペクター下部にMainTexを指定するためのパラメータがありますが、この中身はSprite RendererのSpriteに対応しています。Spriteを変更するとMainTexも変化します(MainTexがSpriteの中身を参照しているイメージ)


それではこのシェーダーグラフを改造して、下からフェードするようにしてみましょう。
画像のようにAlphaの計算を改造してみてください。
【使用しているノード】
・UVノード…UV座標を返す
・Splitノード…Inへ入れた値から特定の値を抽出する
(今回はUV座標のYだけを抽出している)
・Multiplyノード…乗算を行うノード

クリックして
拡大表示
UV座標は画像の左下を(0,0)、右上を(1,1)として、画像のどの地点を描画しようとしているか定義するための座標です。
今回はそのUV座標のYの要素をそのまま不透明度に使うことで、下に行くほど透明になる表現を行っています。

シェーダーグラフを改造できたら保存して、シーンを確認してみてください。
画像が下に行くほど透明になっているでしょうか。

このままでも使えますが、さらに改造してゲームの演出として使用できるようにしてみましょう。
シェーダーグラフを開いて、画像のようにノードを接続してください。
【使用しているノード】
・Addノード…加算を行うノード
・Clampノード…MinからMaxの範囲に値を収めるノード(スクリプトのClamp関数と同じ)

クリックして
拡大表示
パラメータを調整しやすいように、スライダーで表示してみましょう。
Fadeパラメータを選択した状態でNode SettingsからModeを「Slider」に変更してください。
Default(初期値)を1、Min(下限)を-1、Max(上限)を1に設定してみてください。

これでAlpha(不透明度)の値を動的に調整することで、下からフェードして消えていくアニメーションを実装できました。Fadeスライダーを操作して、動作を確認してみてください。

スクリプトからこのパラメータを動的に操作してみましょう。
新しいスクリプトを作成して、以下のように入力してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Sample1 : MonoBehaviour
{
// 使用しているマテリアル
Material m_spriteMaterial;
bool m_isFade = false;
float m_fade = 1.0f;
void Awake()
{
m_spriteMaterial = GetComponent<SpriteRenderer>().material;
}
void Update()
{
// Enterキーが押されたらフェード開始
if (Input.GetKeyDown(KeyCode.Return) && m_isFade == false)
{
m_isFade = true;
}
// マテリアルを更新
if (m_isFade)
{
m_fade -= Time.deltaTime;
m_fade = Mathf.Clamp(m_fade, -1.0f, 1.0f);
// パラメータを設定
m_spriteMaterial.SetFloat("_Fade", m_fade);
}
}
}
Awake関数内でSpriteRenderに設定されているマテリアルを取得しています。
そしてEnterキーが押されたら、取得したマテリアルに対してパラメータ更新の処理を行っています。
パラメータの更新はマテリアルのSet〇〇関数を使用します(今回はfloat型なのでSetFloat関数、他にもSetColor関数やSetVector関数などがあります)
Set関数の第一引数に更新対象のパラメータの名前を指定します。名前はシェーダーグラフのNode Settings内のReferenceに対応しています。

対象のスプライトにコンポーネントをアタッチしてください。
ゲームを実行してEnterキーを押すと、画像が下からフェードしていきます。敵が消滅する時など、演出に一工夫加える時に使ってみてください。

EX-3 キラカードを作ってみる
少しステップアップして、次はキラカードの演出を作ってみましょう。時間経過に応じて光り方が変化します。

まずはEX2と同じように、画像を表示するだけのシェーダーグラフを作ってください。適当なカードの画像を用意して、表示させます。


Node Settingsではプレビュー用の画像を設定できます。シェーダーの変化がわかりやすいように、MainTexのDefaultにカードの画像を設定しておいてください。

まずはきらめき用のテクスチャを用意しましょう。
今回は3D脱出ゲーム編5-3でも紹介しているルール画像配布サイトから「161.png」をプロジェクトへ追加してください(他の画像を使うと光り方も変わります)
【配布サイト】https://4you.bz/rule


きらめき用の新しいテクスチャを指定するためのパラメータを追加してください。
テクスチャはメイン用の1枚だけでなく、複数枚組み合わせることで様々な表現ができます。テクスチャを複数枚使うという発想を持っておくと、独創的な演出を作ることもできるでしょう。
きらめき用のテクスチャをプレビュー用に設定して、Sample Texture2Dノードで抽出してください。

きらめき用のテクスチャ抽出処理とは別に、UVスクロールの処理を作っていきます。以下の画像のようにノードを接続してください。
【使用しているノード】
・Timeノード:時間を返すノード
※ 特にTimeパラメータは経過した時間を返すパラメータで、どんどん増加していく値になります。時間経過で変化する演出を作る際にはほぼ必須といえる重要ノードなので、しっかり覚えておいてください。
・Vector2ノード…Vector2型のパラメータを定義するノード
※ 今回はスクロール速度として設定しています。お好みで速度を変更してください。
また、Vector2型のパラメータを定義すると使いやすくなります。
・Tiling And Offsetノード…UV座標をTiling分敷き詰めて、Offset分ずらすノード

クリックして
拡大表示
UVスクロールの処理がひとまとめにわかりやすいように、ノードをグループ化しましょう。
作成したノードを囲んで右クリックし「Group Selection」を選択してください。

グループに「UVスクロール」と名前をつけてください。これで処理をひとまとめにして、まとめて移動させたりすることができます。
シェーダーグラフが複雑化すると管理しづらくなるため、こまめにグループ化して処理のまとまりをわかりやすくしておきましょう。

先ほど作成したきらめきテクスチャ抽出の処理とは別に、もう一つきらめきテクスチャの抽出処理を作ってください。
そして先ほど作成したUVスクロールの結果を、Sample Texture2DのUVに接続してください。

クリックして
拡大表示
しかし、Sample Texture2Dノードに画像が表示されていません。これは範囲外のUVが指定された場合に引き伸ばす設定になっているからです。
Sampler Stateノードを追加してWarpを「Mirror」に設定してください。そしてSampler StateノードをSample Texture 2DノードのSamplerに接続しましょう。UV座標の変化で抽出されるテクスチャが変化する様子が確認できます。

クリックして
拡大表示
これでUVスクロールできらめきの範囲を作成する処理は終了です。
次にグラデーションを作成しましょう。
最初に作成した方のきらめきテクスチャ抽出処理のRGBAからSample Gradientノードへ繋げてください。これは0~1の値を使ってグラデーションから色を抽出するノードです。
お好みのグラデーションを作成してください。サンプルでは薄めの色を使って虹色のグラデーションを作成、設定しています。

クリックして
拡大表示
これでグラデーションの作成処理は完成です。
グラデーション処理をグループ化してわかりやすいようにしておきましょう。

最後の仕上げです。
UVスクロールを使って作成した影響範囲とグラデーションをMultiplyノードで乗算してください。これが最終的なきらめきのテクスチャになります。

クリックして
拡大表示
最後にきらめきテクスチャとMainTexをAddノードで加算合成しましょう。
テクスチャを加算合成するということはColorが増加し、より1に近くなります。つまり白に近くなる(明るくなる)ということです。
加算合成の結果をBase Colorに出力して、最終結果にしましょう。

クリックして
拡大表示
ここまでできたらゲームを実行して確認してみてください。
(マテリアルへのきらめきテクスチャの設定を忘れずに!)

きらめきにメリハリをつけたい場合は、UVスクロールの後にSmoothstepノードを追加してください。Smoothstepノードは一定範囲外の値を緩やかに切り捨てるノードです。
これできらめきのエフェクトがかかる範囲を狭めることができます。

他にもテクスチャを使うことでできる表現の一例を紹介します。
まずはモンスター部分だけを黒く塗りつぶした画像を用意してください。

影響量を指定するためのテクスチャ用のパラメータを追加してください。
最後に影響量を乗算することで白い部分は×1(そのまま)黒い部分は×0(真っ黒になる)という変化が起きます。

クリックして
拡大表示
これでモンスターの部分だけきらめきの影響を受けないようになります。
複数のテクスチャを組み合わせることでこのように多彩な表現ができるので、ぜひ色々な演出を試してみてください。既存のゲームを見て、模倣するのが理解の近道です!

EX-4 ポストエフェクトを自作する
3Dアクションゲーム編6-4でUnity側に標準で実装されているポストエフェクトの使い方を解説しました。しかしオリジナリティのある表現をするためには、Unity標準のポストエフェクトでは足りない場合があります。
ここでは画面にノイズをかけるポストエフェクトをシェーダーグラフで実装する方法を解説します。


適当なシーンを作成したら、新しいシェーダーを作成してください。
今回はポストエフェクト(全画面)用のシェーダーグラフなので「Fullscreen Shader Graph」を選択しましょう。
※ 全画面シェーダーグラフはUnity2022.2以降の機能なので、表示されない場合はUnityのバージョンを変更してください

まずはシェーダーグラフをゲーム画面に反映させます。
マテリアルを作成して、シェーダーグラフをドラッグ&ドロップしてください。

Settingsフォルダ内のURP-HighFidelity-Rendererを選択してください。

「Add Renderer Feature」をクリックして、開いたメニューから「Full Screen Pass Renderer Feature」を選択してください。


PassMaterialに先ほど作成したマテリアルを設定してください。
シェーダーグラフに何も書いていないため灰色一色の画面が表示されます。


今回「URP-HighFidelity-Renderer」を選択しましたが、他の「URP-Balanced-Renderer」と「URP-Performant-Renderer」はどのように使用されるのでしょうか。
Unityにはプラットフォームによってゲームのクオリティを切り替える機能があります。デフォルトでは「PCにおいてはHighFidelityを使用する」という設定になっています。
他のプラットフォームではそれぞれ対応した設定を使用するので、例えばモバイルゲームで今回のポストエフェクトを使用したい場合は「URP-Balanced-Renderer」にも同じ設定を行ってください。

Unity Tips!

まずは現在の画面を表示できるようにしましょう。
シェーダーグラフを開いたら、URP Sample Bufferノードを追加して、FragmentのBase Colorへ出力してください。Source Bufferは「Blit Source」に設定してください。

これで現在の画面が正常に表示されるようになります。

URP Sample BufferノードのSource Bufferを変更すると、他の画面情報を取得することができます。
NormalWorldSpaceに設定することで法線を、MotionVectorsに設定することで画面の移動量を取得することができます。
MotionVectorsは、以下のサンプルではカメラを左右に動かしながら撮影しているためX座標の移動→R値に置き換えられ、移動している個所が赤く表示されています。MotionVectorsを使う際はFull Screen Pass Renderer FeatureのRequirementsのMotionにチェックを入れてください。



Unity Tips!

まずは画面をモノクロ化してみましょう。
画面をモノクロ化(グレースケール化)するためには各ピクセルのRGBの値を全て同じにする必要があります。その値を計算するために今回は内積を使用しましょう。
ざっくり言うと内積を使うことで2本のベクトルがどれくらい似ているかを計算することができます。
(内積についての数学的な説明はここでは省略します)
今回の場合は現在の画面の色と適当な色を比較して、2つの色がどれだけ似ているかを計算し、その「類似度」をそのまま画面の色とすることでモノクロ化を実現しています。
シェーダーグラフで内積を計算するノードはDot Productノードになります。先ほどのURP Sample BufferノードとBase Colorの間に追加してください。
Dot Productノードの片方の入力値にはX=0.3 Y=0.6 Z=0.1 W=0 を指定してください。

クリックして
拡大表示
ゲーム画面を確認すると、画面がモノクロになっていることを確認できます。
Dot Productノードの入力値を調整することで画面の明るさを調整できます。お好みの表示にしてみてください。

この時点で他にも様々な表現を行うことができます。
例えばDot Productノードの後に色を乗算することで画面の色味を変更できます。
以下のサンプルでは(R=1,G=0.6,B=0.4)を乗算することで画面をセピア色にしています。普通に色を乗算するよりも、一度モノクロ化してから色を乗算する方が綺麗に色が変わります。


URP Sample Bufferノードの後でSubtractノード(減算処理を行うノード)を使って 1-画面の色 の計算を行うと、画面の色が反転します。
0~1の範囲の値は、1に対して減算することで値が反転します。何かと便利なので覚えておいてください。
【例】
・元の値が0.1 → 1 - 0.1 = 0.9
・元の値が0.6 → 1 - 0.6 = 0.4


他にも様々な加工を行うことができます。
画面の印象を簡単にガラッと変えられるため、ぜひ色々な表現を試してみてください。
Unity Tips!

次は画面の階調を落としてみましょう。
階調とは、画面に表示されている色の段階のことを指します。階調を減らすことで画面の色の数を減らすことができます。階調を減らして、よりレトロな雰囲気を演出してみましょう。

階調を落とすときはPosterizeノードを使用します。
URP Sample BufferノードとDot Productノードの間にPosterizeノードを追加してください。
Steps(段階)は8に設定してください。
Posterizeノードは他にも様々な使い道があるので参考にしてみてください。
【参考】Posterizeノード

クリックして
拡大表示
階調を落とすことで画面の情報量が減り、色にメリハリが生まれました。
階調の段階はお好みで調整してください。

次は画面をブラウン管のように歪ませてみましょう。
SpherizeノードはUVを魚眼レンズのように歪ませるノードです。Strength(強さ)に2を入力して、歪ませたUVをURP Sample BufferノードのUVとして使用してください。

ゲーム画面を確認すると、画面が歪んでいることが確認できます。
しかし、歪ませたUVの範囲外は表示がおかしくなっています。範囲外は黒く塗りつぶすようにしてみましょう。

シェーダーグラフで値の比較をする際はComparisonノードとBranchノードを使用します。
Comparisonノードは比較条件を設定することができるノードです。
そしてBranchノードはComparisonノードの条件を満たした場合と満たさなかった場合で任意の値を出力することができるノードです。

以下のようにSpherizeノードからSplitノードへ繋ぎ、UVのX座標を抽出してください。
そしてComparisonノードとBranchノードを使って「UVが0未満の時は1、それ以外の時は0を返す」という処理を行ってください。

クリックして
拡大表示
下に同じように「1より大きいときに1を出力、それ以外の時は0を出力する」ノードを作成してください。これで左右のUVの範囲外を判定できました。

クリックして
拡大表示
UVのY座標でも同じように処理を行ってください。
上下左右全ての範囲を抽出できたらOKです。

クリックして
拡大表示
Addノードを使って、結果を全て加算合成してください。
最終結果が画面を塗りつぶす範囲になります。

クリックして
拡大表示
Dot Productノードの後にSubtractノードを追加して、Dot ProductノードをAに、先ほど合成したAddノードをBに繋いでください。
Subtractノードは A-B を行うノードのため、順番が入れ替わると結果が変わってしまいます
(A-B と B-A で結果が違うのと同じ)

クリックして
拡大表示
これで範囲外が塗りつぶされた状態で画面を歪ませることができます。
シェーダー内で分岐を行うComparisonノードとBranchノードはやや負荷がかかるため、多用することは避けるようにしましょう。

次に画面にノイズ(走査線)を表示してみましょう。
仕組みとしては3D脱出ゲーム編3-8とほぼ同じ内容になります。速度、線の数、色はお好みで調整してください。
【使用しているノード】
・Fractionノード…整数部分を切り捨てて小数部分だけにするノード

クリックして
拡大表示
作ったノイズをAddノードで加算合成してください。

クリックして
拡大表示
これで画面にノイズが表示されるようになります。

ノイズ生成のUV座標にSpherizeノードで歪ませたUVを使用することで、ノイズを歪ませることができます。お好みで切り替えてください。


Unity Tips!

ノイズ(走査線)生成の計算は他にも色々な方法があります。
ここでは別解としてSineノードを使った方法を紹介します。
Sineノードは名前の通りサイン波を返すノードです。3D脱出ゲーム編5-4で紹介したsin関数と同じように使います。

Sineノードそのままでは線が強すぎるため、Remapノードで調整を行います。
Remapノードはしっかり説明すると少しややこしいのですが、簡単に言うと「A~Bの範囲の値をC~Dの場合に置き換えた時どんな値になるか」といった計算をしてくれるノードです。
今回の場合Sineノードで返ってくる値は-1~1の範囲なのですが、これを0.8~1の範囲に調整するためにRemapノードを使用しています。
【参考】Remapノード

クリックして
拡大表示
これでノイズ(走査線)の見た目が変化します。
このように計算式を変えることでシェーダーの見た目も細かく変化するため、色々なノードを試してみてください。「色々試していたらなんかいい感じの見た目になった!」ということもよくあります。

Unity Tips!

仕上げに画面の縁を暗くしてみましょう。
シンプルに中央からの距離を使ってテクスチャを作成します。そのままでは少し弱いので、累乗して値を大きくしてから使用しましょう。
【使用しているノード】
・Distanceノード…AとBの距離を返すノード
・Powerノード…AをB乗した結果を返すノード

クリックして
拡大表示
作成したテクスチャを最後に乗算して合成しましょう。

これで古いカメラのような演出をかけるポストエフェクトの完成です。
このようにシェーダーグラフを用いることで自作のポストエフェクトも簡単に実装できます。ぜひ色々な演出を作ってみてください。

ここまでのシェーダーグラフの全体像です。
うまく表示されないときはノードの繋ぎ方が間違っていないか確認してください。

クリックして
拡大表示
今回はパラメータを使わず固定の値でシェーダーグラフを作成しましたが、スクリプトから作成したポストエフェクトを操作する方法を解説します。
まずはシンプルにマテリアルを操作する方法です。シェーダーグラフにSpeedのパラメータを追加しましょう。これはノイズの速度を管理するためのパラメータです。

後はEX2と同じようにスクリプトからマテリアルを操作するだけです。
新しいスクリプトを作成して、以下のように入力してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Test : MonoBehaviour
{
// 使用しているマテリアル
[SerializeField] Material PostMaterial;
void Update()
{
// Enterキーが押される度にノイズの速度を上げる
if (Input.GetKeyDown(KeyCode.Return))
{
PostMaterial.SetFloat("_Speed", PostMaterial.GetFloat("_Speed") + 1.0f);
}
}
}
後は適当なオブジェクトにスクリプトをアタッチして、ポストエフェクトに使用しているマテリアルを指定するだけです。
次にFull Screen Pass Renderer Featureのアクティブをスクリプトから切り替える方法です。

スクリプトからアクティブ状態を操作します。以下のようなスクリプトを作成してください。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Rendering.Universal; // 追加する
public class Test : MonoBehaviour
{
// 有効/無効を切り替える
[SerializeField] ScriptableRendererFeature Post;
void Update()
{
// Enterキーが押される度にポストエフェクトのアクティブを切り替える
if (Input.GetKeyDown(KeyCode.Return))
{
Post.SetActive(!Post.isActive);
}
}
}
インスペクターから対象のFullScreenPassRendererFeatureを選択してください。

これでゲームを実行し、Enterキーを押すとポストエフェクトの有効状態を切り替えることができます。
ちなみにMaterialやScriptableRendererFeatureの変更はゲームを終了しても自動で元に戻りません。自動で元に戻す場合は、初期状態を記憶しておいてゲーム終了時に復元させる処理を実装する必要があります。
Unity Tips!

(執筆中)