Unity は、シェーダーのソースファイルを個々のシェーダープログラムにコンパイルします。コンパイルされた各シェーダープログラムは、1 つまたは複数の バリアント を持っています。バリアントとはシェーダープログラムのバージョンのことで、シェーダーキーワード の異なる組み合わせで動作します。ランタイムに、Unity がジオメトリをレンダリングするとき、現在の要件に合うバリアントを使用します。Unity がどのようにシェーダーバリアントをロードして使用するかについては、シェーダーのロード を参照してください。
シェーダープログラムには、その時点のマテリアルに必要なコードだけが含まれているため、シェーダーバリアントはパフォーマンスを向上させるのに役立ちます。一般的にこの方法で最適化されるものは、テクスチャの読み込み、頂点の入力、インターポレーター、ループのような複雑なコードなどです。また、シェーダープログラム自体も小さくなります。
シェーダーバリアントは、同じシェーダーソースファイルを違う方法で使用できるため、ワークフローの改善にもつながります。例えば、様々なマテリアルのための設定、異なるハードウェアのための機能定義、ランタイムのシェーダーの動作の動的変更などが可能です。
しかし、潜在的なデメリットもあります。非常に多くのバリアンとを簡単に作れるため、以下が起こる可能性があります。
大規模なプロジェクトでは、これらの問題がパフォーマンスやワークフローの重大な問題につながる可能性があります。そのため、シェーダーバリアントの仕組みを理解し、不要なバリアントをコンパイルから除外 (“ストリップ”) する方法を理解することが非常に重要です。
非常に多くのバリアントを持つシェーダーは、“メガシェーダー” または “ウーバーシェーダー” と呼ばれます。Unity のスタンダードシェーダーは、その一例です。
ビルド時に、Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。各グラフィックス API とビルドターゲットの組み合わせに対するバリアントの数は、シェーダーのソースファイルとシェーダーキーワードの使用に依存します。
Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。シェーダーは、各ビルドターゲットとグラフィックス API の組み合わせごとに異なります。例えば、Unity は iOS の Metal と macOS の Metal に対して、異なるシェーダーをコンパイルします。
シェーダープログラムやキーワードによっては、指定のグラフィックス API やビルドターゲットのみを対象とするものもあります。そのため、グラフィックス API とビルドターゲットの組み合わせごとのバリアントの数は異なります。ただし、これらのバリアントをコンパイルする手順は同じです。
現在のビルドターゲットのグラフィックス API のリストを表示および編集するには、Player 設定 ウィンドウ、または PlayerSettings API を使用します。
Unity は、現在のビルドターゲットとグラフィックス API の組み合わせに対して、コンパイルするシェーダープログラム数を決定する必要があります。
ビルドに含まれるシェーダーのソースファイルごとに、固有のシェーダープログラムをいくつ定義するかが決定されます。
ノート: シェーダーのソースファイルは、ビルドのシーンで参照されている場合、Resources フォルダーの何かによって参照されている場合、Graphics Settings ウィンドウの Always-included Shaders セクションに含まれている場合に、そのビルドに含まれます。
Unity は、現在のビルドターゲットとグラフィックス API に対して、コンパイルするシェーダープログラム数を決定し、次に、各シェーダープログラムに対してコンパイルしなければならないシェーダーバリアントの数を決定します。
各シェーダープログラムに対して、Unity はそのプログラムに影響を与えるシェーダーキーワードの組み合わせを決定します。これは以下によって構成されています。
Unity が 1 つのシェーダープログラムに対してコンパイルするシェーダーバリアントの数は、キーワードの積です。つまり、Unity は各セットから 1 つの要素を含むすべての組み合わせに対して 1 つのバリアントをコンパイルします。
例えば、このセットには 3 つのキーワードが含まれています。
このセットには 4 つのキーワードが含まれています。
これらのキーワードの影響を受けるシェーダープログラムは、以下の 12 種類のヴァリアントになります。
Unity がコンパイルするバリアントの数は、キーワードのセットを増やせば増や すほど急速に増えていきます。例えば、かなり典型的なユースケースを考えてみましょう。シェーダーが、それぞれ 2 つのキーワード (<feature name>_ON
と <feature name>_ OFF
) を持つ多くのキーワードセットを持っている場合です 。シェーダーがそのキーワードセットを 2 個持っている場合、これは 4 つのバリアントになります。シェーダーがそのキーワードセットを 10 個持っている場合は、1024 つのバリアントになります。
コンパイル後、Unity は同一パス内の同一バリアントを自動的に識別し、これらの同一バリアントが同じバイトコードを指すようにします。これを 重複排除 と呼びます。
重複排除は、同一パス内の同一バリアントによるファイルサイズの増加を防ぎますが、同一バリアントがあると、コンパイル時に無駄な作業が発生したり、実行時にメモリ使用量やシェーダーのロード時間が増加したりします。この点を考慮して必要のないバリアントは常に取り除くほうが良いでしょう。
シェーダーバリアントがコンパイルされないようにすることができます。これは ストリッピング と呼ばれています。不要なバリアントをストリッピングすると、ビルド時間、ファイルサイズ、 シェーダーのロード時間、ランタイムのメモリ使用量を大幅に削減できます。大規模なプロジェクトや複雑なシェーダーを持つプロジェクトでは、これは非常に重要な考慮すべき事項です。
以下のシェーダーキーワードの宣言方法によって、生成されるバリアントの数を制限することができます。
multi_compile
の代わりに shader_feature
を可能な限り使用します。multi_compile
で使わないキーワードを定義しないようにします。ハンドコーディングされたシェーダーでこれを行う方法については、HLSL でのシェーダーキーワードの宣言と使用 を参照してください。Shader Graph での実行については、[Shader Graph ブラックボード]を参照してください。Blackboard](https://docs.unity3d.com/Packages/com.unity.shadergraph@latest?subfolder=/manual/Blackboard.html) を参照してください。
Unity エディターの UI には、シェーダーストリッピングを設定できる箇所がいくつかあります。
他の方法では除去できないシェーダーバリアントについては、エディタースクリプトで以下の API を使用してビルド時のストリッピングを行うことができます。
ストリッピングについての詳細は、Unity のブログ スクリプタブルシェーダーバリアントの除去 を参照してください。