こんにちは、Unityエンジニアのオオバです。

お悩みさん
お悩みさん
  • URPに独自パスを追加したい
  • URP拡張の基礎を知りたい
  • RenderGraph版のURP拡張方法の基礎を知りたい
  • オオバ
    オオバ
    本記事ではこれらの悩みを解決します。

    Unityの RenderGraph をご存知でしょうか?

    Unity6になってURP(Universal Render Pipeleine)がRenderGraphというシステムを導入し新しくなりました。その影響によりAPIが大きく変わり、 URPの拡張方法にも変更 が入りました。

    そこで本記事では RenderGraph版URPの拡張方法を紹介 します。今回は始めの一歩ということで最小コードで実装しています。この拡張方法を参考に、より複雑で自分のやりたい表現を実現してもらえればと思います。

    ちなみに本記事は過去に執筆した旧バージョンのURP拡張記事のリライト版という位置づけです。

    【超基本編】URPを拡張する方法(RenderGraph版)_0

    記事後半では具体的なURP拡張としてダウンサンプリングDEMOを作ってみるのでお楽しみに。

    では早速紹介していきます。

    ※本記事は広告を含みます

    👉DOTweenの教科書を読んでUnityアニメーションをプログラミングしてみよう!

    そもそもRenderGraphとは?

    具体的なURP拡張の前に RenderGraph について理解しておく必要があります。そもそもRenderGraphとはUnityが提供するレンダリングシステムの1つで以前から存在はしていました。

    URPのコアとなるCore RP Library v10.2からマニュアルに登場していましたが、URPに導入されてはいませんでした。

    Unity6以降のURPではRenderGraphを使用するようになったということです。

    【超基本編】URPを拡張する方法(RenderGraph版)_1

    RenderGraphはレンダリングパイプラインで使用するリソースを効率的に 自動管理 するシステムで、無駄なリソースを減らし 描画処理を最適化 することで パフォーマンス向上 を図っています。

    URP 17へのアップグレードとRenderGraphの活用方法より

    ↑RenderGraph初心者の方は一度こちらのU/Day動画を見ておくことをおすすめします。

    RenderGraph導入で何が変わる?

    RenderGraphによって描画のパフォーマンスが上がるのはわかったけど、旧システムから何が変わるのでしょうか?

    結論から言うと めちゃくちゃ変わります。

    上記でリストアップした変化があるわけですが、特にRenderTextureを自分で管理する必要がなくなります。これは非常に嬉しいですね。解放漏れなどを心配する必要がなくなるからです。

    今まで ExecuteOnCameraSetup といったScriptableRenderPassのオーバーライド関数が大量にありましたが、 RecordRenderGraph 関数一本に変更になりました。その他の関数はObsoleteとなり近い将来削除されます。

    つまりRenderGraph以前にゴリゴリURPの拡張をしていた機能は、ほぼ全て Unity6では動かなくなる ということです 😇

    ただしRenderGraphは 今後URPを使う上で外せない機能 なので、ぜひ本記事を通して基礎力を身に着けておきましょう。

    改めてURPの基本構成を整理

    URPは複数のファイルが登場するため混乱しやすいです。しかしURPを拡張する上でファイル同士の関係性を知っておくことは非常に重要です。

    そこで具体的なURP拡張をする前に、 URPの構成について整理 しておきましょう。これはURP初心者向けの内容です。

    RenderGraphに変わることでURPのAPIは大きく変わりますが、基本構成に変化はありません。図にするとこんなイメージです。

    【超基本編】URPを拡張する方法(RenderGraph版)_2

    登場人物は以下の4人です。

    UniversalRenderPipelineAsset

    UniversalRenderPipelineAsset が大元のScriptableObjectです。

    【超基本編】URPを拡張する方法(RenderGraph版)_3

    この UniversalRenderPipelineAssetProject Settings > Graphics > Default Render Pipeline にセットされます。

    Default Render PipelineUniversalRenderPipelineAsset がセットされることで URPが動作する ようになります。

    UniversalRendererData

    【超基本編】URPを拡張する方法(RenderGraph版)_4

    UniversalRenderPipelineAsset > RendererListUniversalRendererData が格納されます。変数名の通りRendererListは複数の UniversalRendererData を格納可能です。

    ScriptableRendererFeature

    【超基本編】URPを拡張する方法(RenderGraph版)_5

    UniversalRendererData の中に本題であるURPの拡張 ScriptableRendererFeature をセットします。ここでは拡張名として「DownSamplingRenderFeature」という名前になっています。

    ScriptableRenderPass

    ScriptableRendererFeature の中で実際の描画処理を記述した ScriptableRenderPass を生成します。

    【超基本編】URPを拡張する方法(RenderGraph版)_6

    繰り返しになりますが、 RenderGraphになったからといって基本的な構成に変化はありません。 (旧システムから呼び名は変わっているものもあります)

    URPのファイル構成を理解したうえで、ここから具体的なURPの拡張方法について解説していきます。

    RenderGraph版ダウンサンプリングするURP拡張を作ってみよう

    【超基本編】URPを拡張する方法(RenderGraph版)_8

    ダウンサンプリングを例にRenderGraph版のURP拡張方法を解説していきます。

    作業内容を細かく刻んだため全体では9ステップと多いですが、1つずつ確認していきましょう。

    RenderGraph版URP拡張9ステップ

    ①ScriptableRenderPassの作成

    ②Pass実行に必要なデータクラスの作成

    ③RecordRenderGraphのオーバーライド

    ④ダウンサンプリングするための準備とRenderTexture作成

    ⑤RenderTextureにカメラ画像の書き込み

    ⑥画面にRenderTextureを描画

    ⑦パス内の処理を作成

    ⑧ScriptableRendererFeatureの作成

    ⑨UniversalRendererDataにScriptableRendererFeatureの登録

    1.ScriptableRenderPassの作成

    まず最初にやるべきことはScriptableRenderPassの作成です。

    public class DownSamplingRenderPass : ScriptableRenderPass  
    {
    
    }
    

    ScriptableRenderPass クラスを継承してPassクラスを作成します。ここでは 「DownSamplingRenderPass」というクラス名にしています。

    2.Pass実行に必要なデータクラスの作成

    パスを実行(ダウンサンプリング処理)するために必要なパラメータを渡すためのクラス(PassData)を作成しておきます。

    public class DownSamplingRenderPass : ScriptableRenderPass  
    {
        // パスを実行(ダウンサンプリング処理)するために必要なパラメータを渡すためのクラス  
        private class PassData  
        {
            public TextureHandle srcTextureHandle;  
        }
    }
    

    PassDataクラスはパス内でしか利用しないためPrivateクラスでOKです。

    3.RecordRenderGraphのオーバーライド

    次に RecordRenderGraph関数をオーバーライド します。本関数内に描画処理のすべてを記述していきます。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)  
    {
        // パスの処理を記述るする  
    }
    

    旧URPでは複数存在していたオーバーライド関数ですが、RenderGraphになって RecordRenderGraph 関数だけとなりました。

    4.ダウンサンプリングするための準備とRenderTextureの作成

    今回作成するダウンサンプリングの例では、低解像度のRenderTextureにカメラ画像を書き込み、その後引き伸ばして画面に描画するという仕組みです。

    【超基本編】URPを拡張する方法(RenderGraph版)_9

    ↑図にするとこんな感じです。

    処理としては次の4ステップです。

    1. 現在カメラが映しているテクスチャの取得
    2. カメラデータの取得
    3. 低解像度の決定
    4. 解像度を下げた一時利用のRenderTextureを作成

    ↓コードにするとこんな感じです。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)  
    {
        // 1.現在カメラが映しているテクスチャの取得  
        var resourceData = frameData.Get<UniversalResourceData>();  
        var cameraColorTextureHandle = resourceData.activeColorTexture;  
    
        // 2.カメラデータの取得  
        UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();  
    
        // 3.低解像度の決定  
        int downSample = 100;  
        int w = cameraData.scaledWidth / downSample;  
        int h = cameraData.scaledHeight / downSample;  
    
        // 4.像度を下げた一時利用のRenderTextureの作成  
        var tempRT = UniversalRenderer.CreateRenderGraphTexture(  
            renderGraph,  
            new RenderTextureDescriptor(w, h), "_TempRT", true);  
    }
    

    該当する処理にコメントアウトをしているため確認してみてください。

    5.RenderTextureにカメラ画像の書き込み

    次にやるべきことは一時的に作成した低解像度RenderTextureへの書き込みです。

    1. RenderGraphに対してパスを追加
    2. カメラのテクスチャは読み取り設定
    3. 一時的なRenderTextureは書き込み設定
    4. Pass実行に必要なデータをセット
    5. パス実行の依頼

    ↓コードに落とし込むとこんな感じです。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)  
    {
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
    
        // 1. RenderGraphに対してパスを追加  
        using (var builder = renderGraph.AddRasterRenderPass(passName, out PassData passData))  
        {
            // 2.カメラのテクスチャは読み取り設定  
            builder.UseTexture(cameraColorTextureHandle, AccessFlags.Read);  
    
            // 3.一時的なRenderTextureは書き込み設定  
            builder.SetRenderAttachment(tempRT, 0, AccessFlags.Write);  
    
            // 4.Pass実行に必要なデータをセット  
            passData.srcTextureHandle = cameraColorTextureHandle;  
    
            // 5.パス実行の依頼  
            builder.SetRenderFunc(  
                (PassData data, RasterGraphContext context) => ExecutePass(data, context)  
            );  
        }
    }
    

    この状態ではまだ低解像度のRenderTextureにカメラ画像を書き出しただけで画面内に表示されていません。
    ExecutePass メソッドは後で紹介します

    6.画面にRenderTextureを描画

    低解像度RenderTextureに書き込んだ画像を画面内に映し出す処理を追加します。

    1. RenderGraphに対してパスを追加
    2. 一時的なRenderTextureは読み取り設定
    3. カメラのテクスチャは書き込み設定
    4. Pass実行に必要なデータをセット
    5. パス実行の依頼

    コードにするとこんな感じです。

    public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)  
    {
        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  
    
        // 1. RenderGraphに対してパスを追加  
        using (var builder = renderGraph.AddRasterRenderPass(passName, out PassData passData))  
        {
            // 2.一時的なRenderTextureは読み取り設定  
            builder.UseTexture(tempRT, AccessFlags.Read);  
    
            // 3.カメラのテクスチャは書き込み設定  
            builder.SetRenderAttachment(cameraColorTextureHandle, 0, AccessFlags.Write);  
    
            // 4.Pass実行に必要なデータをセット  
            passData.srcTextureHandle = tempRT;  
    
            // 5.パス実行の依頼  
            builder.SetRenderFunc(  
                (PassData data, RasterGraphContext context) => ExecutePass(data, context)  
            );  
        }
    }
    

    7.パス内の処理を作成

    最後にパス内の処理(ExecutePassメソッド)を実装します。パス内の処理はソース画像を書き込み対象にコピー(Blit)するだけです。次のコードで実現できます。

    private static void ExecutePass(PassData passData, RasterGraphContext graphContext)  
    {
        RasterCommandBuffer cmd = graphContext.cmd;  
        Blitter.BlitTexture(cmd, passData.srcTextureHandle, new Vector4(1, 1, 0, 0), 0, false);  
    }
    

    以上でScriptableRenderPassの実装は完了です。

    8.ScriptableRendererFeatureの作成

    最後に作成したパスをRendererにわたすためのScriptableRendererFeatureを作成します。行数が少ないので一気にScriptableRendererFeatureのコードを一気に紹介します。

    using UnityEngine.Rendering.Universal;  
    
    public class DownSamplingRenderFeature : ScriptableRendererFeature  
    {
        DownSamplingRenderPass _renderPass;  
    
        public override void Create()  
        {
            // パスの作成  
            _renderPass = new DownSamplingRenderPass  
            {
                // 処理のタイミング(透明オブジェクト後に実行)  
                renderPassEvent = RenderPassEvent.AfterRenderingTransparents  
            };  
        }
    
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)  
        {
            // パスの登録  
            renderer.EnqueuePass(_renderPass);  
        }
    }
    

    9.UniversalRendererDataにScriptableRendererFeatureの登録

    最後にUniversalRendererDataへ作成したScriptableRendererFeatureを登録して完了です。

    【超基本編】URPを拡張する方法(RenderGraph版)_10

    Add Renderer Feature ボタンをクリックして、作成したScriptableRendererFeatureを追加しましょう。

    【超基本編】URPを拡張する方法(RenderGraph版)_11

    このように今回作成した DownSamplingRenderFeature を追加します。

    【超基本編】URPを拡張する方法(RenderGraph版)_12

    追加した瞬間にモザイク状態になったかと思います。成功です。

    このような手順でRenderGraph版のURPを拡張してもらえればと思います。

    個人的にはRenderTextureを自前で管理しなくてよいのが非常に嬉しいです。慣れたらRenderGraph版のURP拡張の方がしやすい印象でした。

    まとめ

    今回は RenderGraph版のURP拡張超基本編 ということでダウンサンプリングを作ってみました。最後に記事の内容を簡単にまとめます。

    RenderGraph版URP拡張まとめ

    ①RenderGraphになってAPIは大幅変更

    ②無駄なデータが削減されてパフォーマンスアップ

    ③自前でRenderTextureの管理が不要で作りやすい

    ④オーバーライド関数はRecordRenderGraphのみ

    ⑤慣れるとRenderGraph版の方が拡張しやすそう

    こんな感じです。

    Unity6になってURPがRenderGraphに対応しました。旧来の書き方とは大きく変わってしまいましたが、基本構成は同じです。

    URPはRenderGraphがベースになるためこのタイミングでぜひ覚えておいた方がよさそうですね。本記事はある程度URPのことを知っている人向けに執筆しちゃっているため、いきなりすべてを理解するのは難しいかもしれません。

    何度も読んで、自分で手を動かして試してみてもらえたらと思います。

    全体コードはGistにアップロードしました。

    【超基本編】URPを拡張する方法(RenderGraph版)_13

    参考になった方はぜひ右上のスターボタンを押しておいてください⭐️
    ダウンロードはこちら

    「Unity初心者大学」というUnity初心者向けのYouTube始めました!!
    ぜひチャンネル登録をお願いします!

    最後まで読んでいただきありがとうございました!
    すばらしいURP拡張ライフをお過ごしください。

    オススメ記事
    検証環境
    参考サイト