Version: 2023.1
言語: 日本語
シェーダーキーワード
マテリアルインスペクターでのシェーダーキーワードの使用

C# スクリプトでのシェーダーキーワードの使用

シェーダーキーワードは有効にも無効にもできます。シェーダーキーワードを有効または無効にすると、シェーダーが適切な条件付き動作を表示します。つまり、(キーワードがシェーダーバリアントと動的分岐のどちらと連携するかによって) Unity が適切なシェーダーバリアントをレンダリングするか、GPU が適切な分岐を実行します。

このページには以下の情報が含まれています。

ローカルシェーダーキーワードとグローバルシェーダーキーワード

Unity は、C# でシェーダーキーワードを表すにあたって、ローカルシェーダーキーワード および グローバルシェーダーキーワード という概念を使用します。

ローカルシェーダーキーワードは、シェーダーソースファイル内で宣言する全てのキーワードによって構成されます。ローカルシェーダーキーワードは、個々のシェーダーまたはコンピュートシェーダーに影響を与えます。ローカルキーワードは、ローカルスコープかグローバルスコープを持つことができ、これによって、グローバルシェーダーキーワードでオーバーライド可能かどうかが変わります。

グローバルシェーダーキーワードは、ローカルシェーダーキーワードのオーバーライドとして機能します。これらはシェーダーソースファイルでは宣言されず、C# コードにのみ存在します。グローバルシェーダーキーワードは、同時に複数のシェーダーおよびコンピュートシェーダーに影響を与えることができます。

ローカルシェーダーキーワード

シェーダーソースファイル内でシェーダーキーワードを宣言する場合、Unity はこれを C# で LocalKeyword 構造体で表現します。これを ローカルシェーダーキーワード と呼びます。

LocalKeywordisOverridable プロパティは、そのキーワードがソースファイル内でグローバルスコープとローカルスコープのどちらで宣言されたかを示します。キーワードがグローバルスコープで宣言され、したがって同名のグローバルシェーダーキーワードでオーバーライド可能な場合、これは true になります。キーワードがローカルスコープで宣言され、したがって同名のグローバルシェーダーキーワードでオーバーライドできない場合は、false になります。

Unity は、シェーダーやコンピュートシェーダーに影響を与える全てのローカルシェーダーキーワードを LocalKeywordSpace 構造体に保存します。グラフィックスシェーダーの場合は、Shader.keywordSpace でこれにアクセスできます。コンピュートシェーダーの場合は、ComputeShader-keywordSpace でこれにアクセスできます。

グローバルシェーダーキーワード

Unity は、ソースファイル内で宣言したローカルシェーダーキーワードに加え、それとは別の グローバルシェーダーキーワード のリストを管理します。グローバルシェーダーキーワードはシェーダーソースファイル内で宣言するものではなく、C# で扱うローカルシェーダーキーワードのランタイムオーバーライドです。グローバルシェーダーキーワードは、同時に複数のシェーダーおよびコンピュートシェーダーに影響を与えることができます。

Unity はグローバルシェーダーキーワードを GlobalKeyword 構造体で表します。

グローバルシェーダーキーワードを設定すると、同じシェーダーキーワードを多数のマテリアルやコンピュートシェーダーに対して有効化または無効化する必要がある場合に便利なことがあります。しかしこれは、以下のような不都合をもたらす可能性があります。

  • キーワードのグローバル状態を設定すると、シェーダーが誤って同じ名前のキーワードを定義した場合に、意図しない結果になる可能性があります。キーワードをローカルスコープで宣言するか、衝突の可能性を低減する方法でキーワードを命名することで、これを防ぐことができます。
  • 新しい GlobalKeyword を作成すると、Unity は、この時点でロードされている全てのシェーダーおよびコンピュートシェーダーに関して、グローバルキーワード空間とローカルキーワード空間の間の内部マッピングを更新します。この操作は CPU に高い負荷をかける場合があります。この操作の影響を軽減するには、できるだけ、アプリケーションの開始後すぐに (アプリケーションのロード中に) 全てのグローバルキーワードを作成してください。

ローカルシェーダーとグローバルシェーダーキーワードの相互作用

同じ名前を持つグローバルキーワードとローカルシェーダーキーワードの状態が異なる場合、Unity は、LocalKeywordisOverridable プロパティを使用して、個々のマテリアルまたはコンピュートシェーダーに対してそのキーワードが有効であるか無効であるか判断します。isOverridable は、キーワードがグローバルスコープで宣言されていれば true、 ローカルスコープで宣言されていれば false となります。

  • isOverridabletrue の場合: 同じ名前のグローバルキーワードが存在し、それが有効になっている場合、Unity は、グローバルキーワードの状態を使用します。それ以外の場合は、ローカルキーワードの状態を使用します。
  • isOverridablefalse の場合: Unity は常にローカルキーワードの状態を使用します。

したがって、個々のマテリアルやコンピュートシェーダーに関してシェーダーキーワードが有効か無効か把握するには、isOverridable プロパティの状態と、グローバルキーワードやローカルキーワードの状態を調査する必要があります。

