Shader Graph

プロジェクト設定

ShaderLabの自由度が高く、とてもパワフルなシェーダの作り方であるが、やや難しいところもある。複雑で高速なシェーダが必要なら、ShaderLabの方が良いと思われるが、単純なシェーダなら、Shader Graphの方が楽で、作るのも速くなる。

 Shader Graphは言語ではなく、「ノード」というブロックを紐でつなげることにより、視覚的にシェーダを作成ができる。

 まず、Shader Graphをインストールしましょう。Unityの Package Manger から Shader Graph をインストールしてください。

image.png

初めてのシェーダーグラフ

まず、空のシェーダを作成しましょう。「Project」の中に、専用の「Shader」フォルダーを作成し、その中に「Lit Shader Graph」を作成してください。このシェーダーは後ほど使うので名前を「Chessboard」にしてください。

image.png

新しく作ったシェーダーグラフをダブルクリックすると、シェーダーエディタが開く:

image.png

各部品を理解しましょう:

頂点シェーダー(Vertex)

頂点シェーダーは、メッシュの各頂点の位置、または法線ベクトルを変えることができるシェーダーである。マテリアルを設定するだけで、3Dモデルの変形ができるシェーダー:

image.png

フラグメントシェーダー(Fragment)

フラグメントシェーダーは、メッシュの各ピクセルの色、法線ベクトル、金属性などを変えることができるシェーダーである。マテリアルを設定するだけで、3Dモデルの見え方を変えれるシェーダー:

image.png

ブラックボード(Blackboard)

パラメータを作成ができるところ。Unityのインスペクターで変えることができる数値をここに追加する。

image.png

グラフ・インスペクター(Graph Inspector)

普通のインスペクターと同様に、現在選択されているものに関する情報や数値を確認し、変えることができるパネル:

ノード設定(ノードによって変わる) グラフ設定(全般設定)

image.png

image.png

プレビュー(Main Preview)

シェーダの結果のプレビュー

image.png

様々なシェーダを作ろう!

Shader Graph で使える「ノード」を紹介しながら、いろんな効果を再現し、3Dモデルの見せ方を変えてみましょう。

無限チェスボード

image.png

考え方:「XZ平面の位置により、白いコマ、または黒いコマを表示したい」

ならば、3D平面の各ピクセルは3D空間の中にどこにあるのかを求める必要がある。その場合は、以下のノードを使える:

Positionノード

image.png

Positionノード

  • Vertexと繋ぐと:各頂点の位置
  • Fragmentと繋ぐと:各ピクセルの位置

入力

  • なし

設定

  • Space:どの位置を使う?
    • World:3Dシーンの絶対位置
    • Object:GameObject内の相対位置
    • View:カメラから見た位置

出力

  • Out(3):XYZの位置

Positionノードを Base Color(Fragment)と繋ぐと:

image.png

image.png

ってどういうこと?実は、「位置」を「色」に書き換えた。元々の位置はXYZとして表現していたものを、そのままRGBに書き換えて、表示しているだけ。つまり:

確認:平面の位置を変えて、色がどう変わるのか、確認してみてください。
※ポイント:色はいつも0~1の間で表現するので、位置は「1」を超えると最大値になり、それ以上色が変わらない

 Positionノードで位置を取れることが分かったが、なかなかチェスボードにならない。問題を分けて、少しずつ解決してみましょう。まず、X軸だけで白黒の「しましま」のパターンを作成しましょう。YとZは要らないので、新しいノードが必要:

Split ノード

image.png

Splitノード

  • ベクトルを成分に分割する

入力

  • In(1):2D, 3Dまたは4Dのベクトル

設定

  • なし

出力

  • R(1):赤色、またはX軸
  • G(1):緑色、またはY軸
  • B(1):青色、またはZ軸
  • A(1):アルファ値、または4Dの4つ目の成分(W)

グラフを直して、Position→Split(R)→Base Colorをつなげてみましょう:

image.png

image.png

