Version: 2023.1
言語: 日本語
シェーダーにおける条件
シェーダーバリアント

シェーダーにおける分岐

分岐は、シェーダーコードに条件付きの動作を導入する方法の 1 つです。

このセクションでは、以下の手法について説明します。

シェーダーコードにおける条件分岐に関する概要と、どちらの手法をどのような場合に使用するかについては、シェーダーコードにおける条件分岐 を参照してください。

静的分岐

シェーダープログラムにコンパイル時に評価される条件文が含まれている場合は、静的分岐が使用されます。コンパイラーは使用されていない分岐側のコードを除外するため、そのコードはコンパイルされたシェーダープログラムには含まれません。

内部的には、Unity はシェーダーバリアントの作成時に静的分岐を使用します。ただし、静的分岐自体には、シェーダーバリアントが持つパフォーマンス上のデメリットはありません。

静的分岐のメリットとデメリット

静的分岐の主なメリットは、ランタイムのパフォーマンスに悪影響を与えないことです。静的分岐の主なデメリットは、コンパイル時間にしか使用できないことです。

静的分岐を使用する場合、コンパイラーがシェーダープログラムから不要なコードを除外します。この結果、必要なコードのみを含む、小さく特化したシェーダープログラムが作成されます。静的分岐にはランタイムのパフォーマンスコストがありません。実際には、小さなプログラムほど、ロード時間が短くなりランタイムのメモリ使用量も少なくなる傾向にあります。

静的分岐を使用するには、条件がコンパイル時間に一定である必要があります。つまり、ランタイムに異なる条件用にコードを実行するために使用することはできません。

静的分岐の使用方法

シェーダーへの静的分岐の使用は、以下の方法で行います。

  • ハンドコーディングされたシェーダー内で以下を行います。
    • #if#elif#else および #endif プリプロセッサーディレクティブまたは #ifdef および #ifndef プリプロセッサーディレクティブを使用して静的分岐を作成します。
    • コンパイル時間の定数値を評価する if ステートメント を使用します。if ステートメントは動的分岐にも使用できますが、これを行う代わりに、コンパイラーは、コンパイル時間の定数値を検出して静的分岐を作成します。
    • Unity は、静的分岐に使用できる一部のコンパイル時定数用に ビルトインのマクロ を提供しています。

ノート: 静的分岐はハンドコーディングされたシェーダーでのみ使用可能です。Shader Graph で静的分岐を作成することはできません。

動的分岐

シェーダープログラムにランタイムで評価される条件文が含まれている場合は、動的分岐が使用されます。

動的分岐には、均一 (uniform) 変数に基づく動的分岐と、それ以外のランタイム値に基づく動的分岐の 2 種類があります。均一値がドローコール全体を通して一定であるため、通常は均一変数に基づく分岐のほう効率的です。

動的分岐には シェーダーキーワード を使用できます。これにより、C# スクリプトとマテリアルインスペクターを使用してシェーダーのランタイム分岐動作を設定することが可能になります。これを行う場合、Unity はシェーダーキーワードを均一値としてコンパイルします。

動的分岐のメリットとデメリット

動的分岐の主なメリットは、プロジェクト内のシェーダーバリアントの数を増やすことなくランタイムで条件分岐を使用できることです。動的分岐の主なデメリットは、GPU パフォーマンスに影響を与えることです。

GPU パフォーマンスへの影響は、ハードウェアによって、またシェーダコードによって異なります。その理由は以下の通りです。

  • 均一変数ではない変数に基づく分岐を行う場合、GPU は、異なる操作を同時に実行する (つまり並列性を損ねる) か、または “分岐を平坦化” して、両方の分岐の操作を実行して片方の結果を破棄することで並列性を維持するかの、いずれかを行う必要があります。均一変数に基づく分岐を行う場合は、GPU は分岐を平坦化する必要があります。どちらのアプローチも、結果的に GPU のパフォーマンスを低下さます。
  • どのようなタイプの動的分岐であっても、GPU は最悪のケースを想定してレジスター空間を割り当てる必要があります。一方の分岐側がもう一方と比較して大幅にコストが高い場合、これは GPU がレジスター空間を浪費することを意味します。これにより、シェーダープログラムの並列呼び出しの数が少なくなり、パフォーマンスが低下する可能性があります。

一般的に、コードが均一値で分岐し、両方の分岐の処理負荷がほぼ同様である場合は、GPU パフォーマンスへの影響は小さい傾向にあります。ただし、必ずアプリケーションをプロファイリングし、ケースバイケースでメリットとデメリットを検討する必要があります。

ノート: 動的分岐を使用すると、シェーダープログラムが大きくなる可能性もあります。これは、全ての条件のコードが同じシェーダープログラムにコンパイルされるためです。ただし、通常は、こうした大きなファイルがロード時間やメモリ使用量に与える影響は、シェーダーバリアントによる影響ほどは大きくありません。

シェーダーで条件分岐を使用する他の方法と、自身のユースケースにどの手法が適しているかの判断方法については、シェーダーにおける条件分岐 を参照してください。

動的分岐の使用方法

シェーダーへの動的分岐の使用は、以下の方法で行います。

  • ハンドコーディングされたシェーダー内で以下を行います。
    • 任意: 動的分岐に使用する シェーダーキーワード を設定します。シェーダーキーワードなしで動的分岐を使用することも可能ですが、これによりセットアップとマテリアルごとの設定が簡単になります。
    • シェーダーキーワード (使用されている場合) やその他のランタイムの状態を評価する if ステートメント を使用します。属性を使用して、GPU に、両方の分岐先コードの実行あるいは 1 つの分岐先のみの実行を強制することもできます。
  • Shader Graph では、Branch ノード を使用します。これは常に両方の分岐先を実行します。
シェーダーにおける条件
シェーダーバリアント