以下の例は、特定のマテリアルについて、Unity がキーワードを有効と見なすか無効と見なすか確認する方法を示しています。

using UnityEngine;
using UnityEngine.Rendering;

public class KeywordExample : MonoBehaviour
{
    public Material material;

    void Start()
    {
        CheckShaderKeywordState();
    }

    void CheckShaderKeywordState()
    {
        // Get the instance of the Shader class that the material uses
        var shader = material.shader;

        // Get all the local keywords that affect the Shader
        var keywordSpace = shader.keywordSpace;

        // Iterate over the local keywords
        foreach (var localKeyword in keywordSpace.keywords)
        {
            // If the local keyword is overridable (i.e., it was declared with a global scope),
            // and a global keyword with the same name exists and is enabled,
            // then Unity uses the global keyword state
            if (localKeyword.isOverridable && Shader.IsKeywordEnabled(localKeyword.name))
            {
                Debug.Log("Local keyword with name of " + localKeyword.name + " is overridden by a global keyword, and is enabled");
            }
            // Otherwise, Unity uses the local keyword state
            else
            {
                var state = material.IsKeywordEnabled(localKeyword) ? "enabled" : "disabled";
                Debug.Log("Local keyword with name of " + localKeyword.name + " is " + state);
            }            
        }
    }
}

シェーダーバリアントと動的分岐

Unity では、シェーダーキーワードをシェーダーバリアントで使用することも、動的分岐で使用することもできます。キーワードを宣言する際にこれを決定してください。

LocalKeywordisDynamic プロパティは、キーワードが、シェーダーソースファイル内で、動的分岐で使用するために宣言されたかどうかを示します。キーワードが動的分岐で使用するために宣言された場合は trueとなり、シェーダーバリアントで使用するために宣言された場合は false となります。

シェーダーキーワードの有効化と無効化

グラフィックスシェーダーのローカルパスワードが有効になっているかどうか確認するには、Material.IsKeywordEnabledMaterial.EnableKeyword を使用してください。コンピュートシェーダーには、ComputeShader.IsKeywordEnabledComputeShader.EnableKeyword を使用してください。

グローバルキーワードが有効になっているか確認するには、Shader.IsKeywordEnabledShader.EnableKeyword、または ComputeShader.enabledKeywords を使用してください。

グラフィックスシェーダーのローカルシェーダーキーワードを有効化または無効化するには、Material.SetKeywordMaterial.EnableKeyword、または Material.DisableKeyword を使用してください。コンピュートシェーダーには、ComputeShader.SetKeywordComputeShader.EnableKeyword、または ComputeShader.DisableKeyword を使用してください。

グローバルシェーダーキーワードを有効化または無効化するには、Shader.EnableKeywordShader.DisableKeyword を使用してください。

コマンドバッファでグローバルキーワードを有効化または無効化するには、CommandBuffer.EnableKeywordCommandBuffer.DisableKeyword を使用してください。

ノート: シェーダーバリアントを扱うキーワードを有効化または無効化すると、Unity は別のシェーダーバリアントを使用します。ランタイムにシェーダーバリアントを変更すると、パフォーマンスに影響を与える可能性があります。キーワードを変更するために特定のバリアントを初めて使用する必要が生じる場合、グラフィックスドライバーがシェーダープログラムの準備をする間に、がたつきが発生する可能性があります。これは、大きなシェーダーや複雑なシェーダーで、あるいはグローバルキーワードの状態の変化が複数のシェーダーに影響する場合に、特に問題となる場合があります。これを回避するため、シェーダーバリアントでキーワードを使用する場合には、シェーダーのローディングと事前準備のアプローチを決定する際にキーワードバリアントのことを考慮してください。詳細は、Unity がシェーダーをロードして使用する方法 を参照してください。

ランタイムにキーワードのセットを管理する

シェーダーをオーサリングする際は、キーワードをセットで宣言します。セットには、互いに排他的なキーワードが含まれます。

ランタイムでは、Unity はこうしたセットの概念を持ちません。任意のキーワードを個別に有効化または無効化することが可能になっており、キーワードを有効化または無効化しても、他のどのキーワードの状態にも影響しません。つまり、同じセットに含まれる複数のキーワードを有効にしたり、セット内の全てのキーワードを無効にしたりすることができます。

シェーダーバリアントでキーワードを使用すると、セット内の複数のキーワードが有効になっている場合や、セット内のどのキーワードも有効になっていない場合に、Unity によって “十分に一致する” と見なされたバリアントが選択されます。具体的に何が起こるか正確な保証がなく、意図しない結果につながる可能性があります。キーワードの状態を慎重に管理することで、このような状況を避けるのが最善です。

動的分岐でキーワードを使用すると、セット内の複数のキーワードが有効になっている場合や、セット内のどのキーワードも有効になっていない場合に、それに応じて条件分岐が実行されます。例えば、KEYWORD_AKEYWORD_B の両方が有効な場合、if (KEYWORD_A)if (KEYWORD_B) のブランチが両方実行されます。

シェーダーキーワード
マテリアルインスペクターでのシェーダーキーワードの使用