Version: 2021.1
言語: 日本語
カスタムレンダーパイプラインでのレンダーパイプラインアセットとレンダーパイプラインインスタンスの作成
カメラ

カスタムレンダーパイプラインでのシンプルなレンダーループの作成

レンダーループとは、1 つのフレーム内で行われるすべてのレンダリング操作を意味します。このページでは、Unity のスクリプタブルレンダーパイプラインに基づいたカスタムレンダーパイプラインで、シンプルなレンダーループを作成する方法を説明します。

このページのコード例は、スクリプタブルレンダーパイプラインを使用する際の基本原則を示しています。この情報は、独自のカスタムスクリプタブルレンダーパイプラインを構築する際や、Unity にあらかじめ用意されているスクリプタブルレンダーパイプラインがどのように動作するかを理解するために利用できます。

プロジェクトの準備

レンダーループのコードを書き始める前に、プロジェクトの準備をする必要があります。

その手順は以下の通りです。

  1. SRP 対応のシェーダーを作成 します。
  2. レンダリングする 1 つまたは複数のゲームオブジェクトを作成 します。
  3. カスタム SRP の基本構造を作成 します。
  4. 任意: シンプルなカスタム SRP を拡張して、より複雑な機能を追加することを予定している場合は、SRP Core パッケージをインストールしてください。SRP Core パッケージには、SRP Core シェーダーライブラリ (シェーダーを SRP バッチャーに対応させるために使用します) と、一般的な操作のためのユーティリティ関数が含まれています。詳細については、SRP Core パッケージのドキュメント を参照してください。

SRP 対応のシェーダーの作成

スクリプタブルレンダーパイプラインでは、LightMode パスタグを使用して、ジオメトリの描画方法を決定します。パスタグの詳細については、ShaderLab: タグをパスに割り当てる を参照してください。

このタスクでは、LightMode パスタグ ExampleLightModeTag によって、非常にシンプルな unlit シェーダーオブジェクトを作成する方法を説明します。

  1. プロジェクトに新しいシェーダーアセットを作成します。シェーダーアセットの作成方法については、シェーダーアセット を参照してください。
  2. Project ビューで、シェーダーアセットをダブルクリックして、シェーダーのソースコードをテキストエディターで開きます。
  3. 既存のコードを以下のように置き換えてください。
// これは、カスタムのスクリプタブルレンダーパイプラインと互換性のある単純な unlit シェーダーオブジェクトを定義します。
// ハードコードされた色を適用し、LightMode パスタグの使用法を示します。
// SRP バッチャーとは互換性がありません。

Shader "Examples/SimpleUnlitColor"
{
    SubShader
    {
        Pass
        {
            // LightMode パスタグの値は、ScriptableRenderContext.DrawRenderers の ShaderTagId と一致する必要があります。
            Tags { "LightMode" = "ExampleLightModeTag"}

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

    float4x4 unity_MatrixVP;
            float4x4 unity_ObjectToWorld;

            struct Attributes
            {
                float4 positionOS   : POSITION;
            };

            struct Varyings
            {
                float4 positionCS : SV_POSITION;
            };

            Varyings vert (Attributes IN)
            {
                Varyings OUT;
                float4 worldPos = mul(unity_ObjectToWorld, IN.positionOS);
                OUT.positionCS = mul(unity_MatrixVP, worldPos);
                return OUT;
            }

            float4 frag (Varyings IN) : SV_TARGET
            {
                return float4(0.5,1,0.5,1);
            }
            ENDHLSL
        }
    }
}

レンダリングするゲームオブジェクトの作成

レンダーループが動作することをテストするには、レンダリングするものを作成する必要があります。ここでは、前のタスクで作成した SRP 対応シェーダーを使うゲームオブジェクトをシーンに配置する方法を説明します。

  1. Unity プロジェクトに新しいマテリアルアセットを作成します。手順については、マテリアル を参照してください。
  2. シェーダーアセットをマテリアルアセットに割り当てます。手順については、マテリアル を参照してください。
  3. シーンにキューブを作成します。手順については、プリミティブオブジェクト を参照してください。
  4. キューブにマテリアルを割り当てます。手順については、マテリアル を参照してください。

カスタム SRP の基本構造の作成

