Unreal Engine にグローバル シェーダを追加する

独自のグローバル シェーダを追加して使用する方法の概要です。

Choose your operating system:

Windows

macOS

Linux

Global Shaders (グローバル シェーダー) は、作成にマテリアル エディタを使用しないシェーダです。代わりに、作成には C++ を使用します。グローバル シェーダは固定ジオメトリを操作します。マテリアルやメッシュとインターフェース接続する必要はありません。希望の外観を実現するには、より高度な機能が必要な場合があります。そのためには、カスタム シェーダ パスが必要です。

グローバル シェーダの例のいくつかでは、ポストプロセス エフェクトのレンダリング、コンピューティング シェーダのディスパッチ、画面のクリアを行います。

Unreal シェーダ ファイル

Unreal Engine では、 Unreal シェーダ (.usf) ファイルを使用して、使用するシェーダに関する情報の保存と読み取りをします。作成した新規シェーダのソース ファイルは「 Engine/Shaders 」フォルダに保存する必要があります。 シェーダがプラグインの一部である 場合は、代わりに「 Plugin/Shaders 」フォルダに保存する必要があります。

シェーダのコンパイルに関する詳細なログを取得するには、「 ConsoleVariables.ini 」ファイルのコマンド r.ShaderDevelopmentMode=1 を使用します。

詳細については、「 シェーダー開発 」を参照してください。

グローバル シェーダの例

例として、シンプルなパススルー 頂点シェーダ と、カスタムの色を返す ピクセル シェーダ を作成します。

新しいシェーダを作成して追加する

Engine/Shaders 」フォルダに新規テキスト ファイルを作成し、独自のシェーダを作成します。ファイルの拡張子を .usf に変更して、名前を付けます。次の例では、「 MyTest.usf 」を使用します。

次に、「 MyTest.usf 」ファイルに以下のコードを追加します。

MyTest.usf

// Simple pass-through vertex shader

void MainVS(
    in float4 InPosition :ATTRIBUTE0,
    out float4 Output :SV_POSITION
)
{
    Output = InPosition;
}

// Simple solid color pixel shader
float4 MyColor;
float4 MainPS() :SV_Target0
{
    return MyColor;
}

クラスの宣言

次に、Unreal Engine がシェーダを認識してコンパイルを開始するように、C++ クラスを宣言する必要があります。この例では、頂点シェーダをクラスとして使用します。

MyTestVS.h

#include "GlobalShader.h"

// This can go on a header or cpp file
class FMyTestVS : public FGlobalShader
{
    DECLARE_EXPORTED_SHADER_TYPE(FMyTestVS, Global, /*MYMODULE_API*/);

    FMyTestVS() { }
    FMyTestVS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
        :FGlobalShader(Initializer)
    {
    }

    static bool ShouldCache(EShaderPlatform Platform)
    {
        return true;
    }
};

これを行う際に、いくつかの要件があります。

  • これは FGlobalShader のサブクラスです。そのため、これはグローバル シェーダ マップに含まれます。つまり、検索のためのマテリアルは不要です。

  • DECLARE_EXPORTED_SHADER_TYPE() マクロを使用すると、このシェーダ タイプのシリアル化に必要なエクスポートが生成されます。3 つ目のパラメータは、このシェーダ モジュールが存在するコード モジュールの外部リンク タイプを必要に応じて指定します。例えば、レンダラ モジュールに存在しない C++ コードです。

  • 2 つのコンストラクタがあります。デフォルトのコンストラクタとシリアル化コンストラクタです。

  • 特定の状況でこのシェーダをコンパイルするかどうかを判断する ShouldCache() 関数が必要です。例えば、非コンピューティング シェーダ対応の RHI では、コンピューティング シェーダのコンパイルは不要です。

シェーダ タイプを登録する