X軸の数値だけしか残らないので、Xの位置が大きいほど、白くなる!(RGBはすべてXの数値になる)。ちょっと近づいた気がしますが、しましまのパターンになっていない。何か周期的に繰り返すものがないか?

image.png

この図は「sin(θ)」のグラフであり、2πごとになみのように繰り返す関数である!x軸はどれだけ大きくなっても、出力は必ず-1~1の間になるので、次のノードを追加しましょう:

Sine ノード

image.png

Sineノード

  • ベクトルを成分に分割する

入力

  • In(1):角度(ラジアン)

設定

  • なし

出力

  • Out(1):サイン関数の結果

Position→Split(R)→Sine→Base Colorにすると:

image.png

image.png

しましまのパターンが現れた!…が、色がなめらかに変わる。確かに、Sinのグラフを見ると、-1〜1の間にゆっくり変わる。「白→黒→白→黒」の2色だけがほしい、以下のように:

image.png

つまり、「sin」が「0以上」だったら、「1」にし、「0未満」の場合は、「0」とする(青い線)。これは、「Step」ノードで実現できる!

Step ノード

image.png

Stepノード

  • 入力した値を「しきい値」以上だと「1」を返す、以下だと「0」を返す

入力

  • Edge(1):しきい値
  • In(1):入力値

設定

  • なし

出力

  • Out(1):「0」か「1」

Sineの後にStepノードを挟んで、確認しましょう。Edge は「0」にしてください。

image.png

image.png

かなりよくなった!ただし、1つの「ストライプ」はUnityのグリッドと比べると、サイズが合わない。なぜなら、Unityのグリッドの間隔は「1単位」なのだが、sin の周期は π(約3.1416)である。これは簡単に解決できる。sin の周期は Unity の1単位にすれば良いでしょう。つまり、1つの単位をπ倍数にすれば良いということです。掛け算のノードは:

Multiply ノード

image.png

Multiplyノード

  • 2つの数値の掛け算を返す

入力

  • A(1):1つ目の数値、またはベクトル
  • B(1):2つ目の数値、またはベクトル

設定

  • なし

出力

  • Out(1)
    • 数値と数値:A × B 
      • 10 x 4 = 40
    • 数値とベクトル:数値と各成分でできる新しいベクトル
      • 3 x [1, 2, 3] = [3, 6, 9]
    • ベクトルの場合は、各成分の掛け算を返す
      • 例:[1, 2, 3] × [4, 5, 6] = [4, 10, 18]

Split → Multiply → Sine を繋ぎ、「B」は「3.1416」にしておいてください:

image.png

image.png

Sineの周期をUnityの単位に合わせたので、1つのストライプはグリッドにぴったり!

これで、チェスボードの半分ができた!Z方向の処理は同じになるので、ノードを重複してもOK!(Splitノードの(R)の代わりに(B)を使ってください)

image.png

では、ZとXどう合成すれば良いでしょう?Zは「黒」の場合は、Xのストライプはそのままにして、Zは「白」になったら、Xの色を反転してほしい(白↔黒)

image.png

色は「白:1」「黒:0」というのを分かっているので、XとZの差分(X-Z)を計算してみましょう:

X方向の値(色) Z方向の値(色) X-Z 期待
1(白) 0(黒) 1(白) 1:✓
0(黒) 0(黒) 0(黒) 0:✓
1(白) 1(白) 0(黒) 0:✓
0(黒) 1(白) -1(?) 1:×

最後だけは「1」ではなく、「-1」になってしまうので、絶対値を取れば直せる。以下の2つのノードで「X」と「Z」を合成しましょう: 

Subtract ノード

image.png

Subtract ノード

  • 2つの数値の引き算を返す

入力

  • A(1):1つ目の数値、またはベクトル
  • B(1):2つ目の数値、またはベクトル

設定

  • なし

出力

  • Out(1)
    • 数値と数値:A - B 
      • 10 - 4 = 6
    • 数値とベクトル:数値と各成分でできる新しいベクトル
      • 3 - [1, 2, 3] = [2, 1, 0]
    • ベクトルの場合は、各成分の引き算を返す
      • 例:[1, 2, 3] - [4, 5, 6] = [-3, -3, -3]