準備の最終段階では、カスタム SRP に必要な基本的なソースファイルを作成し、Unity にカスタム SRP を使ったレンダリングを開始するように指示します。

  1. レンダーパイプラインインスタンスとレンダーパイプラインアセットの作成 の手順に沿って、RenderPipeline を継承したクラスと互換性のあるレンダーパイプラインアセットを作成します。
  2. アクティブレンダーパイプラインアセットの設定 の指示に従って、アクティブなレンダーパイプラインアセットを設定します。Unity はカスタム SRP を使ってすぐにレンダリングを開始します。つまり、カスタム SRP にコードを加えるまでは、シーンビューとゲームビューは空白です。

レンダーループの作成

単純なレンダーループの基本的な操作は次のとおりです。

  • レンダーターゲットの消去。最後のフレームで描かれたジオメトリを削除します。
  • カリング。カメラに見えないジオメトリをフィルタリングします。
  • 描画。どのジオメトリを、どのように描画するかを GPU に指示します。

レンダーターゲットの消去

消去とは、最後のフレームで描画されたものを取り除くことです。レンダリングターゲットは通常スクリーンですが、テクスチャにレンダリングして「ピクチャーインピクチャー」効果を出すこともできます。これらの例では、Unity のデフォルト動作であるスクリーンへのレンダリング方法を示しています。

スクリプタブルレンダーパイプラインでレンダーターゲットを消去するには、以下を行います。

  1. CommandBufferClear コマンドで設定します。
  2. CommandBufferScriptableRenderContext のコマンドキューに加えます。これを行うには、ScriptableRenderContext.ExecuteCommandBuffer を呼び出します。
  3. グラフィックス API に ScriptableRenderContext のコマンドキューを実行するように指示します。これを行うには、ScriptableRenderContext.Submit を呼び出します。

他のスクリプタブルレンダーパイプラインの操作と同様に、このコードのエントリーポイントとして、RenderPipeline.Render メソッドを使用します。以下のサンプルコードは、その方法を示しています。

/* 
これは、カスタムのスクリプタブルレンダーパイプラインの簡略化された例です。
基本的なレンダーループがどのように機能するかを示しています。
最も効率的なランタイムパフォーマンスというよりは、最も明確なワークフローを示しています。
*/

using UnityEngine;
using UnityEngine.Rendering;

public class ExampleRenderPipeline : RenderPipeline {
    public ExampleRenderPipeline() {
    }

    protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
        // 現在のレンダーターゲットを消去するコマンドを作成してスケジューリングします
        var cmd = new CommandBuffer();
        cmd.ClearRenderTarget(true, true, Color.black);
        context.ExecuteCommandBuffer(cmd);
        cmd.Release();

        // スケジュールされたすべてのコマンドを実行するようにグラフィックス API に指示します
        context.Submit();
    }
}

カリング

カリングとは、カメラに映らないジオメトリをフィルタリングする処理です。

スクリプタブルレンダーパイプラインでカリングを行うには、以下を行います。

  1. ScriptableCullingParameters 構造体にカメラに関するデータを収集します。これを行うには、Camera.TryGetCullingParameters を呼び出します。
  2. 任意: ScriptableCullingParameters 構造体の値を手動で更新します。
  3. ScriptableRenderContext.Cull を呼び出して、その結果を CullingResults 構造体に格納します。

このサンプルコードは、上の例を拡張したもので、レンダーターゲットをクリアしてからカリング操作を行う方法を示しています。

/* 
これは、カスタムのスクリプタブルレンダーパイプラインの簡略化された例です。
基本的なレンダーループがどのように機能するかを示しています。
最も効率的なランタイムパフォーマンスというよりは、最も明確なワークフローを示しています。
*/

using UnityEngine;
using UnityEngine.Rendering;

public class ExampleRenderPipeline : RenderPipeline {
    public ExampleRenderPipeline() {
    }

    protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
        // コマンドを作成してスケジューリングし、現在のレンダーターゲットを消去します。
        var cmd = new CommandBuffer();
        cmd.ClearRenderTarget(true, true, Color.black);
        context.ExecuteCommandBuffer(cmd);
        cmd.Release();

