URP(2019.3)で自作のレンダーパスを追加する【unity】

 

個人的なメモをインターネットの片隅に添えさせていただきます。

万一、誤った点がありましたらご一報のほどよろしくお願いいたします。

 

サンプルリポジトリ:

github.com

 

引用リンクまとめ

ppsv2

URP

ppsv3

Scriptable(Custom)Renderer

参照

 

本文のまえに

従来のビルトインレンダリングパイプラインではOnRenderImageなどcameraのコールバック系を利用することで自作のポスプロ処理ができました。

が、URPではこのやり方はサポートされません。*1

 ので以前はppsv2にカスタムエフェクトを追加して自作のポスプロ処理をしたのです*2 

が、2019.3のURPではppsはppsv3となり、若干仕様が変わったため従来通りのやり方ではうまくいきません。*3 *4

ので自作のレンダーパスを追加することで自作のポスプロ処理をやっていきます。

概要

レンダーパス追加において主に必要になるのは、

  • Universal Render Pipeline Asset
  • Scriptable(Custom)Renderer
  • ScriptableRenderFeature
  • ScriptableRenderPass

あたりです。

Universal Render Pipeline Asset

URPAssetはURPのgraphicの機能やqualityの設定をコントロールするもので、

これをgraphics settingsで割り当てると、ビルトインからURPに切り替わります。

また複数のassetを用意して、それらを切り替えることもできますが、URP / HDRP / SRP間の切り替えはできません。*5*6

Scriptable(Custom)Renderer

ScriptableRendererはcullingやlightingなど一連のレンダリング機能を実装したもので、これをURPAssetのRendererListに割り当てると、CameraコンポーネントからRendererを選択することができます。*7

RendererのリソースはScriptableRendererDataでselializedされます*8

また、Rendererは後述のRenderFeatureを追加することで拡張できます。

2019.3URPでは(singlepass)ForwardRendererと、2DRenderer(まだexperimental)が付属されており(今後defferdにも対応していくみたい)、これらにRenderFeatureを追加するなどしてカスタムしていきます。

ScriptableRenderFeature

RenderFeatureはRendererに後述のRenderPassを特定のrenderevent(AfterRenderingOpaquesなど)に注入するためのリソースとロジックを含むscriptable objectで、これをRendererのRenderFeaturesListに追加することでRendererをカスタムします。

URPはGUIでfeatureを作成する汎用的なRenderObjects featureを備えていて、またScriptableRenderFeatureを継承したスクリプトを作成することでさらに自由度の高いcustomfeatureを追加することができます。*9 *10

ScriptableRenderPass

Rendererを拡張するための個々のレンダリングパスを実装したもの。他のrendereventのレンダリングパスもScriptableRenderPassによって実装されています。ここでCommandBuffer組み立て処理をします。*11 *12

実装

unity:  2019.3.0f6

最初のテンプレはビルトインを選択して、あとからpackagemanagerでURPをインストールします。*13

 

f:id:cakemas:20200212175229p:plain

f:id:cakemas:20200212175534p:plain

 

 まずURPAssetを作成します。

Create > Rendering > Universal Render Pipeline > Pipeline Asset

 f:id:cakemas:20200212180809p:plain

作成すると、URPAssetといっしょにRendererも生成され、URPAssetのRenderListに割り当てられていることがわかります。

f:id:cakemas:20200212213957p:plain

 

Rendererをカスタムするため、RendererFeatureを作成します。

ScriptableRenderFeatureを継承したスクリプトをつくって、rgbを反転させるポスプロ処理をやっていきます。

 

RenderFeature*14

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace Pipeline.Sample
{
    public class TestRenderFeature : ScriptableRendererFeature
    {
        [SerializeField] public bool invBool;
        private TestRenderPass _scriptablePass;

        public override void Create()
        {
            if(_scriptablePass == null)
                _scriptablePass = new TestRenderPass();

            _scriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingPostProcessing;
        }
        
        public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
        {
            _scriptablePass.SetUp(renderer.cameraColorTarget);
            if (invBool == false)
            {
                _scriptablePass._invBool = 0;
            }
            else
            {
                _scriptablePass._invBool = 1;
            }
            renderer.EnqueuePass(_scriptablePass);
        }
    }
}

 

RenderPass*15

using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;

namespace Pipeline.Sample
{
    public class TestRenderPass : ScriptableRenderPass
    {
        private const string Tag = nameof(TestRenderPass);
        private RenderTargetIdentifier _currentTarget;
        
        public int _invBool;

        public void SetUp(RenderTargetIdentifier target)
        {
            _currentTarget = target;
        }

        public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
        {
            var commandBuffer = CommandBufferPool.Get(Tag);
            var renderTextureId = Shader.PropertyToID("_SampleURPScriptableRenderer");
            var material = new Material(Shader.Find("pipeline/InvRgb"));
            var cameraData = renderingData.cameraData;
            var w = cameraData.camera.scaledPixelWidth;
            var h = cameraData.camera.scaledPixelHeight;
            
            material.SetInt("_invBool", _invBool);
            
            if (_invBool == 0)
            {
                material.EnableKeyword("_INVBOOL_OFF");
                material.DisableKeyword("_INVBOOL_ON");
            }
            else
            {
                material.DisableKeyword("_INVBOOL_OFF");
                material.EnableKeyword("_INVBOOL_ON");
            }
            
            commandBuffer.GetTemporaryRT(renderTextureId, w, h, 0, FilterMode.Point, RenderTextureFormat.Default);
            commandBuffer.Blit(_currentTarget, renderTextureId);
            commandBuffer.Blit(renderTextureId, _currentTarget, material);

            context.ExecuteCommandBuffer(commandBuffer);
            CommandBufferPool.Release(commandBuffer);
        }
    }
}

 

shader

Shader "pipeline/InvRgb"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        [KeywordEnum(Off, On)] _InvBool("InvBool", int) = 0
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            sampler2D _MainTex;
            
            #pragma shader_feature _INVBOOL_OFF _INVBOOL_ON

            fixed4 frag (v2f i) : SV_Target
            {
                fixed4 col = tex2D(_MainTex, i.uv);
                
                #ifdef _INVBOOL_ON
                    col.rgb = 1 - col.rgb;
                #else
                    col = col;
                #endif
                
                return col;
            }
            ENDCG
        }
    }
}

 

すると、RenderFeatureListにつくったfeatureが追加できるようになるので、追加します。

f:id:cakemas:20200212215646p:plain

最後に、Edit > Project Settings > Graphics のScriptableRenderPipelineAssetsにURPAssetsを割り当てます。

f:id:cakemas:20200213021627p:plain

 

以上で、(ppsの描画後に)rgbを反転させるレンダーパスを追加できました。

renderfeatureにシリアライズ変数として実装したbool値を変更することで確認できます。

f:id:cakemas:20200213015537p:plain

f:id:cakemas:20200212220414p:plain

 

 framedebugger

f:id:cakemas:20200213020500p:plain

 

Tips

  • create > Rendering > URP > Render feature でfeatrureのスクリプトテンプレがつくれる
  • ppsv3の公式のカスタムエフェクトの実装はまだないけど、volumecomponentを継承したクラスとrenderfeatureで実装できる(rendereventがポスプロまわりに制限されないppsのカスタムエフェクト?)*16

わからない

renderobjectsでcommandbuffer.blitっぽい処理をしたい、けどできない。わからない...

これから

renderfeature複数割り当てたりrenderer切り替えたりして実際に画づくりやっていきです!