シェーダ タイプ は、シェーダ コードで指定するテンプレートまたはクラスです。これによって、物理 C++ クラスへのマッピングを行います。シェーダ タイプは、次のコードを使用して Unreal Engine のタイプ リストに登録できます。

// This needs to go on a cpp file
IMPLEMENT_SHADER_TYPE(, FMyTestVS, TEXT("MyTest"), TEXT("MainVS"), SF_Vertex);

このマクロによって、タイプ ( FMyTestVS ) が .usf ファイル ( MyTest.usf )、シェーダ エントリ ポイント ( MainVS )、周波数/シェーダ ステージ ( SF_Vertex ) に登録されます。また、 ShouldCache() メソッドが true を返す場合は、シェーダがコンパイル リストに追加されます。

FGlobalShader の追加先のモジュールは、エンジンを起動する前に読み込む 必要 があります。読み込まない場合、次のようなアサートが表示されます。

> `Shader type was loaded after engine init.Use `ELoadingPhase::PostConfigInit` on your module to cause it to load earlier.` 

ゲームまたはエディタの起動後に、動的モジュールによって独自のシェーダ タイプを追加することはできません。

ピクセル シェーダを宣言する

次に、以下のコードを使用してピクセル シェーダを宣言します。

class FMyTestPS : public FGlobalShader
{
    DECLARE_EXPORTED_SHADER_TYPE(FMyTestPS, Global, /*MYMODULE_API*/);

    FShaderParameter MyColorParameter;

    FMyTestPS() { }
    FMyTestPS(const ShaderMetaType::CompiledShaderInitializerType& Initializer)
        :FGlobalShader(Initializer)
    {
        MyColorParameter.Bind(Initializer.ParameterMap, TEXT("MyColor"), SPF_Mandatory);
    }

    static void ModifyCompilationEnvironment(EShaderPlatform Platform, FShaderCompilerEnvironment& OutEnvironment)
    {
        FGlobalShader::ModifyCompilationEnvironment(Platform, OutEnvironment);
        // Add your own defines for the shader code
        OutEnvironment.SetDefine(TEXT("MY_DEFINE"), 1);
    }

    static bool ShouldCache(EShaderPlatform Platform)
    {
        // Could skip compiling for Platform == SP_METAL for example
        return true;
    }

    // FShader interface.
    virtual bool Serialize(FArchive& Ar) override
    {
        bool bShaderHasOutdatedParameters = FGlobalShader::Serialize(Ar);
        Ar << MyColorParameter;
        return bShaderHasOutdatedParameters;
    }

    void SetColor(FRHICommandList& RHICmdList, const FLinearColor& Color)
    {
        SetShaderValue(RHICmdList, GetPixelShader(), MyColorParameter, Color);
    }
};

// Same source file as before, different entry point
IMPLEMENT_SHADER_TYPE(, FMyTestPS, TEXT("MyTest"), TEXT("MainPS"), SF_Pixel);

このクラスでは、.usf ファイルのシェーダ パラメータ MyColor を公開しています。

  • FShaderParameter MyColorParameter メンバーがクラスに追加されます。これは、バインディングを見付けることのできるランタイムの情報を保持します。これにより、このパラメータの値をランタイム時に設定できます。

  • シリアル化コンストラクタに含まれる Bind() 関数は、パラメータを名前によって ParameterMap にバインドします。これは .usf ファイルの名前に 必ず 一致している必要があります。

  • ModifyCompilationEnvironment() 関数は、同じ C++ クラスでさまざまな動作を定義する場合に使用します。また、シェーダで #define の値を設定できるようにします。

  • Serialize() メソッドは 必須 です。ここでランタイム時に、(シリアル化コンストラクタ時にマッチングされた) シェーダのバインディングのコンパイルとクック時の情報が読み取りや保存が行われます。

  • 最後に、カスタム SetColor() メソッドはランタイム時に指定の値で MyColor パラメータを設定する方法の例を示しています。

シンプルな関数を作成する

