【Unity】IF文を使わない!ShaderGraphのスマートな条件分岐の作り方
はじめに
初めての方も、そうでない方もこんにちは!
現役ゲームプログラマーのたむぼーです。
自己紹介を載せているので、気になる方は見ていただければ嬉しいです!
今回は
IF文を使わない!ShaderGraphのスマートな条件分岐の作り方
について紹介します
ShaderGraphのIF文について
ShaderGraphでIF文に相当するノードは「Branchノード」です。
このノードは、コンパイル時にIF文として解釈されるため、実質的にはシェーダー内での「分岐処理」となります。
シェーダーでIF文を使うと「動的な分岐」になり、GPUの並列処理を妨げてパフォーマンスが低下する可能性があります。
以下の例では、Modeの値によって赤・青・緑に切り替える処理をBranchノードで実装しています。
(Mode = 1: 赤、Mode = 2: 青、それ以外: 緑)
IF文を使わない分岐について
全体
この分岐は「Subtractノード」「Stepノード」「OneMinusノード」「Multiplyノード」などを組み合わせて、条件を数値処理で実現しています。
条件に合致するかどうかを「0か1のFloat」に変換し、Lerpで最終的な色を選択しています。
数値的条件式の詳細
Modeの値を整数化
Mode(Float)の値を「Floorノード」で整数化します。
(※Branchノードの条件式でも同じように使えます。)
Floorノードとは、切り捨てを行うノードで、小数点以下を切り捨てて最も近い小さい整数を返します。
例えば:
0.1 → 0
1.9 → 1
-0.3 → -1 ←(※マイナスもちゃんと繰り下げになる)
このように、入力が少数でも確実に整数値として条件判定できるようになるため、Modeによる分岐処理などにとても便利です。
数値的条件式について
整数化したModeの値を使い分岐します。
この例では、Modeが1の時に赤(1,0,0)、それ以外は黒(0,0,0)になります。
- [Mode]の値を[Subtractノード]で差分を取る(Bの値がMode一致条件)
A: Modeの値、B: 条件(Mode - 条件)になる - [Subtractノード]のOutを[Absoluteノード]で絶対値にする
「0 → 0」「-3 → 3」「2.5 → 2.5」というイメージ - [Stepノード]で閾値比較(閾値は0.01固定)
条件に一致の場合「In = 0」になるので、Inの値が0以外の場合は条件に一致していないとなる - [One Minusノード]で[Stepノード]の結果を反転(Stepノードで0以外の場合はTrueなので)
- [Multiplyノード]で条件結果(0 or 1)と反映したい値を乗算する
[Mode]の値と[Subtractノード]のBが一致しないと、条件結果が0になるので、反映したい値がどんなあたいでも0になる
今回は、[Multiplyノード]でVector3の色情報を渡しているので、一致する場合は赤、しない場合は黒になる
※条件を増やす場合は、一連のノードを複製し、[Subtractノード]のBの値と、[Multiplyノード]のBの値を変更することで、分岐を増やす事が可能
グループ化しておくと管理が楽になります。
図解
┌────────────┐
│ Mode │ ← Floorで整数化したMode値
└────┬───────┘
↓
┌────────────┐
│ Subtract │ ← Mode - B
│ B: 1 │ ← Bの値が条件になる(Modeが1の時にTureする場合)
└────┬───────┘
↓
┌────────────┐
│ Absolute │ ← 絶対値をとる
└────┬───────┘
↓
┌────────────┐
│ Step │ ← 閾値 0.01 を使って 0/1 判定
│ Edge: 0.01 │ ← Inに入る値が0の場合は、Modeと一致しているとなる
└────┬───────┘
↓
┌────────────┐
│ OneMinus │ ← 判定結果を反転(StepのInが0の場合の出力はFalseなので、Trueにする)
└────┬───────┘
↓
┌───────────────────┐
│ Multiply │
│ A * B │ ◀── 条件に一致するか(0 or 1) * 適応したい値
│ A: 0 or 1 │ ◀── Aは条件に一致するか
│ B: Vector3(1,0,0) │ ◀── Bは適応したい値(今回は赤色)
└────┬──────────────┘
↓
┌────────────────────┐
│ Out │
│ (1,0,0) or (0,0,0) │
└────────────────────┘
最終的な合成について
数値的条件式の結果を合成して最終的な一つにする
- 各条件の[Multiplyノード]のOutを[Addノード]で合成する
2以上の条件がある場合は、[Addノード]のOutと次の条件の[Multiplyノード]のOutをさらに合成する - デフォルトが必要な場合は最後の条件を合成後のOutとデフォルトの値を[Lerpノード]で切り替える
最終的なAddの結果で[Lerpノード]のTを求める
※Tを求めるノードは[Addノード]結果によって変更が必要
Vector3の場合、[Addノード]→[Lengthノード]→[Stepノード]→[OneMinusノード]
Floatの場合、[Addノード]→[OneMinusノード]
図解
条件1 条件2 条件3
↓ ↓ ↓
┌────────────┐ ┌────────────┐ ┌────────────┐
│ Multiply │ │ Multiply │ │ Multiply │
└────┬───────┘ └────┬───────┘ └────┬───────┘
↓ │ │
┌────────────┐ │ │
│ Add │◀──────┘ │
└────┬───────┘ │
↓ ◀── 条件1と条件2の合成結果 │
┌────────────┐ │
│ Add │◀──────────────────────┘
└────┬───────┘
├──────────────┐
│ ↓
│ ┌────────────┐
│ │ Length │ ◀── ※デフォルトが必要な場合
│ └────┬───────┘ (以下はVector3の場合なので処理によって変更が必要)
│ ↓
│ ┌────────────┐
│ │ Step │ ◀── Lengthの結果が0.01より大きい場合
│ │ Edge: 0.01 │
│ └────┬───────┘
│ ↓
│ ┌────────────┐
│ │ OneMinus │
↓ └────┬───────┘
┌────────────┐ │
│ Lerp │ │
│ A: Add │ │
│ B: Default │ │
│ T: 0 or 1 │◀────┘ ◀── OneMinusの結果で、Addを使うか、Defaultを使うか
└────┬───────┘
↓
┌────────────────┐
│ Out │
│ Add or Default │
└────────────────┘
Discussion