重いノイズ計算や法線変形を、描画効率向上のために頂点シェーダー(Vertex)側で行い、ピクセル(Fragment)側へデータを正しく引き渡すための「Custom Interpolator(カスタム補間)」の罠を解消します。

不具合の症状と具体的な現象

Shader Graphにおいて、頂点シェーダー側で行った計算結果をピクセル(Fragment)側に渡すために Custom Interpolator を追加した際、以下の描画バグが発生します。

  • マテリアルを適用した3Dモデルが、カメラの角度や距離によって激しくねじれたり伸縮したりする。
  • モデルの表面に奇妙なノイズやストライプ状の模様が現れ、描画結果が安定しない。
  • シェーダーのコンパイルエラーが発生し、モデルが真っピンク(マゼンタ)で表示される。
  • 色のグラデーションや波の表現がポリゴンの境界でカクカクと不連続になる。

初心者向けの例え話:山頂の旗と斜面のペインター

この仕組みと不具合の原因を理解するために、頂点シェーダーを「山の山頂や尾根に立てられたいくつかの旗(頂点)」、ピクセルシェーダーを「旗と旗の間の斜面を塗装するペインター(ピクセル)」に例えてみましょう。

山の尾根にある旗には、それぞれ「ここは風の強さ10」「あちらの旗は風の強さ0」という情報が書かれています。ペインターが斜面を塗るとき、旗の間に張られたまっすぐなロープ(カスタム補間器)を参考にします。ペインターは、2本の旗の中間地点にいれば「風の強さは中間の5だな」と自動的に計算して滑らかに色を塗ることができます。これが「線形補間」と呼ばれる正しい動作です。

しかし、ここで「風が吹いている方角(ベクトル)」という複雑な情報をそのまま渡そうとすると問題が起きます。片方の旗では「東向き(1, 0, 0)」、もう片方の旗では「西向き(-1, 0, 0)」だったとします。ペインターが中間地点でロープを参考にすると、平均値として「(0, 0, 0)つまり無風」というおかしな中間データを受け取ってしまいます。方向ベクトルは直線的に変化するものではないため、データが歪んでしまうのです。また、旗が「風の強さ」を渡しているのに、ペインター側が「風の向き」を受け取ろうと待ち構えているような、手紙の形式(データの次元数)が食い違っている場合も、ペインターは混乱して塗装作業を完全に放棄(描画崩壊)してしまいます。

想定される主な原因

Shader Graphにおけるカスタム補間の描画エラーは、以下の原因によって引き起こされます。

  1. ベクトルの線形補間による歪み: ベクトル等の「向き」を頂点側から渡すと、線形補間でベクトルの長さが縮んでしまいます。ピクセル側で再正規化(Normalize)を行わないとライティング計算が歪みます。
  2. データの型とチャンネル数の不一致: 頂点側で定義した Custom Interpolator の型(例: Vector3)と、そこに接続したノードの出力型、およびピクセル側で取り出す型が一致していない場合にエラーが発生します。
  3. 座標空間の不一致: 頂点での計算時とピクセルでの利用時で、オブジェクト空間とワールド空間などの座標系が混在してしまっているケースです。

具体的な解決方法と手順

Shader Graphにおける正しい Custom Interpolators の運用フローです。

手順1:Graph Settings での定義

Shader Graphの右上にある **Graph Settings** を開き、**Custom Interpolators** の設定を展開し、新しい補間器を追加します。渡したいデータの次元数に合った名前と型を設定してください(例: `WorldPos (Vector3)`)。

手順2:Vertex Block(頂点出力部)での接続

定義が完了すると、頂点シェーダーの出力部である **Vertex Block** に、定義した `WorldPos` というスロットが自動的に追加されます。ここに、頂点側で計算したノード(例: `Position` ノードのワールド空間設定)の出力を接続します。

手順3:Fragment Block(ピクセル入力部)での受取と歪み補正

ピクセルシェーダー側でこのデータを読み出します。 `Custom Interpolator` ノードを作成し、定義した `WorldPos` を選択します。**もしデータが「方向ベクトル」である場合は、必ず接続する直前に `Normalize` ノードを噛ませてください。** これにより、長さが縮んでしまったベクトルが正しい向きと長さ(1)に修復され、描画が正常化します。

実務向けチェックリスト&対応表

チェック項目 確認すべき動作 修正手順・推奨対策
データ型の次元一致 設定した型と接続するノードの次元は同じか? Graph Settings内の定義と、頂点・ピクセルの入出力を統一する。
ベクトルの正規化 ピクセル側で受け取ったベクトルはそのまま計算に使っていないか? 必ず `Normalize` ノードを通してから計算に繋ぐ。

以下は、Custom Interpolator に対応した HLSL カスタムコードを Shader Graph 内の **Custom Function ノード** で使用し、データを渡すためのコードブロックの例です。

// 頂点シェーダー側でカスタム計算を行い、データを出力する関数
void CalculateCustomInterpolation_float(
    float3 positionOS,      // オブジェクト空間の頂点座標(入力)
    float3 normalOS,        // オブジェクト空間の法線(入力)
    float3 timeParameters,  // 時間パラメータ(入力)
    out float3 customData,  // カスタム補間器へ渡すデータ(出力)
    out float3 vertexOffset // 頂点変形オフセット(出力)
)
{
    float wave = sin(positionOS.y * 5.0 + timeParameters.x * 2.0) * 0.1;
    vertexOffset = normalOS * wave;
    customData = positionOS + float3(wave, wave * 2.0, wave * 0.5);
}

このように、Custom Interpolator を用いて頂点シェーダーとピクセルシェーダーの処理を最適化することで、GPU性能が限られたターゲットデバイスにおいて、描画負荷を劇的に改善することができます。グラフィックスの品質を保ちながら最高のFPSを引き出すために有効なテクニックです。