Absolute ノード

image.png

Absolute ノード

  • 絶対値を返す

入力

  • In(1):数値、またはベクトル

設定

  • なし

出力

  • Out(1):数値、また各成分の絶対値

X方向とZ方向の差分+絶対値を計算し、Base Colorと繋ぐと:

image.png

image.png

レインボーキューブ

image143.gif

このシェーダは、オブジェクトのUVを変えて、テクスチャをアニメーションするシェーダである。まず、「rainbow」のテクスチャをドラッグドロップでシェーダに追加する。自動的に、Sample Texture 2Dのノードを作成される:

Sample Texture ノード

image.png

Sample Texture ノード

  • テクスチャの色データを読み込む

入力

  • Texture(T2):テクスチャ画像
  • UV(2):読み込むべきのUV座標。
    • 初期値としてメッシュのUV座標が使われるが、上書きができる。
  • Sampler(SS):(高級)色の補完方法

設定

  • Type:画像の種類
    • Default:色テクスチャ(模様)
    • Normal:法線マップ
  • Space:(高級)ノーマルマップの空間(TangentでOK)

出力

  • RGBA(4):画像から取得したRGB色とアルファ値(透明度)
  • R(1):赤の成分のみ
  • G(1):緑の成分のみ
  • B(1):青の成分のみ
  • A(1):アルファ値の成分のみ

このままで「RBGA」を「Base Color」と繋いでみましょう:

image.png

UnityのCubeの各面のUVは(0, 0)~(1, 1)になっているため、各面が同じテクスチャをそのまま貼っている!

image.png

このままだと、エフェクトを作れないので、MeshのUVを上書きしましょう。Meshデータの代わり、オブジェクトの相対位置を使って、UをXに一致し、VをYに一致しましょう、つまり:

image.png

「Position」から位置を取得し「Space」は「Object」(相対位置)にする。それをUVとして使えば

image.png

テクスチャの縦(V座標)はY座標と一致しているので、上から下の方向でテクスチャを読み込んで貼り付ける。テクスチャ横(U座標)はX軸に従うので、思い通り貼り付けることができた。

 ただ、これが動いていないので、時間に従って、縦方向の「V座標」だけ動かしましょう。このため、この3つのノードが必要

Time ノード

image.png

Time ノード

  • 時間を返す

入力

  • なし

設定

  • なし

出力

  • Time(1):シェーダ開始してからの時間
  • Sine Time(1):Timeのサイン
  • Cosine Time(1):Timeのコサイン
  • Delta Time(1):前のフレームから過ごした時間
Vector2 ノード

image.png

Vector2 ノード

  • 2Dベクトルを作成

入力

  • X(1):X成分
  • Y(1):Y成分

設定

  • なし

出力

  • Out(1):出力のベクトル
Add ノード

image.png

Add ノード

  • 2つの数値の足し算を返す

入力

  • A(1):1つ目の数値、またはベクトル
  • B(1):2つ目の数値、またはベクトル

設定

  • なし

出力

  • Out(1)
    • 数値と数値:A + B 
      • 10 + 4 = 14
    • 数値とベクトル:数値と各成分でできる新しいベクトル
      • 3 + [1, 2, 3] = [4, 5, 6]
    • ベクトルの場合は、各成分の足し算を返す
      • 例:[1, 2, 3] + [4, 5, 6] = [5, 7, 9]

完成シェーダグラフ

image.png

魔法巻物を燃やす

image142.gif

まず、シーンを設定してください(scroll.fbx を追加し、空のシェーダを作成、Materialも準備しましょう)そして、「scroll」のテクスチャをシェーダに追加し、そのまま「Base Color」と繋げてみる:

image.png

image.png

テクスチャがちゃんと貼り付けられたことが分かる。もう一つの「progress」追加し、「Base Color」とつなげてください

image.png

image.png