        // すべてのカメラに繰り返します
        foreach (Camera camera in cameras)
        {
            // 使用しているカメラからカリングパラメーターを取得します。
            camera.TryGetCullingParameters(out var cullingParameters);

            // カリングパラメーターを使ってカリング操作を行い、結果を保存します。
            var cullingResults = context.Cull(ref cullingParameters);
        }

        // すべてのスケジュールされたコマンドを行うよう、グラフィックス API に指示します。
        context.Submit();
    }
}

描画

描画とは、指定した設定で指定した 1 組のジオメトリを描画するようにグラフィックス API に指示するプロセスです。

SRP で描画するには、以下を行います。

  1. 上述のようにカリング操作を実行し、その結果を CullingResults 構造体に格納します。
  2. FilteringSettings 構造体を作成して設定します。この構造体には、カリングの結果をフィルタリングする方法が記述されています。
  3. DrawingSettings 構造体を作成して設定します。この構造体には、どのジオメトリをどのように描画するかが記述されています。
  4. 任意: デフォルトでは、Unity はシェーダーオブジェクトに基づいてレンダー状態を設定します。描画しようとしているジオメトリの一部または全部のレンダリング状態をオーバーライドしたい場合は、RenderStateBlock 構造体を使用します。
  5. ScriptableRenderContext.DrawRenderers を呼び出して、作成した構造体をパラメーターとして渡します。Unity は、設定に応じてフィルタリングされたジオメトリを描画します。

このサンプルコードでは、上の例を基に、レンダーターゲットの消去、カリング操作の実行、その結果のジオメトリの描画を行います。

/* 
これは、カスタムのスクリプタブルレンダーパイプラインの簡略化された例です。
これは、基本的なレンダーループがどのように機能するかを示しています。 最も効率的なランタイムパフォーマンスというよりは、最も明確なワークフローを示しています。
*/

using UnityEngine;
using UnityEngine.Rendering;

public class ExampleRenderPipeline : RenderPipeline {
    public ExampleRenderPipeline() {
    }

    protected override void Render (ScriptableRenderContext context, Camera[] cameras) {
        // 現在のレンダーターゲットを消去するコマンドを作成してスケジューリングします
        var cmd = new CommandBuffer();
        cmd.ClearRenderTarget(true, true, Color.black);
        context.ExecuteCommandBuffer(cmd);
        cmd.Release();

        // すべてのカメラに繰り返します
        foreach (Camera camera in cameras)
        {
            // 現在のカメラからカリングパラメーターを取得します
            camera.TryGetCullingParameters(out var cullingParameters);

            // カリングパラメーターを使用してカリング操作を実行し、結果を保存します
            var cullingResults = context.Cull(ref cullingParameters);

            // 現在のカメラに基づいて、ビルトインのシェーダー変数の値を更新します
            context.SetupCameraProperties(camera);

            // LightMode パスタグの値に基づいて、 Unity に描画するジオメトリを指示します
            ShaderTagId shaderTagId = new ShaderTagId("ExampleLightModeTag");

            //現在のカメラに基づいて、Unity にジオメトリを並べ替える方法を指示します
            var sortingSettings = new SortingSettings(camera);

            // どのジオメトリを描画するかとその描画方法を説明する DrawingSettings 構造体を作成します
            DrawingSettings drawingSettings = new DrawingSettings(shaderTagId, sortingSettings);

            // カリング結果をフィルタリングする方法を Unity に指示し、描画するジオメトリをさらに指定します
            // FilteringSettings.defaultValue を使用して、フィルタリングなしを指定します
            FilteringSettings filteringSettings = FilteringSettings.defaultValue;
        
            // 定義した設定に基づいて、ジオメトリを描画するコマンドをスケジューリングします
            context.DrawRenderers(cullingResults, ref drawingSettings, ref filteringSettings);

            // 必要に応じて、スカイボックスを描画するコマンドをスケジューリングします
            if (camera.clearFlags == CameraClearFlags.Skybox && RenderSettings.skybox != null)
            {
                context.DrawSkybox(camera);
            }

            // スケジュールされたすべてのコマンドを実行するようにグラフィックス API に指示します
            context.Submit();
        }
    }
}
カスタムレンダーパイプラインでのレンダーパイプラインアセットとレンダーパイプラインインスタンスの作成
カメラ