docs.unity3d.com
    目次を表示する/隠す

    レンダーパイプラインの作成

    このページでは、RenderGraph API を使用してレンダーパイプラインを記述する手順を説明します。RenderGraph API については、レンダーグラフシステム および レンダーグラフの基礎 を参照してください。

    レンダーグラフの初期化とクリーンアップ

    はじめに、レンダーパイプラインには少なくとも 1 つの RenderGraph インスタンスを維持する必要があります。これがこの API へのメインエントリーポイントです。レンダーグラフの複数のインスタンスを使用することもできますが、Unity では RenderGraph インスタンス間でリソースが共有されないことに注意が必要です。メモリ使用量を最適に保つために、使用するインスタンスは 1 つだけにしてください。

    using UnityEngine.Experimental.Rendering.RenderGraphModule;
    
    public class MyRenderPipeline : RenderPipeline
    {
        RenderGraph m_RenderGraph;
    
        void InitializeRenderGraph()
        {
            m_RenderGraph = new RenderGraph(“MyRenderGraph”);
        }
    
        void CleanupRenderGraph()
        {
            m_RenderGraph.Cleanup();
              m_RenderGraph = null;
        }
    }
    

    RenderGraph インスタンスを初期化するにはコンストラクターを呼び出します。このとき、レンダーグラフを識別する名前を任意で指定できます。初期化すると、SRP のデバッグウィンドウにレンダーグラフ専用のパネルが登録されます。これは RenderGraph インスタンスのデバッグに役立ちます。レンダーパイプラインを破棄するときは、レンダーグラフで割り当てたすべてのリソースを適切に解放するために、RenderGraph インスタンスで Cleanup() メソッドを呼び出します。

    レンダーグラフの開始

    レンダーグラフにレンダーパスを追加する前に、まずレンダーグラフを初期化する必要があります。そのためには、Begin メソッドを呼び出します。このメソッドのパラメーターの詳細については、API ドキュメント を参照してください。

    var renderGraphParams = new RenderGraphExecuteParams()
    {
        scriptableRenderContext = renderContext,
        commandBuffer = cmd,
        currentFrameIndex = frameIndex
    };
    
    m_RenderGraph.Begin(renderGraphParams);
    

    レンダーグラフのリソースの作成

    レンダーグラフの使用時には、開発者が直接リソースを割り当てるわけではありません。RenderGraph インスタンスのリソースの割り当てと破棄は、そのインスタンス自体によって処理されます。リソースを宣言してレンダーパスで使用するには、リソースにハンドルを返すレンダーグラフ固有の API を呼び出します。

    レンダーグラフで使用されるリソースには、大きく分けて次の 2 種類があります。

    • 内部リソース: これらのリソースはレンダーグラフの実行時に内部で使用され、RenderGraph インスタンスの外部からアクセスすることはできません。あるグラフの実行から別の実行にこれらのリソースを渡すこともできません。これらのリソースの生存期間は、レンダーグラフによって管理されます。
    • インポートされたリソース: これらは通常、レンダーグラフの実行の外部から取得されます。代表的な例として、バックバッファ (カメラから提供) や、一時的な効果用に複数のフレーム間で使い回すバッファ (一時的なアンチエイリアスのためにカメラのカラーバッファを使用する場合など) があります。これらのリソースの生存期間は、開発者が管理する必要があります。

    リソースを作成またはインポートすると、レンダーグラフシステムは、そのリソースをリソースタイプ固有のハンドル (TextureHandle、ComputeBufferHandle、または RendererListHandle) として表します。この方法により、レンダーグラフでは、すべての API で内部リソースとインポートされたリソースを同じように使用できます。

    public TextureHandle RenderGraph.CreateTexture(in TextureDesc desc);
    public ComputeBufferHandle RenderGraph.CreateComputeBuffer(in ComputeBufferDesc desc)
    public RendererListHandle RenderGraph.CreateRendererList(in RendererListDesc desc);
    
    public TextureHandle RenderGraph.ImportTexture(RTHandle rt);
    public TextureHandle RenderGraph.ImportBackbuffer(RenderTargetIdentifier rt);
    public ComputeBufferHandle RenderGraph.ImportComputeBuffer(ComputeBuffer computeBuffer);
    

    リソースの主な作成方法は上記のとおりですが、これらの関数にはバリエーションがあります。すべての一覧については、API ドキュメント を参照してください。カメラのバックバッファをインポートするための関数は RenderTargetIdentifier です。

    リソースを作成するには、各 API にパラメーターとして記述子構造体を渡す必要があります。これらの構造体のプロパティは、それぞれが表すリソース (RTHandle、ComputeBuffer、RendererList) のプロパティとほぼ同様です。ただし、レンダーグラフテクスチャに固有のプロパティもあります。

    最も重要なプロパティを以下に示します。

    • clearBuffer: このプロパティは、バッファの作成時にそのバッファを消去する必要があるかどうかをグラフに指示します。レンダーグラフの使用時には、この方法でテクスチャを消去する必要があります。レンダーグラフではリソースがプールされるため、パスでテクスチャを作成すると、そのパスはコンテンツが定義されていない既存のテクスチャを受け取る可能性があります。そのため、テクスチャをクリアすることが重要です。

    • clearColor: このプロパティは、バッファを消去するときに使用する色 (適用される場合) を格納します。

    レンダーグラフが TextureDesc コンストラクターを通して公開するテクスチャには、2 つの固有の概念もあります。

    • xrReady: このブーリアンは、このテクスチャが XR レンダリング用かどうかをグラフに示します。true の場合、レンダーグラフは、XR のそれぞれの眼にレンダリングするための配列としてテクスチャを作成します。
    • dynamicResolution: このブーリアンは、アプリケーションが動的解像度を使用している場合に、このテクスチャのサイズを動的に変更する必要があるかどうかをグラフに示します。false の場合、テクスチャは自動的には拡大縮小されません。

    リソースは、レンダーパスの外部やレンダーパスの設定コードの内部で作成できますが、レンダリングコード内では作成できません。

    最初のパスで使用するリソースが、頻繁に変更される可能性のあるコード内のロジックに依存しているような場合は、すべてのレンダーパスの外部でリソースを作成すると便利です。この場合、リソースはどのパスよりも先に作成する必要があります。例として、カラーバッファをディファードライティングパスとフォワードライティングパスのどちらかに使用する場合を考えます。これらのパスは両方ともカラーバッファに書き込みますが、Unity は、カメラ用に選択された現在のレンダリングパスに応じてどちらか一方だけを実行します。このような場合は、カラーバッファを両方のパスの外部に作成し、適切なパスにパラメーターとして渡します。

    レンダーパスの内部でリソースを作成することは、通常、レンダーパス自体が作成するリソース用です。例えば、ブラーパスには既存の入力テクスチャが必要ですが、出力はパス自体によって作成され、レンダーパスの最後に返されます。

    このようにリソースを作成する場合、すべてのフレームに GPU メモリが割り当てられるわけではありません。代わりに、レンダーグラフシステムはプールされたメモリを再利用します。レンダーグラフのコンテキストでは、リソースの作成を実際のメモリ割り当てとして捉えるより、レンダーパスのコンテキストにおけるデータフローのようなものと考える方が的確です。レンダーパスがまったく新しい出力を作成すると、レンダーグラフに新しいテクスチャが "作成" されます。

    レンダーパスの作成

    Unity でレンダーグラフを実行するには、その前にすべてのレンダーパスを宣言する必要があります。レンダーパスは、設定とレンダリングという 2 つの部分に分けて記述します。

    設定

    設定の段階では、レンダーパスと、その実行に必要なすべてのデータを宣言します。レンダーグラフはデータをレンダーパスに固有のクラスによって表し、関連するすべてのプロパティはそのクラスに含まれます。これらは、通常の C# 構造体 (struct、PoD など) と、レンダーグラフのリソースハンドルです。このデータ構造体には、実際のレンダリングコードの中でアクセスすることができます。

    class MyRenderPassData 
    {
        public float parameter;
        public TextureHandle inputTexture;
        public TextureHandle outputTexture;
    }
    

    パスデータを定義したら、レンダーパス自体を宣言できます。

    using (var builder = renderGraph.AddRenderPass<MyRenderPassData>("My Render Pass", out var passData))
    {
            passData.parameter = 2.5f;
        passData.inputTexture = builder.ReadTexture(inputTexture);
    
        TextureHandle output = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
                            { colorFormat = GraphicsFormat.R8G8B8A8_UNorm, clearBuffer = true, clearColor = Color.black, name = "Output" });
        passData.outputTexture = builder.WriteTexture(output);
    
        builder.SetRenderFunc(myFunc); // details below.
    }
    

    レンダーパスは、AddRenderPass 関数の周りの using スコープ内で定義します。スコープの最後で、レンダーグラフは後の処理のために、レンダーパスをレンダーグラフの内部構造体に加えます。

    builder 変数は RenderGraphBuilder のインスタンスです。ここがレンダーパスに関する情報を構築するエントリーポイントとなります。いくつかの重要な部分について以下で説明します。

    • リソース使用法の宣言: これが RenderGraph API で最も重要な面の 1 つです。ここで、レンダーパスがリソースへの読み取りアクセスや書き込みアクセスを必要とするかどうかを明示的に宣言します。これにより、レンダーグラフでレンダリングフレームの全体像を把握し、GPU メモリの最適な使用と、さまざまなレンダーパス間の同期ポイントを決定できるようになります。
    • レンダリング関数の宣言: この関数の中でグラフィックスコマンドを呼び出します。この関数は、レンダーパス用に定義したパスデータと、レンダーグラフコンテキストをパラメーターとして受け取ります。レンダーパスのレンダリング関数を設定するには SetRenderFunc を使用します。関数はグラフのコンパイル後に実行されます。
    • 一時リソースの作成: 一時リソースまたは内部リソースとは、このレンダーパスが継続している間だけ存在するリソースです。これらのリソースは、その生存期間が反映されるように、レンダーグラフ自体ではなくビルダー内で作成されます。一時リソースの作成には、RenderGraph API 内の同等の関数と同じパラメーターを使用します。これは、パスが外部からアクセスされてはいけない一時バッファを使用する時に特に便利です。一時的なリソースを宣言したパスの外部では、リソースへのハンドルが無効になり、使用しようとすると Unity からエラーがスローされます。

    passData 変数は、パスの宣言時に開発者が指定した型のインスタンスです。ここにレンダリングコードからアクセスできるデータを設定します。passData のコンテンツはレンダーグラフですぐに使用されるのではなく、すべてのパスが登録され、レンダーグラフがコンパイルされて実行された後に、フレーム内で使用されます。このため、passData に格納する参照はすべて、フレーム全体を通じて変わらずに維持される必要があります。そうでない場合、レンダーパスの実行前にコンテンツが変更されると、レンダーパスの実行中には正しくないコンテンツが含まれることになります。この理由から、パスの実行が終了するまで参照が維持されることが確実でない限り、passData には値型のみを格納することをお勧めします。

    RenderGraphBuilder API の概要については、以下の表を参照してください。詳細については、API ドキュメントを参照してください。

    関数 目的
    TextureHandle ReadTexture(in TextureHandle input) この関数に渡した input テクスチャからレンダーパスが読み取ることを宣言します。
    TextureHandle WriteTexture(in TextureHandle input) この関数に渡した input テクスチャにレンダーパスが書き込むことを宣言します。
    TextureHandle UseColorBuffer(in TextureHandle input, int index) WriteTexture と同じですが、指定したバインディングインデックスのレンダーテクスチャとして、テクスチャをパスの開始時に自動的にバインドします。
    TextureHandle UseDepthBuffer(in TextureHandle input, DepthAccess flags) WriteTexture と同じですが、関数に渡したアクセスフラグを使用して、テクスチャを深度テクスチャとして自動的にバインドします。
    TextureHandle CreateTransientTexture(in TextureDesc desc) 一時テクスチャを作成します。このテクスチャは、パスが継続している間だけ存在します。
    RendererListHandle UseRendererList(in RendererListHandle input) この関数に渡したレンダラーリストをレンダーパスが使用することを宣言します。レンダーパスは、RendererList.Draw コマンドを使用してリストをレンダリングします。
    ComputeBufferHandle ReadComputeBuffer(in ComputeBufferHandle input) この関数に渡した input 計算バッファからレンダーパスが読み取ることを宣言します。
    ComputeBufferHandle WriteComputeBuffer(in ComputeBufferHandle input) この関数に渡した input 計算バッファにレンダーパスが書き込むことを宣言します。
    ComputeBufferHandle CreateTransientComputeBuffer(in ComputeBufferDesc desc) 一時計算バッファを作成します。このテクスチャは、計算バッファが継続している間だけ存在します。
    void SetRenderFunc(RenderFunc renderFunc) where PassData :class, new() レンダーパスのレンダリング関数を設定します。
    void EnableAsyncCompute(bool value) レンダーパスが非同期計算パイプラインで実行されることを宣言します。
    void AllowPassCulling(bool value) Unity でレンダーパスをカリングする必要があるかどうかを指定します (デフォルトは true です)。これは、レンダーパスに副作用があり、レンダーグラフシステムでカリングが行われないようにする場合に役立ちます。

    レンダリングコード

    設定が完了したら、RenderGraphBuilder の SetRenderFunc メソッドを通じて、レンダリングに使用する関数を宣言できます。割り当てる関数には、以下のシグネチャが使用されている必要があります。

    delegate void RenderFunc<PassData>(PassData data, RenderGraphContext renderGraphContext) where PassData :class, new();
    

    レンダー関数は static 関数またはラムダ (lambda) として渡すことができます。ラムダ関数を使用するメリットは、レンダリングコードと設定コードを同じ場所に記述できるため、コードの透明性が向上する点にあります。

    ただし、ラムダを使用する場合は、関数のメインスコープにあるパラメーターをキャプチャしないように十分に注意してください。これを行うとガベージが生成され、Unity は後のガベージコレクションのときに、これを見つけて解放します。Visual Studio を使用している場合は、=> の上にマウスポインターを置くと、ラムダがスコープから何かをキャプチャしているかどうかを確認できます。メンバーやメンバー関数を使用すると this がキャプチャされるため、これらへのアクセスは避けてください。

    レンダー関数は次の 2 つのパラメーターを受け取ります。

    • PassData data: レンダーパスの宣言時に渡す型のデータです。これを通じて、設定フェーズで初期化したプロパティにアクセスし、レンダリングコードで使用することができます。
    • RenderGraphContext renderGraphContext: ScriptableRenderContext および CommandBuffer への参照が格納されています。これらによってユーティリティ関数が提供され、レンダリングコードを記述できるようになります。
    レンダーパス内のリソースへのアクセス

    レンダリング関数の内部では、passData に格納されているすべてのレンダーグラフリソースハンドルにアクセスできます。実際のリソースへの変換は自動的に行われるため、関数が RTHandle、ComputeBuffer、RendererList を必要とする時にはいつでもハンドルを渡すことができ、レンダーグラフがハンドルを実際のリソースに暗黙的に変換します。ただし、このような暗黙変換をレンダリング関数の外部で実行すると、例外が発生することに注意してください。レンダリングの外部では、これらのリソースがまだレンダーグラフによって割り当てられていない場合があるため、この例外が発生します。

    RenderGraphContext の使用

    RenderGraphContext は、レンダリングコードを記述するために必要なさまざまな機能を提供します。最も重要なのが、あらゆるレンダリングコマンドの呼び出しで使用する ScriptableRenderContext と CommandBuffer の 2 つです。

    RenderGraphContext には RenderGraphObjectPool も含まれています。このクラスは、レンダリングコードに必要な一時オブジェクトの管理に役立ちます。

    Get Temp 関数

    レンダーパスで特に役立つ関数として、GetTempArray と GetTempMaterialPropertyBlock の 2 つがあります。

    T[] GetTempArray<T>(int size);
    MaterialPropertyBlock GetTempMaterialPropertyBlock();
    

    GetTempArray は、型が T でサイズが size の一時的な配列を返します。これは、マテリアルにパラメーターを渡すための一時的な配列を割り当てる場合や、RenderTargetIdentifier 配列を作成して複数のレンダーターゲットの設定を作成する場合に便利です。配列の生存期間を自分で管理する必要はありません。

    GetTempMaterialPropertyBlock は、クリーンなマテリアルプロパティブロックを返します。これを使用してマテリアルのパラメーターを設定できます。マテリアルは複数のパスで使用されることがあり、それぞれのパスではマテリアルに異なるパラメーターを使用する可能性があるため、これは非常に重要です。レンダリングコードの実行はコマンドバッファ経由で遅延されるため、実行時のデータ整合性を保つには、マテリアルプロパティブロックをコマンドバッファにコピーする必要があります。

    レンダーグラフは、これらの 2 つの関数から返されるすべてのリソースを、パスの実行後に自動的に解放してプールします。このため、開発者がこれらを自分で管理する必要はなく、ガベージが発生することもありません。

    レンダーパスの例

    以下のコード例は、設定コードとレンダー関数を含むレンダーパスを示しています。

    TextureHandle MyRenderPass(RenderGraph renderGraph, TextureHandle inputTexture, float parameter, Material material)
    {
        using (var builder = renderGraph.AddRenderPass<MyRenderPassData>("My Render Pass", out var passData))
        {
            passData.parameter = parameter;
            passData.material = material;
    
            // このパスが inputTexture を読み取ることをグラフに通知します。
            passData.inputTexture = builder.ReadTexture(inputTexture);
    
            // 出力テクスチャを作成します。
            TextureHandle output = renderGraph.CreateTexture(new TextureDesc(Vector2.one, true, true)
                            { colorFormat = GraphicsFormat.R8G8B8A8_UNorm, clearBuffer = true, clearColor = Color.black, name = "Output" });
            // このパスがこのテクスチャを書き込むことと、これをレンダーターゲット 0 として設定する必要があることをグラフに通知します。
            passData.outputTexture = builder.UseColorBuffer(output, 0);
    
            builder.SetRenderFunc(
            (MyRenderPassData data, RenderGraphContext ctx) =>
            {
                // レンダーターゲットは、上記の UseColorBuffer の使用を通じて既に設定されています。
                // builder.WriteTexture を使用した場合は、次のようなコードが必要になります。
                // CoreUtils.SetRenderTarget(ctx.cmd, data.output);
    
                // レンダリング用のマテリアルを設定します。
                var materialPropertyBlock = ctx.renderGraphPool.GetTempMaterialPropertyBlock();
                materialPropertyBlock.SetTexture("_MainTexture", data.input);
                materialPropertyBlock.SetFloat("_FloatParam", data.parameter);
    
                CoreUtils.DrawFullScreen(ctx.cmd, data.material, materialPropertyBlock);
            });
    
            return output;
        }
    }
    

    レンダーグラフの実行

    すべてのレンダーパスを宣言したら、レンダーグラフを実行する必要があります。そのためには、Execute メソッドを呼び出します。

    m_RenderGraph.Execute();
    

    これにより、レンダーグラフをコンパイルして実行するプロセスが発生します。

    フレームの終了

    アプリケーション全体にわたって、レンダーグラフはさまざまなリソースを割り当てる必要があります。これらのリソースはある時点では使用されますが、その後不要になることがあります。グラフでこのようなリソースを解放するには、EndFrame() メソッドをフレームごとに 1 回呼び出します。これにより、前のフレーム以降にレンダーグラフで使用されなかったリソースの割り当てがすべて解除されます。さらに、レンダーグラフがフレームの最後で必要とする内部処理もすべて実行されます。

    このメソッドは、すべてのレンダリングの完了後 (例えば、最後のカメラがレンダリングを完了した後) に、フレームごとに 1 回だけ呼び出す必要があります。これは、カメラごとにレンダリングパスが異なる可能性があり、必要なリソースも異なるためです。各カメラの後でこのメソッドを呼び出すと、レンダーグラフがリソースを早期に解放する結果となり、次のカメラで必要となるリソースも解放されてしまう可能性があります。

    トップに戻る
    Copyright © 2023 Unity Technologies — 商標と利用規約
    • 法律関連
    • プライバシーポリシー
    • クッキー
    • 私の個人情報を販売または共有しない
    • Your Privacy Choices (Cookie Settings)