「progress」は色テクスチャではなく、「データテクスチャ」という。このテクスチャは表示するべきではなく、シェーダの計算のために使うテクスチャである。今回は穴が開くようなエフェクトを作るので、「progress」はアニメーションの進歩のために使い、0(黒)から1(白)まで徐々に開けていく。

 色テクスチャではないため、Unityにその旨を伝えないといけない。「progress」を選択し、Inspectorで「sRGB (Color Texture)」のチェックを外してください

image.png

また、今回のエフェクトでは、巻物が消えていくので、シェーダが透明度に対応するようにしなければならない。透明度の処理は主に2つある

image.png

今回のエフェクトでは「Alpha Clipping」は適切であり、シェーダの設定でAlpha Clippingを有効化しよう。「Graph Inspector」で「Alpha Clipping」のチェックを付けてください。これでシェーダが2つの新しい出力ができる:

image.png

image.png

 「Alpha」はアルファ値(透明度)であり、0〜1の間に設定できる。「Alpha Clip Threshold」は「見える・見えない」のしきい値である。これで、「progress」の(R)を「Alpha」と繋ぎ、いくつかのしきい値を試してみましょう:

image.png

しきい値:0.25

image.png

しきい値:0.5

image.png

しきい値:0.7

image.png

しきい値:0.9

何度もしきい値を変えて確認するのは大変なので、Materialのプロパティとして自由に変えられるようにしましょう。「Blackboard」で新しいパラメータ「Progress」の「Float」を作成し、「Alpha Clip Threshold」とつなげてみましょう:

image.png

image.png

そして、Materialを選択すると、編集できるパラメータが現れる。この数値を変えれば、すぐその効果を確認できる。

image.png

 ただし、Alpha Clippingは0〜1の間だけしか効かないので、-100や100にしても意味がない。Progressに制限を付けましょう。「Blackboard」でパラメータを選択し、「Node Settings」で範囲を設定しましょう。「Mode」を「Slider」にし、少し余裕があるため -0.2~1.2の間の制限を設定:

image.png

➡ をすると、Materialの見え方が変わる:

 

image.png

つぎに、巻物が消える領域の周りに光るエフェクトを実現しましょう。つまり、「しきい値のちょっと手前」の領域を求めないといけない。

 一旦、すべてのノードを外して、各領域を可視化しましょう。「progress」テクスチャの(R)をStepノードを通して、「Progress」しきい値で白黒の範囲を「Base Color」で確認しましょう

image.png

image.png

きい値未満の値は「黒」になり、しきい値以上の値は白になる。「少し手前」を求めたいなら、基本的には、「Progress値 + 少し」で新しいしきい値を作れば良いでしょう。

image.png

そして、この2つの領域の差分だけ残せば、リングを作成することができる:

image.png

image.png

←「Progress」と「Progress+少し」の差分で残った領域
このリングの厚さをパラメータとしてBlackBoardで作ってください。名前は「Edge Size」にしましょう。

image.png

これを「光る」輪郭のために使うので、「Emission」出力と繋げば良いでしょう。ただし、色も選択したいので「黒(0):光らない/白(1):光る」光る色とリングを掛け算すれば良いでしょう。

image.png

なお、光る強さを調整できるようにするため、EdgeColor のパラメータを「HDR 対応」にしないといけない。EdgeColor を選択し、Node Settings で「Mode:HDR」にしてください。これをすることにより、Inspectorで「Intensity」(強度)を調整ができる。

image.png

さて、「Scroll」テクスチャを「Base Color」にし、「Emission」は今まで通りで、「Alpha」と「Clip Threshold」を元に戻せば、以下のように見える:

image.png

はこうなる ↓

image.png

これで燃えるエフェクトが近づいてきたが、円の形が不自然で、中央から広げてもちょっと揺れる境界線を再現したい。これを実現できるため、Shader Graphは「Noise」ノードを提供する:

Simple Noise ノード

image.png

Simple Noise ノード

  • ランダムノイズを生成

入力

  • UV(2):UV座標。デフォルトとしてモデルのUVを使用
  • Scale(1):ノイズサイズ(拡大・縮小)

設定

  • なし

出力

  • Out(1):2Dのノイズ画像。数値は0~1の間にランダムの数値になる