次のコードは、指定のシェーダ タイプを使用して全画面のクワッドを描画するシンプルな関数を作成します。

void RenderMyTest(FRHICommandList& RHICmdList, ERHIFeatureLevel::Type FeatureLevel, const FLinearColor& Color)
{
    // Get the collection of Global Shaders
    auto ShaderMap = GetGlobalShaderMap(FeatureLevel);

    // Get the actual shader instances off the ShaderMap
    TShaderMapRef MyVS(ShaderMap);
    TShaderMapRef MyPS(ShaderMap);

    // Declare a bound shader state using those shaders and apply it to the command list
    static FGlobalBoundShaderState MyTestBoundShaderState;
    SetGlobalBoundShaderState(RHICmdList, FeatureLevel, MyTestBoundShaderState, GetVertexDeclarationFVector4(), *MyVS, *MyPS);

    // Call our function to set up parameters
    MyPS->SetColor(RHICmdList, Color);

    // Setup the GPU in prep for drawing a solid quad
    RHICmdList.SetRasterizerState(TStaticRasterizerState::GetRHI());
    RHICmdList.SetBlendState(TStaticBlendState<>::GetRHI());
    RHICmdList.SetDepthStencilState(TStaticDepthStencilState::GetRHI(), 0);

    // Setup the vertices
    FVector4 Vertices[4];
    Vertices[0].Set(-1.0f, 1.0f, 0, 1.0f);
    Vertices[1].Set(1.0f, 1.0f, 0, 1.0f);
    Vertices[2].Set(-1.0f, -1.0f, 0, 1.0f);
    Vertices[3].Set(1.0f, -1.0f, 0, 1.0f);

    // Draw the quad
    DrawPrimitiveUP(RHICmdList, PT_TriangleStrip, 2, Vertices, sizeof(Vertices[0]));
}

これをコードベースでテストします。コンソール変数は、ランタイム時にクリアして切り替えることができます。それには、次のコードを使用します。

static TAutoConsoleVariable CVarMyTest(
    TEXT("r.MyTest"),
    0,
    TEXT("Test My Global Shader, set it to 0 to disable, or to 1, 2 or 3 for fun!"),
    ECVF_RenderThreadSafe
);

void FDeferredShadingSceneRenderer::RenderFinish(FRHICommandListImmediate& RHICmdList)
{
    [...]
    // ***
    // Inserted code, just before finishing rendering, so we can overwrite the screen's contents!
    int32 MyTestValue = CVarMyTest.GetValueOnAnyThread();
    if (MyTestValue != 0)
    {
        FLinearColor Color(MyTestValue == 1, MyTestValue == 2, MyTestValue == 3, 1);
        RenderMyTest(RHICmdList, FeatureLevel, Color);
    }
    // End Inserted code
    // ***
    FSceneRenderer::RenderFinish(RHICmdList);
    [...]
}

プロジェクトを実行し、チルダ (~) キーを使用してコンソール ウィンドウを開いて、追加したコンソール変数の機能をテストします。その後、次のコマンドのいずれか 1 つを入力して、変数を設定します。

  • 色を変更するには、「 r.MyTest 」と 1、2、3 のいずれかの値を入力します。

  • シェーダ パスを無効にするには、「 r.MyTest 0 」と入力します。

追記

  • .usf ファイルのコンパイルのデバッグや、処理したファイルを表示する方法については、「 シェーダ コンパイル プロセスのデバッグ 」を参照してください。

  • クックされていないゲームやエディタの実行中に .usf ファイルを変更して、すばやくイテレーションを行うことができます。キーボード ショートカット「 Ctrl + Shift + . (ピリオド)」を使用するか、コンソール ウィンドウを開いて、コマンド「 recompileshaders changed 」を入力し、シェーダを取得して再ビルドします。

Unreal Engine のドキュメントを改善するために協力をお願いします!どのような改善を望んでいるかご意見をお聞かせください。
調査に参加する
閉じる