シェーダーバリアントは、シェーダー順列とも呼ばれ、シェーダーコードに条件付き動作を導入する方法の 1 つです。
Unity は、シェーダーのソースファイルをシェーダープログラムにコンパイルします。コンパイルされた各シェーダープログラムには、1 つ以上のバリアントがあり、さまざまな条件に応じて異なるバージョンのシェーダープログラムを使用できます。ランタイムに、Unity は現在の要件に一致するバリアントを使用します。シェーダーキーワードを使用してバリアントを設定します。
シェーダーコードにおける条件に関する概要と、どの手法をどのような場合に使用するかについては、シェーダーコードにおける条件を参照してください。Unity がシェーダーバリアントをロードする方法の詳細については、シェーダーのロードを参照してください。
多くのバリアントを持つシェーダーは、“メガシェーダー” または “ウーバーシェーダー” と呼ばれます。Unity のスタンダードシェーダーは、その一例です。
シェーダーバリアントの主な利点は、動的分岐が GPU パフォーマンスへ影響を与えずに、シェーダープログラムでランタイム条件を使用できることです。シェーダーバリアントの主な欠点は、バリアントの数が多いとビルド時とランタイムの両方でパフォーマンスの問題の原因になる可能性があることです。
Unity がシェーダーバリアントを作成する場合は、静的分岐を使用して複数の小さな専用シェーダープログラムを作成します。そのうえで Unity は、ランタイムで条件に一致するシェーダープログラムを使用します。つまり、GPU のパフォーマンスを低下させずに、動的分岐で GPU パフォーマンスの低下を引き起こす可能性のあるコードにシェーダーバリアントを使用できます。
ただし、多数のバリアントがあると、ビルド時間、ファイルサイズ、ランタイムのメモリ使用量、ロード時間が増加する可能性があります。また、シェーダーを手動でプリロード (事前準備) する場合の複雑さが増すこともあります。プロジェクトに非常に多くのシェーダーバリアントが含まれている場合、これらの問題はパフォーマンスとワークフローに重大な問題をもたらす可能性があります。
注意:不用意に過剰な数のシェーダーバリアントを作成すると、パフォーマンスに重大な問題につながる可能性があります。したがって、Unity がシェーダーバリアントの数を決定する方法、コンパイルから不要なバリアントを除外 (“ストリップ”) する方法、およびシェーダーで他のタイプの条件を使用するタイミングを理解することは非常に重要です。
ビルド時に、Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。各グラフィックス API とビルドターゲットの組み合わせに対するバリアントの数は、シェーダーのソースファイルとシェーダーキーワードの使用に依存します。
Unity は、現在のビルドターゲットについてリストの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。シェーダーは、各ビルドターゲットとグラフィックス API の組み合わせごとに異なります。例えば、Unity は iOS の Metal と macOS の Metal に対して、異なるシェーダーをコンパイルします。
シェーダープログラムやキーワードによっては、指定のグラフィックス API やビルドターゲットのみを対象とするものもあります。そのため、グラフィックス API とビルドターゲットの組み合わせごとのバリアントの数は異なります。ただし、これらのバリアントをコンパイルする手順は同じです。
現在のビルドターゲットのグラフィックス API のリストを表示および編集するには、Player Settings ウィンドウ、または PlayerSettings API を使用します。
Unity は、現在のビルドターゲットとグラフィックス API の組み合わせに対して、コンパイルするシェーダープログラム数を決定する必要があります。
ビルドに含まれるシェーダーのソースファイルごとに、固有のシェーダープログラムをいくつ定義するかが決定されます。
注意シェーダーのソースファイルは、ビルドのシーンで参照されている場合、Resources フォルダーの何かに参照されている場合、または Graphics Settings ウィンドウの Always-included Shaders セクションに含まれている場合に、そのビルドに含まれます。
Unity は、現在のビルドターゲットとグラフィックス API に対して、コンパイルするシェーダープログラム数を決定し、次に、各シェーダープログラムに対してコンパイルしなければならないシェーダーバリアントの数を決定します。
各シェーダープログラムに対して、Unity はさまざまなシェーダーキーワードの組み合わせ、つまりさまざまなバリアントを決定します。これは以下によって構成されています。
Unity が 1 つのシェーダープログラムに対してコンパイルするシェーダーバリアントの数は、キーワードの積です。つまり、Unity は各セットから 1 つの要素を含むすべての組み合わせに対して 1 つのバリアントをコンパイルします。
例えば、このセットには 3 つのシェーダーバリアントキーワードが含まれています。
このセットには、4 つのシェーダーバリアントキーワードが含まれています。
これらのシェーダーバリアントキーワードの影響を受けるシェーダープログラムは、以下の 12 種類のバリアントになります。
シェーダーバリアントキーワードのセットを追加すると、Unity がコンパイルするバリアントの数が急速に増加することがあります。この非常に急速な増加を表す用語は、組み合わせ爆発です。
例えば、非常に一般的なユースケースについて考えてみましょう。1 つのシェーダーに複数のシェーダーバリアントキーワードのセットがあり、それぞれに 2 つのキーワード (<feature name>_ON と <feature name>_OFF) が含まれている場合です。シェーダーにこのようなキーワードが 2 組ある場合、これは 4 つのバリアントになります。シェーダーにこのようなキーワードのセットが 10 個ある場合、1024 のバリアントになります。
コンパイル後、Unity は同一パス内の同一バリアントを自動的に識別し、これらの同一バリアントが同じバイトコードを指すようにします。これを重複排除と呼びます。
重複排除は、同一パス内の同一バリアントによるファイルサイズの増加を防ぎますが、同一バリアントがあると、コンパイル時に無駄な作業が発生したり、ランタイムにメモリ使用量やシェーダーのロード時間が増加したりします。この点を考慮して、必要のないバリアントは常に取り除く方が良いでしょう。