image.png

 このノイズを「progress」テクスチャと組み合わせれば良いでしょう。これで進歩テクスチャのタイミングを多少ずらすことができる。例えば、Progressは「0.5」(進歩の半分)であったところがランダムノイズを追加するので「0.6」や「0.8」になる可能性があり、リングのしきい値がランダムに変わる。

 

ただし、これで試してみると、2つの問題が現れる:

  • ノイズが強すぎ、調整できない。
  • Progressを1.2にしても、エフェクトが途中で終わってしまう。

なっぜなら、最大のランダム数値は「1」であり、progressテクスチャの最大数値も「1」であるため、2つの数値を足せば、可能な最大数値は「2」になってしまうので、1.2を超えず、途中で終わってしまう。

このため、ノイズの強さを調整できれば良いでしょう。0〜1ではなく、例えば、-0.1〜0.1の間にすれば、progressの範囲は -0.1〜1.1の間になる。Unityは便利な「Remap」ノードを提供する。

Remap ノード

image.png

Remap ノード

  • ある範囲を別の範囲へ変換

入力

  • In(1):変換すべきの数値
  • In Min Max (2):入力数値の範囲(最小値・最大値)
  • Out Min Max(2):希望の出力範囲(最小値・最大値)

設定

  • なし

出力

  • Out(1):「In」 を変換した後の結果

  • In Min Max(入力範囲)0~1
  • Out Min Max(出力範囲)-0.1~0.1
  • In(変換すべき):0.8
  • Out(変換のあと): 0.06

image.png

今回のノイズの強さを調整したいので、Addする前に、Remapで範囲を調整しましょう。

image.png

これでシェーダは完成。Simple Noise の Scale や Remap の範囲をパラメータとして調整できるようにすれば、Materialでいつでも簡単に調整ができる。

完成したシェーダーグラフ:

image.png

ランダム地形を作成

image.png

今まで、フラグメントシェーダーで、3Dモデルの各ピクセルの色を調整してきたが、次「頂点シェーダー」を使い、メッシュの頂点をGPUで変えましょう。

まず、シーンに「terrain」メッシュを追加してください。このメッシュが真平に見えるが、実はたくさんの三角形で分割されている:

image.png

頂点がたくさんあるので、頂点シェーダーで弄ってみましょう。新しいシェーダ「Terrain」を作成し、頂点の位置を編集するので、Positionノードを追加してください。なお、それぞれの頂点の高さ(Y軸の位置)を変えるので、「Height A」パラメータを追加しましょう。Positionノードから得た位置を Split ノード で分解し、頂点のY軸を「Height A」で上書きしましょう。

ただし、分解したベクトルをもう一度3Dベクトルに合体しないといけないので、Splitノードの逆「Combine ノード」を使いましょう:

Combine ノード

image.png

Combine ノード

  • 最大に、4つの数値を1つのベクトルにする

入力

  • R(1):赤色、またはX軸
  • G(1):緑色、またはY軸
  • B(1):青色、またはZ軸
  • A(1):アルファ値、または4Dの4つ目の成分(W)

設定

  • なし

出力

  • RGBA(4):4つの成分のベクトル
  • RGB(3):3つの成分のベクトル
  • RG(2):2つの成分のベクトル

以下のようになる:

image.png

Inspector で Height A の値を変えて、効果を確認してください。たしかに、すべての頂点のXとZが変わらず、Y軸だけ高くなったり、低くなったりするが、すべての頂点が全く同じ高さになってしまう!

ランダムに高さを変えるには、またノイズを使いましょう。山の形やそのた自然な効果を再現できる「Voronoi」ノイズを紹介:

Voronoi ノード

image.png

Voronoi ノード

  • 自然な効果を真似するノイズ

入力

  • UV(2):UV座標。デフォルトとしてモデルのUVを使用
  • AngleOffset(1):黒い中心点を揃えるランダム性
    • 0:縦横できれいに揃える
    • 大きくなると:だんだん混ざっていく
  • CellDensity(1):黒い中心点の数(ノイズ拡大縮小)

