Version: 2020.3
言語: 日本語
シェーダーのバリエーションとシェーダーのキーワード
シェーダーキーワード

シェーダーバリアント

Unity は、シェーダーのソースファイルを個々のシェーダープログラムにコンパイルします。コンパイルされた各シェーダープログラムは、1 つまたは複数の バリアント を持っています。バリアントとはシェーダープログラムのバージョンのことで、シェーダーキーワード の異なる組み合わせで動作します。ランタイムに、Unity がジオメトリをレンダリングするとき、現在の要件に合うバリアントを使用します。Unity がどのようにシェーダーバリアントをロードして使用するかについては、シェーダーのロード を参照してください。

シェーダープログラムには、その時点のマテリアルに必要なコードだけが含まれているため、シェーダーバリアントはパフォーマンスを向上させるのに役立ちます。一般的にこの方法で最適化されるものは、テクスチャの読み込み、頂点の入力、インターポレーター、ループのような複雑なコードなどです。また、シェーダープログラム自体も小さくなります。

シェーダーバリアントは、同じシェーダーソースファイルを違う方法で使用できるため、ワークフローの改善にもつながります。例えば、様々なマテリアルのための設定、異なるハードウェアのための機能定義、ランタイムのシェーダーの動作の動的変更などが可能です。

しかし、潜在的なデメリットもあります。非常に多くのバリアンとを簡単に作れるため、以下が起こる可能性があります。

  • ビルド時間、ファイルサイズ、ランタイムのメモリ使用量、ロード時間が増加する。
  • シェーダーを手動でプリロード (事前準備) する際に、より複雑になる。

大規模なプロジェクトでは、これらの問題がパフォーマンスやワークフローの重大な問題につながる可能性があります。そのため、シェーダーバリアントの仕組みを理解し、不要なバリアントをコンパイルから除外 (“ストリップ”) する方法を理解することが非常に重要です。

非常に多くのバリアントを持つシェーダーは、“メガシェーダー” または “ウーバーシェーダー” と呼ばれます。Unity のスタンダードシェーダーは、その一例です。

シェーダーバリアントの数

ビルド時に、Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。各グラフィックス API とビルドターゲットの組み合わせに対するバリアントの数は、シェーダーのソースファイルとシェーダーキーワードの使用に依存します。

グラフィックス API

Unity は現在のビルドターゲットの各グラフィックス API に対して 1 セットのシェーダーバリアントをコンパイルします。シェーダーは、各ビルドターゲットとグラフィックス API の組み合わせごとに異なります。例えば、Unity は iOS の Metal と macOS の Metal に対して、異なるシェーダーをコンパイルします。

シェーダープログラムやキーワードによっては、指定のグラフィックス API やビルドターゲットのみを対象とするものもあります。そのため、グラフィックス API とビルドターゲットの組み合わせごとのバリアントの数は異なります。ただし、これらのバリアントをコンパイルする手順は同じです。

現在のビルドターゲットのグラフィックス API のリストを表示および編集するには、Player 設定 ウィンドウ、または PlayerSettings API を使用します。

シェーダープログラムの数

Unity は、現在のビルドターゲットとグラフィックス API の組み合わせに対して、コンパイルするシェーダープログラム数を決定する必要があります。

ビルドに含まれるシェーダーのソースファイルごとに、固有のシェーダープログラムをいくつ定義するかが決定されます。

  • コンピュートシェーダーアセットは、1 つのシェーダープログラムを定義します。
  • ハンドコードされたシェーダーでは、シェーダープログラムの数はコードに依存します。総数は以下から構成されます。
    • ソースファイルのすべてのパスのすべてのシェーダーステージ。例えば、各頂点ステージで 1 つのシェーダープログラムを定義し、各フラグメントステージで 1 つのシェーダープログラムを定義する、というように。
    • ソースファイルの依存関係にあるすべてのパスのすべてのシェーダーステージ。これには、すべての フォールバックシェーダーUsePass コマンド を使用するすべてのパスが含まれます。
  • Shader Graph シェーダーでは、シェーダープログラムの数は、Unity がグラフから生成するコードに依存します。Unity が生成するシェーダーコードを見るには、Shader Graph アセットを右クリックし、 See generated code を選択します。その後、ハンドコードされたシェーダーの場合と同じ方法で、シェーダープログラムの総数が決まります。

ノート: シェーダーのソースファイルは、ビルドのシーンで参照されている場合、Resources フォルダーの何かによって参照されている場合、Graphics Settings ウィンドウの Always-included Shaders セクションに含まれている場合に、そのビルドに含まれます。

シェーダープログラムに影響を与えるキーワード

Unity は、現在のビルドターゲットとグラフィックス API に対して、コンパイルするシェーダープログラム数を決定し、次に、各シェーダープログラムに対してコンパイルしなければならないシェーダーバリアントの数を決定します。

各シェーダープログラムに対して、Unity はそのプログラムに影響を与えるシェーダーキーワードの組み合わせを決定します。これは以下によって構成されています。

Unity が 1 つのシェーダープログラムに対してコンパイルするシェーダーバリアントの数は、キーワードの積です。つまり、Unity は各セットから 1 つの要素を含むすべての組み合わせに対して 1 つのバリアントをコンパイルします。

例えば、このセットには 3 つのキーワードが含まれています。

  • COLOR_RED
  • COLOR_GREEN
  • COLOR_BLUE

このセットには 4 つのキーワードが含まれています。

  • QUALITY_LOW
  • QUALITY_MEDIUM
  • QUALITY_HIGH
  • QUALITY_ULTRA

これらのキーワードの影響を受けるシェーダープログラムは、以下の 12 種類のヴァリアントになります。

  • COLOR_RED、QUALITY_LOW
  • COLOR_RED、QUALITY_MEDIUM
  • COLOR_RED、QUALITY_HIGH
  • COLOR_RED、QUALITY_ULTRA
  • COLOR_GREEN、QUALITY_LOW
  • COLOR_GREEN、QUALITY_MEDIUM
  • COLOR_GREEN、QUALITY_HIGH
  • COLOR_GREEN、QUALITY_ULTRA
  • COLOR_BLUE and QUALITY_LOW
  • COLOR_BLUE and QUALITY_MEDIUM
  • COLOR_BLUE and QUALITY_HIGH
  • COLOR_BLUE and QUALITY_ULTRA

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) を参照してください。

エディター UI でのシェーダーバリアントのストリッピング

Unity エディターの UI には、シェーダーストリッピングを設定できる箇所がいくつかあります。

  • Graphics Settings ウィンドウ で、Shader stripping セクションの設定を行います。
  • Always-included shaders 設定で、必要のないシェーダーが含まれないようにします 。
  • GPU インスタンス化、ライトマッピング、フォグに関連するバリアントを除去します。
  • ビルトインレンダーパイプラインでは、階層設定が違うことが重要でない場合は、お互いに同じ階層設定にしてください。詳しくは、グラフィックスの階層 を参照してください。
  • ユニバーサルレンダーパイプライン (URP)では、URP アセットで使用しない機能を無効にします。詳細は、シェーダーストリッピング を参照してください。

エディタースクリプトを使ったシェーダーバリアントのストリッピング

他の方法では除去できないシェーダーバリアントについては、エディタースクリプトで以下の API を使用してビルド時のストリッピングを行うことができます。

ストリッピングについての詳細は、Unity のブログ スクリプタブルシェーダーバリアントの除去 を参照してください。

シェーダーのバリエーションとシェーダーのキーワード
シェーダーキーワード