設定

  • Hash Type:ノイズを生成方法

出力

  • Out(1):ノイズの値(0~1)
  • Cells(1):(高級)各ピクセルの黒い中心点までの距離

ノイズの出力を Height A との掛け算し、新しい頂点の高さにしましょう。

image.png

は以下のようになる:

image.png

一応、頂点の高さはランダムに変わり、山っぽいものができたが、陰がおかしい。なぜなら、頂点の変更で三角形の向きが変わったが、方法ベクトルの向きは更新されていないから。

image.png

この問題を解決するには、Shader Graph は以下のノードがある:

Normal From Height ノード

image.png

Normal From Height ノード

  • 高さ(Heightmap)から法線マップを作成

入力

  • In(1):高さ
  • Strength(1):法線ベクトルの強さ(長さ)

設定

  • Output Space:方法ベクトルの向き
    • Tangent:(標準)各三角形に相対に作る
    • World:3D空間に対して、絶対位置を使い作る

出力

  • Out(3):各ピクセルの方法ベクトル

Voronoi ノードから高さを取れるので、グラフを分岐し、高さを Normal From Height の In(1) と繋ぎ、方法ベクトルをフラグメントシェーダーの「Normal」と接続。

image.png

これで陰が直る:

image.png

Voronoi の「AngleOffset」と「CellDensity」で山の頻度の分配をコントロールできるので、Blackboard で2つのパラメータ(Randomness, Density)を追加し、繋いでみましょう。

image.png

これで、マテリアル側で山の頻度とランダム性を調整ができる!

image.png

これで単純な山を作成できたが、とてもシャープで、きれいすぎるので、2つのノイズをまぜて、もう少しデコボコの現象を再現してみましょう。また「Simple Noise」を追加し、すでにある高さとの加算してください。なお、2つののノイズの高さは「Height B」で調整できるようにしてください:

image.png

以下のようになる:

image.png

もう少し改善できたが、ノイズが細かすぎるので、「Scale」パラメータを Blackboard に追加し、お好みに調整してみてください:

image.png

image.png

次に色を付けましょう。「Ground」テクスチャをシェーダーに追加し「Base Color」と接続すると。ついでに、表面が輝きすぎるので、Smoothness を 0 にしましょう:

image.png

image.png

これで地形っぽくなったが、頂上周りには雪が欲しくて、残りは「Ground」のままにしたいので、テクスチャを混ぜるノードが必要。Shader Graph で2種類があるが、最も単純なのは:

Lerp ノード

image.png

Lerp ノード

  • Linear Interpolation(線形補間)を計算する

入力

  • A(1):1つ目の数値、またはベクトル
  • B(1):2つ目の数値、またはベクトル
  • T(1):補間パラメータ(0~1)

設定

  • なし

出力

  • Out(1):補間した数値、また各成分。

線形補間は2つの数値の間に線形的に変えてゆく関数である。

  • T は 0 の場合は、Lerp が A を返す。
  • T は 1 の場合は、Lerp が B を返す。
  • その間には、A と B を混ぜて返す。

image.png

 

例えば、

  • A:20
  • B:40
  • T:0.5
  • Lerp(20, 40, 0.5)⇒ 30(T は 0.5=AとBのちょうど真ん中)

2つの画像を Lerp で補間すると:

image.png

T = 0

image.png

T = 1

image.png

T = 0.5

それぞれのピクセルの色を補間し、混ぜる。まず、雪と地面のテクスチャを Lerp で 混ぜましょう:

image.png

これで:

の切り替えができる!そうすると、「ある高さ(しきい値)以上1,それ以下0」を返す Step ノードをを使い、Lerpノードの T と接続しましょう:

image.png

これで、高さは「4」以上だったら「雪」にする処理になっている!

image.png

雪の高さを Blackboard で調整できるようにすれば完成!

image.png

完成したグラフ(クリックで拡大):

image.png


Revision #4
Created 2026-07-01 02:57:29 UTC by Menendez Francisco
Updated 2026-07-06 03:16:15 UTC by Menendez Francisco