Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Question (URP 13.1.8) Proper RTHandle usage in a Renderer Feature

Discussion in 'Universal Render Pipeline' started by Eno-Khaon, Sep 27, 2022.

  1. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    Unity 2022.1.14f1
    Universal Rendering Pipeline 13.1.8

    In my experiments and testing to try and introduce myself to ScriptableRendererFeatures (and, by extension, ScriptableRenderPasses), I've failed to find a single, up-to-date example online to use as a starting point and am clearly on the wrong track to some extent, since the first successful modification to my current render consumed more and more memory in RenderTextures until my third out-of-VRAM-induced computer restart.

    So far, my best first attempt combined the primary concepts from https://alexanderameye.github.io/notes/scriptable-render-passes/ with the URP's "Upgrade Guide" offering bare minimum suggestions for updating to RTHandle use. Research has also spanned more than a dozen other sources (most of which were lost to the aforementioned computer restarts, but all of which involved earlier versions of URP, prior to non-RTHandles being deprecated), but those two have actually resulted in something that's offered a visual result.

    With all of that in mind, this is the current state of my script(s) (with most non-deprecation-related comments removed):
    --TemplateFeature.cs--
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering.Universal;
    3.  
    4. // https://alexanderameye.github.io/notes/scriptable-render-passes/
    5. public class TemplateFeature : ScriptableRendererFeature
    6. {
    7.     [System.Serializable]
    8.     public class PassSettings
    9.     {
    10.         public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    11.  
    12.         [Range(1, 4)]
    13.         public int downsample = 1;
    14.  
    15.         [Range(0, 20)]
    16.         public int blurStrength = 5;
    17.     }
    18.  
    19.     TemplatePass pass;
    20.     public PassSettings passSettings = new PassSettings();
    21.  
    22.     public override void Create()
    23.     {
    24.         pass = new TemplatePass(passSettings);
    25.     }
    26.  
    27.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    28.     {
    29.         renderer.EnqueuePass(pass);
    30.     }
    31. }
    --TemplatePass.cs--
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. // https://alexanderameye.github.io/notes/scriptable-render-passes/
    6. public class TemplatePass : ScriptableRenderPass
    7. {
    8.     const string profilerTag = "Template Pass";
    9.  
    10.     TemplateFeature.PassSettings passSettings;
    11.  
    12.     //^RenderTargetIdentifier colorBuffer;
    13.     //^RenderTargetIdentifier temporaryBuffer;
    14.     // ^Updating deprecated RenderTargetIdentifier usage to RTHandle-based
    15.     RTHandle colorBuffer;
    16.     RTHandle temporaryBuffer;
    17.     //^int temporaryBufferID = Shader.PropertyToID("_TemporaryBuffer");
    18.  
    19.     Material mat;
    20.  
    21.     static readonly int blurStrengthProperty = Shader.PropertyToID("_BlurStrength");
    22.  
    23.     public TemplatePass(TemplateFeature.PassSettings passSettings)
    24.     {
    25.         this.passSettings = passSettings;
    26.  
    27.         renderPassEvent = passSettings.renderPassEvent;
    28.  
    29.         if(mat == null)
    30.         {
    31.             mat = CoreUtils.CreateEngineMaterial("Hidden/TemplateBlur");
    32.         }
    33.  
    34.         mat.SetInt(blurStrengthProperty, passSettings.blurStrength);
    35.     }
    36.  
    37.     public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    38.     {
    39.         RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
    40.  
    41.         // Downsample the original camera target descriptor
    42.         descriptor.width /= passSettings.downsample;
    43.         descriptor.height /= passSettings.downsample;
    44.  
    45.         descriptor.depthBufferBits = 0; // Color and depth cannot be combined in RTHandles
    46.  
    47.         // Grab the color buffer from the renderer camera color target
    48.         //^colorBuffer = renderingData.cameraData.renderer.cameraColorTarget;
    49.         colorBuffer = renderingData.cameraData.renderer.cameraColorTargetHandle;
    50.  
    51.         //^cmd.GetTemporaryRT(temporaryBufferID, descriptor, FilterMode.Bilinear);
    52.         //^temporaryBuffer = new RenderTargetIdentifier(temporaryBufferID);
    53.         // This included variations on descriptor definitions and scaling definitions
    54.         RenderingUtils.ReAllocateIfNeeded(ref temporaryBuffer, Vector2.one / passSettings.downsample, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_TemporaryBuffer");
    55.     }
    56.  
    57.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    58.     {
    59.         // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
    60.         // Currently there's an issue which results in mismatched markers.
    61.         CommandBuffer cmd = CommandBufferPool.Get();
    62.         using(new ProfilingScope(cmd, new ProfilingSampler(profilerTag)))
    63.         {
    64.             Blit(cmd, colorBuffer, temporaryBuffer, mat, 0); // shader pass 0
    65.             Blit(cmd, temporaryBuffer, colorBuffer, mat, 1); // shader pass 1
    66.         }
    67.  
    68.         context.ExecuteCommandBuffer(cmd);
    69.         CommandBufferPool.Release(cmd);
    70.     }
    71.  
    72.     public override void OnCameraCleanup(CommandBuffer cmd)
    73.     {
    74.         if(cmd == null)
    75.         {
    76.             throw new System.ArgumentNullException("cmd");
    77.         }
    78.  
    79.         //^cmd.ReleaseTemporaryRT(temporaryBufferID);
    80.         temporaryBuffer = null;
    81.         //colorBuffer = null; // I don't know whether it's necessary, but either way doesn't help
    82.     }
    83. }
    ... And the shader, although it's not specifically the cause of any problems here
    --TemplateBlur.shader--
    Code (CSharp):
    1. Shader "Hidden/TemplateBlur"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.     }
    7.         HLSLINCLUDE
    8.  
    9.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    10.  
    11.         struct Attributes
    12.         {
    13.             float4 positionOS : POSITION;
    14.             float2 uv : TEXCOORD0;
    15.         };
    16.  
    17.         struct Varyings
    18.         {
    19.             float4 positionHCS : SV_POSITION;
    20.             float2 uv : TEXCOORD0;
    21.         };
    22.  
    23.         TEXTURE2D(_MainTex);
    24.         SAMPLER(sampler_MainTex);
    25.         float4 _MainTex_TexelSize;
    26.         float4 _MainTex_ST;
    27.  
    28.         int _BlurStrength;
    29.  
    30.         Varyings Vert(Attributes input)
    31.         {
    32.             Varyings output;
    33.             output.positionHCS = TransformObjectToHClip(input.positionOS.xyz);
    34.             output.uv = TRANSFORM_TEX(input.uv, _MainTex);
    35.             return output;
    36.         }
    37.  
    38.         half4 FragHorizontal(Varyings input) : SV_TARGET
    39.         {
    40.             float2 res = _MainTex_TexelSize.xy;
    41.             half4 sum = 0;
    42.  
    43.             int samples = 2 * _BlurStrength + 1;
    44.  
    45.             for(float x = 0; x < samples; x++)
    46.             {
    47.                 float2 offset = float2(x - _BlurStrength, 0);
    48.                 sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv + offset * res);
    49.             }
    50.             return sum / samples;
    51.         }
    52.  
    53.         half4 FragVertical(Varyings input) : SV_TARGET
    54.         {
    55.             float2 res = _MainTex_TexelSize.xy;
    56.             half4 sum = 0;
    57.  
    58.             int samples = 2 * _BlurStrength + 1;
    59.  
    60.             for(float y = 0; y < samples; y++)
    61.             {
    62.                 float2 offset = float2(0, y - _BlurStrength);
    63.                 sum += SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv + offset * res);
    64.             }
    65.             return sum / samples;
    66.         }
    67.  
    68.         ENDHLSL
    69.  
    70.    
    71.     SubShader
    72.     {
    73.         Tags
    74.         {
    75.             "RenderType"="Opaque"
    76.             "RenderPipeline"="UniversalPipeline"
    77.         }
    78.  
    79.         Pass // 0
    80.         {
    81.             Name "Horizontal Box Blur"
    82.  
    83.             HLSLPROGRAM
    84.  
    85.             #pragma vertex Vert
    86.             #pragma fragment FragHorizontal
    87.  
    88.             ENDHLSL
    89.         }
    90.    
    91.         Pass // 1
    92.         {
    93.             Name "Vertical Box Blur"
    94.  
    95.             HLSLPROGRAM
    96.  
    97.             #pragma vertex Vert
    98.             #pragma fragment FragVertical
    99.  
    100.             ENDHLSL
    101.         }
    102.     }
    103. }
    To say this again now, DON'T USE THIS AS-IS. You will regret it quickly.

    Anyway, with all of this in mind, what exactly have I overlooked at this point? I have been completely unable to find a source for a functioning example of a ScriptableRendererFeature (as of changes made over the course of URP 13 for the current non-beta Unity Editor build), and without prior experience using them, I'm at a loss regarding what I'm missing in terms of assembling this.

    For that matter, is there anything else I should also be taking into consideration when working with this to begin with? For example, is ScriptableRenderContext.DrawRenderers() a viable way to draw a specific subset of GameObjects in my scene (i.e. using LayerMask) with a Renderer Feature, rather than only using the current frame (at a given time)?

    -----------------------------

    Edit: Where KeepFrameFeature.cs in the URP "Samples" package contains something like a dozen lines reporting as "deprecated", Test107Renderer in Unity's URP Github "Graphics" branch, while not a ScriptableRendererFeature, looks like it might get me on the right track... despite not seeming to strictly adhere to the Upgrade Guide. I can't say I'm enthusiastic about the risk of crashing my computer again, though, so I'll hold off on testing it while I focus on other things (unless there are any better suggestions offered here by the time I get to trying that approach - namely, incorporating CommandBuffer.GetTemporaryRT() and CommandBuffer.ReleaseTemporaryRT() when they're specifically filtered out in the Upgrade Guide itself).
     
    Last edited: Sep 28, 2022
    illustor, Lechuza, mitaywalle and 4 others like this.
  2. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    Update: I still have not found a clear approach to take yet. I've been digging around Unity's "Graphics" GitHub repository (example search, effectively no results) and have been unable to find an example of a ScriptableRendererFeature-based ScriptableRenderPass that makes use of OnCameraCleanup() (as opposed to an inherited call from ScriptableRendererFeature.Dispose()) and allocates/clears RTHandle(s) in the process.

    Since it seems like that should be an expected/intended use case, based on the Upgrade Guide (which includes an example of "Dispose()" without context for calling it and a variable "m_Handle" with unclear purpose), I'm still unclear on what is expected for a properly-formed, up-to-date script here.

    Am I supposed to pass down a call to a general function (i.e. "Dispose()") from ScriptableRendererFeature.Dispose() to clear RTHandles that are allocated within the ScriptableRenderPass (example: ScreenCoordOverrideRenderPass.Cleanup() is called from ScreenCoordOverrideScriptableRenderFeature.Dispose()), while RTHandles inherited from already-existing data (e.g. RenderingData.cameraData.renderer.cameraDepthTargetHandle) should be detached in OnCameraCleanup()? If that is the case, however, is OnCameraCleanup() necessary at all in most cases? There are very few examples which aren't empty functions or out of date (prior to RTHandle usage), and they're generally just part of the main rendering pipeline (is that even significant at all?).

    I guess in writing this post, it DOES seem like OnCameraCleanup() is barely used, though, so I guess that will be my next avenue for working through this some more. To note, however, since it's part of the template for a ScriptableRendererFeature file (with self-contained ScriptableRenderPass), and it includes the pre-written comment of "// Cleanup any allocated resources that were created during the execution of this render pass.", that doesn't lend itself to the expectation that you (potentially) WOULDN'T actually clean up allocated RTHandles there, and would instead do so with a call handed down from ScriptableRendererFeature.Dispose().
     
  3. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Something that's strange in that documentation. On this page: https://docs.unity3d.com/Packages/c...iversal@13.1/manual/upgrade-guide-2022-1.html , it says:

    If the target is known to not change within the lifetime of the application, then simply a RTHandles.Alloc would suffice and it will be more efficient due to not doing a check on each frame.​

    However, the example uses the less efficient
    RenderingUtils.ReAllocateIfNeeded(ref m_Handle, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_CustomPassHandle");
     
  4. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    Well, I finally went and got things sorted out, but there's still one thing that has me confused, which I haven't been able to locate any clear/definitive information on:

    When does RenderingUtils.ReAllocateIfNeeded() actually decide to do so? Or, alternatively, does it actually make a difference if the texture is larger than necessary?

    When I increase the window size of the editor/game view, the temporary texture's resolution increases to match. When I decrease the window size, the texture inherited from RenderingData.cameraData.renderer.cameraColorTargetHandle continues to match the current resolution, but the texture created from RenderingUtils.ReAllocatedIfNeeded() retains its maximum dimensions (as described in the Frame Debugger).

    Additionally, there seem to be no visible changes to the image overall, despite the shader utilizing pixel-size-driven modifiers (namely, _MainTex_TexelSize for pixel offsets), so it seems like accommodations are being made behind the scenes, but it's also confusing at the same time, since the main rendering process doesn't claim to retain higher-resolution-than-necessary textures under the same circumstances. It seems at least a bit silly that it wouldn't offer a clearer means of reducing the texture's resolution for circumstances like a low-capability device that just dropped render resolution for performance. Why should it be necessary to restart the game to optimize performance just for a window size change?

    At any rate, now that I have a much better grasp on how this needed to be organized, here's an updated state for the original "TemplateFeature" and "TemplatePass" files, for reference (the shader needed no modification):

    --TemplateFeature.cs--
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. // https://alexanderameye.github.io/notes/scriptable-render-passes/
    6. public class TemplateFeature : ScriptableRendererFeature
    7. {
    8.     [System.Serializable]
    9.     public class PassSettings
    10.     {
    11.         public Material material;
    12.  
    13.         public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    14.  
    15.         [Range(1, 4)]
    16.         public int downsample = 1;
    17.  
    18.         [Range(0, 20)]
    19.         public int blurStrength = 5;
    20.     }
    21.  
    22.     TemplatePass pass;
    23.     public PassSettings passSettings = new PassSettings();
    24.  
    25.     // This prevents attempted destruction of a manually-assigned material later
    26.     bool useDynamicTexture = false;
    27.  
    28.     public override void Create()
    29.     {
    30.         if(passSettings.material == null)
    31.         {
    32.             passSettings.material = CoreUtils.CreateEngineMaterial("Hidden/TemplateBlur");
    33.             useDynamicTexture = true;
    34.         }
    35.         pass = new TemplatePass(passSettings);
    36.     }
    37.  
    38.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    39.     {
    40.         renderer.EnqueuePass(pass);
    41.     }
    42.  
    43.     protected override void Dispose(bool disposing)
    44.     {
    45.         if(useDynamicTexture)
    46.         {
    47.             // Added this line to match convention for cleaning up materials
    48.             // ... But only for a dynamically-generated material
    49.             CoreUtils.Destroy(passSettings.material);
    50.         }
    51.         pass.Dispose();
    52.     }
    53. }
    54.  
    --TemplatePass.cs--
    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. // https://alexanderameye.github.io/notes/scriptable-render-passes/
    6. public class TemplatePass : ScriptableRenderPass
    7. {
    8.     const string profilerTag = "Template Pass";
    9.  
    10.     TemplateFeature.PassSettings passSettings;
    11.  
    12.     RTHandle colorBuffer;
    13.     RTHandle temporaryBuffer;
    14.  
    15.     Material mat;
    16.  
    17.     static readonly int blurStrengthProperty = Shader.PropertyToID("_BlurStrength");
    18.  
    19.     public TemplatePass(TemplateFeature.PassSettings passSettings)
    20.     {
    21.         this.passSettings = passSettings;
    22.         renderPassEvent = passSettings.renderPassEvent;
    23.  
    24.         // Now that this is verified within the Renderer Feature, it's already "trusted" here
    25.         mat = passSettings.material;
    26.  
    27.         mat.SetInt(blurStrengthProperty, passSettings.blurStrength);
    28.     }
    29.  
    30.     public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    31.     {
    32.         RenderTextureDescriptor descriptor = renderingData.cameraData.cameraTargetDescriptor;
    33.  
    34.         descriptor.width /= passSettings.downsample;
    35.         descriptor.height /= passSettings.downsample;
    36.  
    37.         descriptor.depthBufferBits = 0; // Color and depth cannot be combined in RTHandles
    38.  
    39.         // Enable these if your pass requires access to the CameraDepthTexture or the CameraNormalsTexture
    40.         // ConfigureInput(ScriptableRenderPassInput.Depth);
    41.         // ConfigureInput(ScriptableRenderPassInput.Normal);
    42.  
    43.         colorBuffer = renderingData.cameraData.renderer.cameraColorTargetHandle;
    44.  
    45.         RenderingUtils.ReAllocateIfNeeded(ref temporaryBuffer, Vector2.one / passSettings.downsample, descriptor, FilterMode.Bilinear, TextureWrapMode.Clamp, name: "_TemporaryBuffer");
    46.     }
    47.  
    48.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    49.     {
    50.         // A reasonably common and simple safety net
    51.         if(mat == null)
    52.         {
    53.             return;
    54.         }
    55.  
    56.         // NOTE: Do NOT mix ProfilingScope with named CommandBuffers i.e. CommandBufferPool.Get("name").
    57.         // Currently there's an issue which results in mismatched markers.
    58.         CommandBuffer cmd = CommandBufferPool.Get();
    59.         using(new ProfilingScope(cmd, new ProfilingSampler(profilerTag)))
    60.         {
    61.             Blit(cmd, colorBuffer, temporaryBuffer, mat, 0); // shader pass 0
    62.             Blit(cmd, temporaryBuffer, colorBuffer, mat, 1); // shader pass 1
    63.         }
    64.  
    65.         // Execute the command buffer and release it
    66.         context.ExecuteCommandBuffer(cmd);
    67.         CommandBufferPool.Release(cmd);
    68.     }
    69.  
    70.     public override void OnCameraCleanup(CommandBuffer cmd)
    71.     {
    72.         if(cmd == null)
    73.         {
    74.             throw new System.ArgumentNullException("cmd");
    75.         }
    76.  
    77.         // Mentioned in the "Upgrade Guide" but pretty much only seen in "official" examples
    78.         // in "DepthNormalOnlyPass"
    79.         // https://github.com/Unity-Technologies/Graphics/blob/9ff23b60470c39020d8d474547bc0e01dde1d9e1/Packages/com.unity.render-pipelines.universal/Runtime/Passes/DepthNormalOnlyPass.cs
    80.         colorBuffer = null;
    81.     }
    82.  
    83.     public void Dispose()
    84.     {
    85.         // This seems vitally important, so why isn't it more prominently stated how it's intended to be used?
    86.         temporaryBuffer?.Release();
    87.     }
    88. }
    89.  
    (No, this version won't cripple your computer)
     
    wwWwwwW1 likes this.
  5. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    As an additional frame of reference, with relation to burningmime's comments regarding RenderingUtils.ReAllocateIfNeeded(), there's an example of an on-demand usage of RTHandles.Alloc() in FinalBlitPass:

    Code (CSharp):
    1. if (m_CameraTargetHandle != cameraTarget)
    2. {
    3.     m_CameraTargetHandle?.Release();
    4.     m_CameraTargetHandle = RTHandles.Alloc(cameraTarget);
    5. }
    This could easily be extrapolated to be regenerated whenever the window size/screen resolution has changed, but I haven't yet located any frame of reference for doing so efficiently/sanely (through an event/message). It *could* be done by checking the width/height of the camera's (color) target texture and comparing with last-known values, but that seems wasteful to constantly check those values as part of the rendering loop, especially because that would mean checking against values which already properly adapted themselves.
     
  6. jjxtra

    jjxtra

    Joined:
    Aug 30, 2013
    Posts:
    1,467
    URP 13 is quite broken on Unity 2022, I have found it much more stable to stay on URP 12 and Unity 2021.
     
  7. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    All things considered, I don't think I'd necessarily describe it as "broken" or "(un)stable". It may not have been an especially sound idea for me to try and introduce myself to ScriptableRendererFeatures on this latest version (granted, I didn't know what I was getting myself into until I was already in the thick of it), but after what I've dug up, it seems to be more a problem of documentation/clarification than one of functionality.

    For one, that's why I made sure to include a template for a common, yet somewhat multi-faceted effect (blur), using a combination of base Render Texture (screen/camera color) and a temporary/surrogate one. If it's enough to give anyone *some* idea of how to get started who comes across this thread, then that would already make it a clearer resource than a majority of what I looked up just to ensure I set it up correctly as a newcomer to this system myself.

    For all my ranting up to this point in this thread, I've really been aiming more toward expressing the thought process that's gone into this as someone starting into this because the Built-in Renderer was giving me trouble with a logistical nightmare of a rendering concept and the High-Definition Rendering Pipeline doesn't lend itself to modification well enough to suit my current goal(s). (Namely, a combination of Multiple Render Target shader output, coupled with refraction, coupled with blurring, coupled with customizable lighting and/or shadow processing per GameObject during deferred rendering... the only part I still haven't found a suitable solution to in combination with all of this is also factoring in thickness (back face minus front face depth of same mesh added together per pixel for all meshes on the same layer), but I am willing to settle for avoiding yet another rendering pass on a layer just to make that work)

    As I've mentioned before, however, there are still aspects that seem... unclear (namely, texture resolution and usage attributes from ReAllocateIfNeeded()), but my testing since wrapping my head around parts of this system as a whole doesn't suggest that the URP has become dysfunctional with the most recent (non-beta) iteration.
     
    ChristopherKerr likes this.
  8. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    297
    I had to do a similar upgrade recently. Here's documenting what I had to do in the hope that it's useful (like you I'm not sure if this is really the intended way - it's extremely hard to find functional examples and the API changes so frequently that as soon as you do they are often out of date).

    I've trimmed out duplication for this minimal example:

    Defining render target handles in my Pass:

    Code (CSharp):
    1. #if URP_13
    2.         private RTHandle _PixelizationMap;
    3.         ...
    4. #else
    5.         private int _PixelizationMap;
    6.         ...
    7. #endif
    Allocating render target resources in override void Configure:

    Code (CSharp):
    1. #if URP_13
    2.             RenderingUtils.ReAllocateIfNeeded(ref _PixelizationMap, pixelizationMapDescriptor, name: "ProP_PixelizationMap");
    3. #else
    4.             _PixelizationMap = Shader.PropertyToID("_PixelizationMap");
    5.             cmd.GetTemporaryRT(_PixelizationMap, pixelizationMapDescriptor);
    6. #endif
    Disposing of render target handles:

    Code (CSharp):
    1. #if URP_13
    2.         public void Dispose()
    3.         {
    4.             _PixelizationMap?.Release();
    5.         }
    6. #endif
    7.  
    8.         public override void FrameCleanup(CommandBuffer cmd)
    9.         {
    10. #if URP_13
    11.  
    12. #else
    13.             cmd.ReleaseTemporaryRT(_PixelizationMap);
    14. #endif
    15.         }
    These resources were helpful references for me:
    https://forum.unity.com/threads/ren...lete-deprecated-in-favor-of-rthandle.1211052/

    https://github.com/Unity-Technologi...ion~/rthandle-system-using.md#using-rthandles
     
    lajala, acnestis and ChristopherKerr like this.
  9. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    Ah, whoops. I completely forgot to ensure that I was using an up-to-date variant of Blit(). Rather than ScriptableRenderPass.Blit(), which in turn calls CommandBuffer.Blit(), it looks like I should be making use of Blitter.BlitCameraTexture() for equivalent behavior.

    On that note, however, it's a shame that there isn't better/clearer information provided on what some of the other Blitter functions are doing. For example, Blitter.BlitTexture() appears to just be Blitter.BlitCameraTexture() without calling SetRenderTarget() first (namely, BlitCameraTexture() calls SetRenderTarget() just before calling BlitTexture() itself). The description of BlitTexture(), however, is "Blit a RTHandle texture.".

    Having tried adjustments, however, simply replacing the Blit() calls with Blitter.BlitCameraTexture() (reusing the same arguments) in my script posted in this thread resulted in no blur occurring anymore, so I'll try digging around to see what I'm missing.

    Edit: Specifically, the first Blit resulted in a solid black image and the second Blit basically came back with the original, unblurred image, so clearly many things weren't assigned *just* right.

    Edit 2: Oh, wait... the overload of ScriptableRenderPass.Blit() I was using literally calls Blitter.BlitCameraTexture() itself. The material isn't reporting as null, so it's not using the other branch there... so how is (B.)BlitCameraTexture() failing when (SRP.)Blit() is succeeding!?

    Edit 3: Ugh, GitHub decided to revert to the newest version of URP, rather than sticking with the approximately-version-relevant entry I'd previously looked at. I don't know exactly which "2022.1" time frame it might've been (and not simply listing the GitHub versioning based on URP version certainly hinders apparent relevancy), but the behavior of ScriptableRenderPass.Blit() changed significantly since then, in that it calls CommandBuffer.Blit() itself in that version.
     
    Last edited: Oct 9, 2022
    ChristopherKerr likes this.
  10. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    645
    I'm trying to update some RendererFeature code to 2022.1, using the RTHandles and the new Blitter. Initially I'm converting a custom postprocessed fog pass.

    I've got the basics functioning, after figuring out the changes needed to custom blit shaders to get UVs/position and the renamed source texture.

    But I'm doing a blit from the color target to a temporary texture, with the standard blit shader and the simplest Blit function, and the UVs seem to be wrong, particularly after resizing the scene/game window. I'm just doing:

    Blitter.BlitCameraTexture( cmd, renderingData.cameraData.renderer.cameraColorTargetHandle, m_tempTexture );

    Both textures are the same dimensions, it should be a 1:1 blit. But I'm getting weird UVs that seem to come from the _BlitScaleBias shader parameter. Any idea what's going on there?

    Edit: Solved? - It looks like BlitCameraTexture isn't setting _BlitScaleBias - so it's essentially undefined. I can do the blits successfully with CoreUtils.SetRenderTarget and Blitter.BlitTexture instead.

    blit.png
     
    Last edited: Nov 16, 2022
    ChristopherKerr likes this.
  11. Dustyroom

    Dustyroom

    Joined:
    Sep 23, 2015
    Posts:
    108
    I agree, there needs to be a clear example. All official sample Renderer Feature that use any custom texture currently do not work in URP 14.
    We shouldn't need to reverse engineer this.
     
  12. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    This is all going to change again in 2023 when render graph support arrives. If you have the luxury to do so, I'd recommend skipping the 2022 cycle for URP. Anything you learn now will be obsolete when you're using TextureHandle instead of RTHandle.
     
    daneobyrd likes this.
  13. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    297
    Thanks for the heads up, it would be really useful if someone from Unity who is familiar with plans for 2023 could weigh in on this comment
     
  14. bluescrn

    bluescrn

    Joined:
    Feb 25, 2013
    Posts:
    645
    Urgh. Will the breaking changes to URP ever slow down?

    Custom RendererFeatures seemed great when I started using them, but they've turned into a real maintenance liability. Even worse if you're relying on store assets where updates aren't guaranteed.

    I'd just started to try and move a project from 2021.2 to 2022.2, to move to RTHandles and the new blitter, although I've given up on that for now due to far-too-frequent editor crashes in 2022.2.

    The upcoming render graph stuff sounds worrying if it really limits SetRenderTarget as much as has been suggested. I've been dealing with an object-outline-rendering system that blits back and forth between several render targets, and bloom/blur type effects need to do similar.
     
    Last edited: Jan 4, 2023
    Dustyroom likes this.
  15. ElliotB

    ElliotB

    Joined:
    Aug 11, 2013
    Posts:
    297
    Just fyi, I haven't had any editor crashes since disabling the SRPBatcher - may be worth a go.
     
  16. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    Anyone got this working in 2022.2? all of this API change are confused me, and they didn't give a proper sample on how to use it.
    My first attempt just happen to also try converting the template feature to 2022.2. . .
     
    equal and noio like this.
  17. Lechuza

    Lechuza

    Joined:
    Oct 3, 2014
    Posts:
    23
    Same here, what a mess
     
  18. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    this is technically incorrect. RenderGraph uses TextureHandles, yes, which are just an RTHandle wrapper.
    One of the main reason why RTHandles were adopted in URP was that it was necessary preparation work in order to be able to use RenderGraph.
    It's all part of a long term roadmap, we don't add new features to deprecate them the version after.

    The main benefit of RenderGraph is that you will be able to use RTHandles which are totally managed by RG, so you won't be responsible of allocations, lifetime management etc, so should make your life easier. Any code based on RTHandles will be easy to port to the new API.

    RenderGraph is built on RTHandles. You can verify that by looking at the 23 public Graphics repository where RG code is already available (even though the feature is still disabled for users). HDRP also uses RenderGraph and RTHandles.

    The other reason why RTHandles were introduced was dynamic scaling support, which was not possible with the old TempRT system.

    Regarding lack of RTHandles documentation: we are aware of it and the docs team is working also on samples, to make sure that by 22 LTS it will be available.
    For now, on top of upgrade guides, which show very basic examples, you can check URP internal complex render features like SSAO and Decals, which are fully working with RTHandles and Blitter API
     
  19. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    @ManueleB
    Regarding the new Blitter, is the texture always locked to _BlitTexture?
    and is _BlitTexture = _CameraOpaqueTexture?

    Edit #1: Okay. . so it's not, _BlitTexture is the _frameBuffer of the Current RenderPass
     
    Last edited: Feb 12, 2023
  20. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    Calling
    RTHandle.Alloc()
    from a
    ScriptableRendererFeature
    is not supported by URP in 2021, and not best practice in 2023. Whether or not that's "add[ing a] new feature to deprecate [it] the version after" is a matter of semantics, but if a user wants to have best practices for a ScriptableRendererFeature that uses temp textures, they need significantly different code structures in all of 2021, 2022, and 2023.

    The whole idea of allocating/managing RTHandles in a ScriptableRendererFeature only applies to 2022. In 2021, you didn't have RTHandles, and in 2023 if you won't ever allocate RTHandles directly (unless you need to save results between frames), just register for them in the RenderGraph. "Proper RTHandle usage in a render feature" is going to look very different in 2023 than in 2022.

    I know the team is pushing forward to align with HDRP. Which is awesome; I applaud you guys for that. But I'm not sure if encouraging users to move to 2022's APIs only to turn around and tell them to port again to 2023 with its configure methods is a good idea. You're just making more work for everybody involved.
     
    UbiBenKenobi and Lechuza like this.
  21. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    oh welp, i just got the hang of the new RTHandle and blitter API in URP 14(2022), and now it gonna be changed again? *sigh......
     
  22. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110
    no it won't change, don't worry
     
    Lorrak likes this.
  23. ManueleB

    ManueleB

    Unity Technologies

    Joined:
    Jul 6, 2020
    Posts:
    110

    RTHandle.Alloc will be supported in 23 and there are scenarios where you still want to manage your resources manually and import them in RenderGraph (you can see HDRP doing it in different places), so I am not sure where the "best practices" you mention are coming from. We are working on extensive documentation and upgrade guides so I'd suggest to wait for those before drawing conclusions.

    In general, if you have a project in 21, and plan eventually to upgrade to 23, skipping 22 sounds like a bad idea for a couple of reasons:
    • 23 will still have the "old" non-RG path and RG can be enabled as an option, this way you can upgrade your project without breaking changes and then simply porting it to RG. The RTHandle.Alloc (or the ReallocIfNeeded utility function, which you can see used in different places in URP, and probably the best default allocation method) is it still going to be needed so the old non-RG path will work correctly allowing you easily to port to RG and compare the 2 paths on a per feature basis.
    • Normally the deprecation cycle in URP works like this: something is made obsolete with a warning (soft deprecation), so you have a full release cycle ~1 year) to keep using the obsolete API and upgrade to the new one, without having breaking compile errors.
      After the first year of deprecation we "bump" the obsolete warnings to errors, so now projects are forced to update to the latest API. This will allow us in the cycle after that to completely remove the old deprecated code.
      So if you upgrade from 21 to 22 you will have the full 22 release cycle to upgrade all your projects to RTHandles. If you would try to update from 21 to 23 directly, your project will be broken because of "hard deprecation" resulting in compile error
    • The actual rendering code executing commands in RenderGraph uses the same command as the non RG path, these all use RTHandles. Same for APIs like Blitter, they are all RTHandles based. So RTHandles are a mandatory requirement to be able to use RG, and while in 22 you will get only deprecation warnings and be able to use the old RTID API, in 23 this will be a breaking change. The 22 release gives you the transition period to migrate to RTHandles without disruptions, guaranteeing minimal issues when porting to RG in 23, should you decide to do so. This is the same strategy we used for internal development, since adding RTHandles+RG in a single release would have been very disruptive and too much work.
    So, long story short: RTHandles are here to stay as the new default RenderTarget representation in URP and HDRP, which is a big step towards unification. RTHandles.Alloc is not a bad practice and totally justified to use in some scenarios with RenderGraph, and also necessary in upgrading your code "towards" the 23 RenderGraph changes, minimizing disruptions.

    Documentation will be available when we are ready to roll out RG, for now if you are curious about the future changes, all code is public and already visible in the 23.2 branch, or in HDRP
     
    Lechuza likes this.
  24. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    The Render Graph documentation itself: https://docs.unity3d.com/Packages/c...l/render-graph-writing-a-render-pipeline.html . The texture handles you want to import when writing a custom SRP are mainly for textures you need to persist between frames (previous frame for temporal effects, buffers used in a fluid simulation, etc). At least when I was working on my SRP, the impression I got was that RG should manage any temporary buffers so that it could track their lifetimes and repurpose them.

    That being said, it's not really my place to be making statements or advice here, so I'll let y'all handle the messaging.
     
  25. funkyCoty

    funkyCoty

    Joined:
    May 22, 2018
    Posts:
    741
    This all sounds great if you're just making a game. But if you're like me (or a few others) and making asset store stuff, what you described is actual hell. It means basically yearly you expect asset store developers to update every single graphics related package to account for random stuff becoming obsolete.
     
  26. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,526
    Hi,

    Is there any reason for all those massively breaking changes between versions ?

    Why need to break everything between 2021 and 2022 and then between 2021 and 2023 and 2022 and 2023 ?

    I also see a massive amount of deprecation warnings in 2023, does that mean that 2024 will again break everything ?

    We cant work this way, is getting too much to handle. Nothing works anymore.

    Note also that one year to essentially remake out image effects from start is not even close to enough, even three years could be a challenge.

    Not everyone knows the inners of Unity and the details of the changes made to be able to port things so easily, don't judge by your own knowledge of the engine, we usually have to spend weeks to understand what broke and why and then months fixing it. And this is only for one of the multiple tiny parts of the project that breaks everything.

    The real issue is that the two pipelines and backend were not unified from the start.

    We worry, a lot. Making such changes can render whole big project unrecoverable, not everyone can spend millions in salaries to fix broken things. You either have a stable engine or not.
     
    Last edited: Mar 26, 2023
  27. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    281
    Hi everyone, thank you for your feedback!

    I'm the Director for the Render Pipelines and can share a bit of context about the transition to RenderGraph and how we aim to support you. With adoption of RenderGraph in URP we aim to provide a huge amount of benefits like higher stability and better GPU bandwidth and memory usage. It's also an important step to unify the render pipeline foundation and bring HDRP and URP closer together.

    We are also working towards a RenderGraph based Custom Render Pass API that will abstract some of the lower level complexities like managing render targets, setting up native render passes for optimal bandwidth on mobile (tiled) GPUs, dynamic resolution, etc. Because RG hides this complexity, it's also a great abstraction to have as a stable API.

    Now, it's a huge project and it's one of our top priorities to make the transition fast to reduce the burden on you. Most of the groundwork is done at this point, for example adopting RTHandles. We can't avoid changing URP but we do want to do everything we can to support you. We have time planned next month to analyze popular assets and write down best practices to upgrade. We'll share elaborate documentation. The APIs will be consistent and have more guardrails to guide you towards optimal usage. We'll provide additional helper functions. Our goal is to make upgrading straightforward and make sure you know exactly what you are doing. We'll start sharing more info soon and look forward to your feedback on how to best support you in this transition.
     
  28. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,526
    Hi, thanks for the feedback.

    One of the biggest and most confusing issues is rendering on demand a different camera with some shader, e.g to get scene depth from top down view on a specific objects layer etc

    Is it possible to provide a concrete globally applicable example that wont break between 2021 and 2024 unity ?

    That would be great help, thanks in advance.

    A change like removing the urp camera render will just break my most popular assets completly, as rendering top down depth is used in all to adapt foliage, water etc

    I now write one such system for each pipeline and have some issues in hdrp syching such rendering, so a fully working globally applicable example would be the ultimate help

    Currently seeing the unity 2023 warnings, i have to issue a warning in most of my assets that wont be 2024 compatible, as will require very radical changes to work.

    Another issue is missing proper replacement shaders like is standard pipeline, if standard is removed in 2024 also will break one of my systems that uses it for proper repacement shaders, that grab each individual material properties
     
    Last edited: Mar 29, 2023
  29. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    281
    Thanks for the suggestion, I'll investigate how we can share info about this use case.

    Replacement shaders for SRPs landed in 2022.2. Did you have a chance to try this?
     
    nasos_333 likes this.
  30. nasos_333

    nasos_333

    Joined:
    Feb 13, 2013
    Posts:
    13,526
    Thanks, that would be very helpful as rendering depth from other cameras has been one of the biggest issues i deal with from the pipeline introduction.

    Thanks also for the hint on the replacement shaders, i just finalized porting all my code to RTHandle, so i can now start port on this, hopefully will be an easy replace.

    I use this in LUMINA asset to voxelize the world, so material properties are important to be passed to the shading to grab the color and light information. I toggle to Standard Pipeline to do this rendering and then back to URP, so if works with the new system would be ideal to avoid this toggle.

    Will get back on this as it progresses.
     
  31. Reanimate_L

    Reanimate_L

    Joined:
    Oct 10, 2009
    Posts:
    2,788
    I found rendering using replacement shaders break SRP Batching sadly, any chance this can be improved/fixed?
     
    nasos_333 likes this.
  32. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    244
    Same, haha. I can't wrap my head around these RTHandles, where to initialize them, and how to pass them from one ScriptableRenderPass to the next.

    Even the sample code by @Eno-Khaon does not work for me in 2022.2 with URP 14

    Would be so nice to have a sample project or at least a working code example of how to set up a basic ScriptableRendererFeature that uses RTHandle
     
    Last edited: Apr 18, 2023
  33. StaggartCreations

    StaggartCreations

    Joined:
    Feb 18, 2015
    Posts:
    2,296
    Please consider taking this asset into consideration for analysis. It's arguably either the worst or best implementation of a ScriptableRenderPass for post processing. It works from Unity 2019.4 through 2023.2 (+VR), through no shortage of frustration and effort. Which the kind of abstraction I had hoped Unity would have provided.
     
  34. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    281
    Yes we are working on this now. The changes are not trivial so it will likely not be backported to 22LTS and land in 23 LTS.

    The package samples are updated in the latest 22.2.x patch releases.

    Thanks for the suggestion, we'll take a look.
     
  35. noio

    noio

    Joined:
    Dec 17, 2013
    Posts:
    244
    but I wish it included something like what @Eno-Khaon started this thread with. A custom feature with a few passes, working on temporary RT data.

    The "KeepFrame" example comes somewhat close, maybe it'll help me understand the bigger picture.

    1. Inside
      DrawOldFramePass.Execute()
      why is
      cmd.SetGlobalTexture
      used? I thought
      Blitter.BlitTexture
      sets the source (to to textureId "_BlitTexture")?
    2. Shouldn't source be
      m_Handle
      ? Why is
      cameraColorTargetHandle
      passed as the source?
    3. Where is the target of the DrawOldFramePass set? I don't see any calls to
      ConfigureTarget
      ?
    4. What makes the difference between this being a KeepFrame and discarding the data? Where is it determined that this data will persist to the next frame?

    Code (CSharp):
    1. CommandBuffer cmd = CommandBufferPool.Get("DrawOldFramePass");
    2. cmd.SetGlobalTexture(m_TextureName, m_Handle);
    3.  
    4. var source = renderingData.cameraData.renderer.cameraColorTargetHandle;
    5.  
    6. Vector2 viewportScale = source.useScaling ? new Vector2(source.rtHandleProperties.rtHandleScale.x, source.rtHandleProperties.rtHandleScale.y) : Vector2.one;
    7. Blitter.BlitTexture(cmd, source, viewportScale, m_DrawOldFrameMaterial, 0);
    8.  
    9. context.ExecuteCommandBuffer(cmd);
    10. CommandBufferPool.Release(cmd);
    11.  
    EDIT: I've answered some of my own questions already :)

    1. If the shader uses "_BlitTexture" as input, there is no need to SetGlobalTexture.
    2. It's not needed.
    3. I'm assuming that the current camera render target is the default target.
    4. This I didn't figure out.
     
    Last edited: Apr 19, 2023
  36. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    I'm certainly the type to be on the fence when it comes to visual editor-type coding replacements. I half-begrudgingly use Shader Graph, simply because it fills in 11 variants equally, but it definitely doesn't cover every possible base. Overall, I definitely prefer the coding angle anywhere I can get away with it, but that's just my personal take on it.

    I certainly expect that I *will* wind up utilizing RenderGraph once I do someday transition to a newer Editor version, but that's not really an urgent need (yet) as far as I can tell.

    Incidentally, I'm specifically utilizing replacement shaders (which I'll get to later in this post), since my specific goal eventually turned to rendering an indeterminate number of GameObjects sharing Material attributes across multiple Shaders.

    ------------------------------------------

    Wait, WHAT?

    No, no, no, no, no. Even after (barely) figuring some stuff out, there's no way I'm the ideal person to use as a template.

    That said, I *did* get things into a functioning state (Unity 2022.2.10, URP 14.0.6 - Specifically, to utilize ScriptableRenderer.overrideShader). But there's no possible way it's "by the book", considering my implementation wound up not even touching the RTHandles.Alloc() function, when any documentation made it sound like it's what I *should* have been using.

    ------------------------------------------

    Considering I hadn't stopped working on this after my early posts here, this seems like as good a time as any to point out the final (a.k.a. current) state of the RendererFeature work that started all of this.

    Here's the general rundown (pared back a bit, and with hundreds of commented/failed lines removed) of the current/working state of the scripts involved... This takes GameObjects rendered to a specific Layer, renders them again with a Replacement Shader into two output textures, blurs those two and a third texture horizontally, then blurs them again vertically while simultaneously compositing them for the final render:

    CustomFeature.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public partial class CustomFeature : ScriptableRendererFeature
    6. {
    7.     [System.Serializable]
    8.     public class PassSettings
    9.     {
    10.         public LayerMask layerMask = -1; // All layers
    11.         public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
    12.         public Shader gatherDataShader;
    13.         // pass indices aren't necessary - just included out of principle
    14.         public int gatherDataPassIndex = 0;
    15.         public Material blurCompositeMat;
    16.         public int blur1PassIndex = 0;
    17.         public int blur2CompositePassIndex = 1;
    18.     }
    19.  
    20.     GatherDataPass gatherDataPass;
    21.     BlurPass blurPass;
    22.     CompositePass compositePass;
    23.  
    24.     public RTHandle normalDepthTex;
    25.     public RTHandle miscDataTex;
    26.     public RTHandle mainBlurTex;
    27.     public RTHandle normalDepthBlurTex;
    28.     public RTHandle miscDataBlurTex;
    29.  
    30.     public PassSettings passSettings = new PassSettings();
    31.     // This was part of the example I copied as-is. It may or may not really be important
    32.     public string[] passNames;
    33.  
    34.     public CustomFeature()
    35.     {
    36.         if(passSettings == null)
    37.         {
    38.             // This should be, and never has, been reached
    39.             // but remains as insurance
    40.             passSettings = new PassSettings();
    41.         }
    42.     }
    43.  
    44.     /// <inheritdoc/>
    45.     public override void Create()
    46.     {
    47.         gatherDataPass = new GatherDataPass(passSettings, passNames);
    48.         gatherDataPass.SetDepthState();
    49.         compositePass = new CompositePass(passSettings);
    50.     }
    51.  
    52.     public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    53.     {
    54.         if(renderingData.cameraData.isPreviewCamera)
    55.         {
    56.             return;
    57.         }
    58.         if(passSettings.gatherDataShader == null || passSettings.blurCompositeMat == null)
    59.         {
    60.             return;
    61.         }
    62.         renderer.EnqueuePass(gatherDataPass);
    63.         renderer.EnqueuePass(compositePass);
    64.     }
    65.  
    66.     public override void SetupRenderPasses(ScriptableRenderer renderer, in RenderingData renderingData)
    67.     {
    68.         RenderTextureDescriptor desc = renderingData.cameraData.cameraTargetDescriptor;
    69.         desc.depthBufferBits = 0; // Color and depth cannot be combined in RTHandles
    70.         RenderTextureDescriptor descNormalDepth = renderingData.cameraData.cameraTargetDescriptor;
    71.         descNormalDepth.depthBufferBits = 0;
    72.         // A 4th channel and moderate accuracy needed for
    73.         // [NormalXY, Depth, 1.0] texture for blurring later
    74.         descNormalDepth.colorFormat = RenderTextureFormat.ARGB64;
    75.  
    76.         RenderingUtils.ReAllocateIfNeeded(ref mainBlurTex, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_MainBlurTex");
    77.         RenderingUtils.ReAllocateIfNeeded(ref normalDepthTex, descNormalDepth, FilterMode.Point, TextureWrapMode.Clamp, name: "_NormalDepthTex");
    78.         RenderingUtils.ReAllocateIfNeeded(ref normalDepthBlurTex, descNormalDepth, FilterMode.Point, TextureWrapMode.Clamp, name: "_NormalDepthBlurTex");
    79.         RenderingUtils.ReAllocateIfNeeded(ref miscDataTex, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_MiscDataTex");
    80.         RenderingUtils.ReAllocateIfNeeded(ref miscDataBlurTex, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_MiscDataBlurTex");
    81.  
    82.         gatherDataPass.Setup(normalDepthTex, miscDataTex);
    83.         compositePass.ConfigureInput(ScriptableRenderPassInput.Color | ScriptableRenderPassInput.Depth);
    84.         compositePass.Setup(mainBlurTex, normalDepthTex, normalDepthBlurTex, miscDataTex, miscDataBlurTex);
    85.     }
    86.  
    87.     protected override void Dispose(bool disposing)
    88.     {
    89.         mainBlurTex?.Release();
    90.         normalDepthTex?.Release();
    91.         normalDepthBlurTex?.Release();
    92.         miscDataTex?.Release();
    93.         miscDataBlurTex?.Release();
    94.  
    95.         // Some overlap, but run as backup
    96.         gatherDataPass.Dispose();
    97.         compositePass.Dispose();
    98.     }
    99. }
    And the main files for the passes themselves:

    GatherDataPass.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4. using System.Collections.Generic;
    5.  
    6. public partial class CustomFeature : ScriptableRendererFeature
    7. {
    8.     class GatherDataPass : ScriptableRenderPass
    9.     {
    10.         const string profilerTag = "Custom Pass: Gather Data";
    11.         ProfilingSampler sampler = new ProfilingSampler(profilerTag);
    12.  
    13.         CustomFeature.PassSettings passSettings;
    14.  
    15.         public RTHandle normalDepthTex;
    16.         public RTHandle miscDataTex;
    17.  
    18.         RTHandle[] mrtHandles;
    19.  
    20.         FilteringSettings filteringSettings;
    21.         // This was part of the example I copied as-is. It may or may not really be important
    22.         List<ShaderTagId> shaderTagIdList = new List<ShaderTagId>();
    23.  
    24.         RenderStateBlock renderStateBlock;
    25.  
    26.         public GatherDataPass(PassSettings settings, string[] passNames)
    27.         {
    28.             passSettings = settings;
    29.             renderPassEvent = passSettings.renderPassEvent;
    30.             RenderQueueRange renderQueueRange = RenderQueueRange.transparent;
    31.             filteringSettings = new FilteringSettings(renderQueueRange, passSettings.layerMask);
    32.  
    33.             if(passNames != null && passNames.Length > 0)
    34.             {
    35.                 for(int i = 0; i < passNames.Length; i++)
    36.                 {
    37.                     shaderTagIdList.Add(new ShaderTagId(passNames[i]));
    38.                 }
    39.             }
    40.             else
    41.             {
    42.                 shaderTagIdList.Add(new ShaderTagId("SRPDefaultUnlit"));
    43.                 shaderTagIdList.Add(new ShaderTagId("UniversalForward"));
    44.                 shaderTagIdList.Add(new ShaderTagId("UniversalForwardOnly"));
    45.             }
    46.  
    47.             renderStateBlock = new RenderStateBlock(RenderStateMask.Nothing);
    48.         }
    49.  
    50.         public void SetDepthState()
    51.         {
    52.             renderStateBlock.mask |= RenderStateMask.Depth;
    53.             renderStateBlock.depthState = new DepthState(true, CompareFunction.LessEqual);
    54.         }
    55.  
    56.         public void Setup(RTHandle normalDepthTex, RTHandle miscDataTex)
    57.         {
    58.             this.normalDepthTex = normalDepthTex;
    59.             this.miscDataTex = miscDataTex;
    60.         }
    61.  
    62.         public void Dispose()
    63.         {
    64.             normalDepthTex?.Release();
    65.             miscDataTex?.Release();
    66.         }
    67.  
    68.         public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    69.         {
    70.             passSettings.blurCompositeMat.SetTexture(normalDepthTex.name, normalDepthTex);
    71.             passSettings.blurCompositeMat.SetTexture(miscDataTex.name, miscDataTex);
    72.  
    73.             if(mrtHandles == null || mrtHandles.Length != 2)
    74.             {
    75.                 mrtHandles = new RTHandle[2];
    76.             }
    77.             // Assign Multiple Render Targets for ScriptableRenderContext.DrawRenderers()
    78.             mrtHandles[0] = normalDepthTex;
    79.             mrtHandles[1] = miscDataTex;
    80.  
    81.             ConfigureTarget(mrtHandles, renderingData.cameraData.renderer.cameraDepthTargetHandle);
    82.             ConfigureClear(ClearFlag.All, Color.clear);
    83.         }
    84.  
    85.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    86.         {
    87.             if(passSettings.gatherDataShader == null)
    88.             {
    89.                 return;
    90.             }
    91.  
    92.             DrawingSettings drawingSettings = CreateDrawingSettings(shaderTagIdList, ref renderingData, SortingCriteria.CommonTransparent);
    93.  
    94.             // overrideMaterial and overrideShader are mutually exclusive, according to documentation
    95.             drawingSettings.overrideMaterial = null;
    96.             drawingSettings.overrideShader = passSettings.gatherDataShader;
    97.             drawingSettings.overrideShaderPassIndex = passSettings.gatherDataPassIndex;
    98.  
    99.             CommandBuffer cmd = CommandBufferPool.Get();
    100.             using(new ProfilingScope(cmd, sampler))
    101.             {
    102.                 context.DrawRenderers(renderingData.cullResults, ref drawingSettings, ref filteringSettings, ref renderStateBlock);
    103.             }
    104.  
    105.             context.ExecuteCommandBuffer(cmd);
    106.             cmd.Clear();
    107.  
    108.             CommandBufferPool.Release(cmd);
    109.         }
    110.  
    111.         public override void OnCameraCleanup(CommandBuffer cmd)
    112.         {
    113.             if(cmd == null)
    114.             {
    115.                 throw new System.ArgumentNullException("cmd");
    116.             }
    117.  
    118.             mainRender = null;
    119.         }
    120.     }
    121. }
    BlurPass.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public partial class CustomFeature : ScriptableRendererFeature
    6. {
    7.     class BlurPass : ScriptableRenderPass
    8.     {
    9.         const string profilerTag = "Custom Pass: Blur";
    10.         ProfilingSampler sampler = new ProfilingSampler(profilerTag);
    11.  
    12.         CustomFeature.PassSettings passSettings;
    13.  
    14.         public RTHandle mainRender;
    15.         public RTHandle mainBlurTex;
    16.         public RTHandle normalDepthTex;
    17.         public RTHandle normalDepthBlurTex;
    18.         public RTHandle miscDataTex;
    19.         public RTHandle miscDataBlurTex;
    20.  
    21.         RTHandle[] mrtHandles;
    22.  
    23.         public BlurPass(PassSettings settings)
    24.         {
    25.             passSettings = settings;
    26.             renderPassEvent = passSettings.renderPassEvent;
    27.         }
    28.  
    29.         public void Setup(RTHandle mainBlurTex, RTHandle normalDepthTex, RTHandle normalDepthBlurTex, RTHandle miscDataTex, RTHandle miscDataBlurTex)
    30.         {
    31.             this.mainBlurTex = mainBlurTex;
    32.             this.normalDepthTex = normalDepthTex;
    33.             this.normalDepthBlurTex = normalDepthBlurTex;
    34.             this.miscDataTex = miscDataTex;
    35.             this.miscDataBlurTex = miscDataBlurTex;
    36.         }
    37.  
    38.         public void Dispose()
    39.         {
    40.             mainBlurTex?.Release();
    41.             normalDepthTex?.Release();
    42.             miscDataTex?.Release();
    43.             normalDepthBlurTex?.Release();
    44.             miscDataBlurTex?.Release();
    45.             if(mrtHandles != null)
    46.             {
    47.                 for(int i = 0; i < mrtHandles.Length; i++)
    48.                 {
    49.                     mrtHandles[i]?.Release();
    50.                 }
    51.             }
    52.         }
    53.  
    54.         public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    55.         {
    56.             mainRender = renderingData.cameraData.renderer.cameraColorTargetHandle;
    57.             passSettings.blurCompositeMat.mainTexture = mainRender;
    58.  
    59.             passSettings.blurCompositeMat.SetTexture(mainBlurTex.name, mainBlurTex);
    60.             passSettings.blurCompositeMat.SetTexture(normalDepthBlurTex.name, normalDepthBlurTex);
    61.             passSettings.blurCompositeMat.SetTexture(miscDataBlurTex.name, miscDataBlurTex);
    62.  
    63.             if(mrtHandles == null || mrtHandles.Length != 3)
    64.             {
    65.                 mrtHandles = new RTHandle[3];
    66.             }
    67.             // Assign Multiple Render Targets for Blitter.BlitTexture()
    68.             mrtHandles[0] = mainBlurTex;
    69.             mrtHandles[1] = normalDepthBlurTex;
    70.             mrtHandles[2] = miscDataBlurTex;
    71.  
    72.             ConfigureTarget(mrtHandles, renderingData.cameraData.renderer.cameraDepthTargetHandle);
    73.         }
    74.  
    75.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    76.         {
    77.             if(passSettings.blurCompositeMat == null)
    78.             {
    79.                 return;
    80.             }
    81.  
    82.             CommandBuffer cmd = CommandBufferPool.Get();
    83.             using(new ProfilingScope(cmd, sampler))
    84.             {
    85.                 Blitter.BlitTexture(cmd, mainRender, Vector2.one, passSettings.blurCompositeMat, passSettings.blur1PassIndex);
    86.             }
    87.  
    88.             context.ExecuteCommandBuffer(cmd);
    89.             cmd.Clear();
    90.  
    91.             CommandBufferPool.Release(cmd);
    92.         }
    93.  
    94.         public override void OnCameraCleanup(CommandBuffer cmd)
    95.         {
    96.             if(cmd == null)
    97.             {
    98.                 throw new System.ArgumentNullException("cmd");
    99.             }
    100.             mainRender = null;
    101.         }
    102.     }
    103. }
    CompositePass.cs

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4.  
    5. public partial class CustomFeature : ScriptableRendererFeature
    6. {
    7.     class CompositePass : ScriptableRenderPass
    8.     {
    9.         const string profilerTag = "Custom Pass: Composite";
    10.         ProfilingSampler sampler = new ProfilingSampler(profilerTag);
    11.  
    12.         CustomFeature.PassSettings passSettings;
    13.  
    14.         public RTHandle mainRender;
    15.         public RTHandle mainBlurTex;
    16.         public RTHandle normalDepthTex;
    17.         public RTHandle normalDepthBlurTex;
    18.         public RTHandle miscDataTex;
    19.         public RTHandle miscDataBlurTex;
    20.         public RTHandle tempTex;
    21.  
    22.         public CompositePass(PassSettings settings)
    23.         {
    24.             passSettings = settings;
    25.             renderPassEvent = passSettings.renderPassEvent;
    26.         }
    27.  
    28.         public void Setup(RTHandle mainBlurTex, RTHandle normalDepthTex, RTHandle normalDepthBlurTex, RTHandle miscDataTex, RTHandle miscDataBlurTex)
    29.         {
    30.             this.mainBlurTex = mainBlurTex;
    31.             this.normalDepthTex = normalDepthTex;
    32.             this.normalDepthBlurTex = normalDepthBlurTex;
    33.             this.miscDataTex = miscDataTex;
    34.             this.miscDataBlurTex = miscDataBlurTex;
    35.         }
    36.  
    37.         public void Dispose()
    38.         {
    39.             mainBlurTex?.Release();
    40.             normalDepthTex?.Release();
    41.             normalDepthBlurTex?.Release();
    42.             miscDataTex?.Release();
    43.             miscDataBlurTex?.Release();
    44.             tempTex?.Release();
    45.         }
    46.  
    47.         public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    48.         {
    49.             RenderTextureDescriptor desc = renderingData.cameraData.cameraTargetDescriptor;
    50.             desc.depthBufferBits = 0; // Color and depth cannot be combined in RTHandles
    51.  
    52.             mainRender = renderingData.cameraData.renderer.cameraColorTargetHandle;
    53.             passSettings.blurCompositeMat.mainTexture = mainRender;
    54.             // Need a quick back-and-forth to render to the correct main texture
    55.             RenderingUtils.ReAllocateIfNeeded(ref tempTex, desc, FilterMode.Point, TextureWrapMode.Clamp, name: "_TempTex");
    56.  
    57.             ConfigureTarget(mainRender, renderingData.cameraData.renderer.cameraDepthTargetHandle);
    58.         }
    59.  
    60.         public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    61.         {
    62.             if(passSettings.blurCompositeMat == null)
    63.             {
    64.                 return;
    65.             }
    66.  
    67.             CommandBuffer cmd = CommandBufferPool.Get();
    68.             using(new ProfilingScope(cmd, sampler))
    69.             {
    70.                 // --- Perform second blur and composite results ---
    71.                 Blitter.BlitCameraTexture(cmd, mainRender, tempTex, passSettings.blurCompositeMat, passSettings.blur2CompositePassIndex);
    72.                 Blitter.BlitCameraTexture(cmd, tempTex, mainRender);
    73.             }
    74.  
    75.             context.ExecuteCommandBuffer(cmd);
    76.             cmd.Clear();
    77.  
    78.             CommandBufferPool.Release(cmd);
    79.         }
    80.  
    81.         public override void OnCameraCleanup(CommandBuffer cmd)
    82.         {
    83.             if(cmd == null)
    84.             {
    85.                 throw new System.ArgumentNullException("cmd");
    86.             }
    87.  
    88.             mainRender = null;
    89.         }
    90.     }
    91. }
    ------------------------------------------

    For Shaders, I start with a Shader Graph-based shader, to utilize Physically-Based Rendering properties using a Lit, Transparent shader. This is also done in order to both cast and receive shadows before moving on to the RendererFeature effects. Since this isn't really specific to this thread (and I've gotta keep *SOME* secrets), these will be more about noting structure and listing output textures.

    After this, we have (greatly simplified):

    GatherData.shader

    Code (CSharp):
    1. Shader "Hidden/GatherData"
    2. {
    3.     Properties
    4.     {
    5.         // URP MainTex equivalent
    6.         _BaseMap ("Texture", 2D) = "white" {}
    7.         _NormalMap ("Normal Map", 2D) = "white" {}
    8.         // A few of these
    9.         _Modifier ("Modifier", Range(0.0, 1.0)) = 0.0
    10.     }
    11.  
    12.     HLSLINCLUDE
    13.  
    14.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    15.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
    16.  
    17.         struct VertexInput
    18.         {
    19.             float4 positionOS : POSITION;
    20.             float2 uv : TEXCOORD0;
    21.             float3 normal : NORMAL;
    22.             float4 tangent : TANGENT;
    23.             uint vertexID : SV_VertexID;
    24.             UNITY_VERTEX_INPUT_INSTANCE_ID
    25.         };
    26.  
    27.         struct VertexOutput
    28.         {
    29.             float4 positionCS : SV_POSITION;
    30.             float2 uv : TEXCOORD0;
    31.             float3 tbnX: TEXCOORD1; // tangent, bitangent, normal
    32.             float3 tbnY: TEXCOORD2;
    33.             float3 tbnZ: TEXCOORD3;
    34.             UNITY_VERTEX_OUTPUT_STEREO
    35.         };
    36.  
    37.         struct GatherDataOutput
    38.         {
    39.             half4 normalDepth : SV_Target0;
    40.             half4 miscData : SV_Target1;
    41.         };
    42.  
    43.         CBUFFER_START(UnityPerMaterial)
    44.             float _Modifier;
    45.             float4 _BaseMap_TexelSize;
    46.             float4 _BaseMap_ST;
    47.             float4 _NormalMap_TexelSize;
    48.             float4 _NormalMap_ST;
    49.         CBUFFER_END
    50.  
    51.         TEXTURE2D(_BaseMap);
    52.         SAMPLER(sampler_BaseMap);
    53.      
    54.         TEXTURE2D(_NormalMap);
    55.         SAMPLER(sampler_NormalMap);
    56.  
    57.         VertexOutput Vert(VertexInput input)
    58.         {
    59.             VertexOutput output;
    60.             UNITY_SETUP_INSTANCE_ID(input);
    61.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    62.  
    63.             output.positionCS = TransformObjectToHClip(input.positionOS.xyz);
    64.             float3 normalWorld = TransformObjectToWorldNormal(input.normal);
    65.             float3 tangentWorld = TransformObjectToWorldDir(input.tangent.xyz);
    66.             float3 bitangentWorld = cross(normalWorld, tangentWorld) * input.tangent.w * unity_WorldTransformParams.w;
    67.  
    68.             output.tbnX = float3(tangentWorld.x, bitangentWorld.x, normalWorld.x);
    69.             output.tbnY = float3(tangentWorld.y, bitangentWorld.y, normalWorld.y);
    70.             output.tbnZ = float3(tangentWorld.z, bitangentWorld.z, normalWorld.z);
    71.  
    72.             output.uv = input.uv;
    73.             return output;
    74.         }
    75.  
    76.         GatherDataOutput GatherData(VertexOutput input)
    77.         {
    78.             GatherDataOutput output;
    79.  
    80.             float4 texNormal = float4(UnpackNormal(SAMPLE_TEXTURE2D(_NormalMap, sampler_NormalMap, input.uv)), 1);
    81.             float3 scaledNormal = float3(texNormal.rg * _NormalStrength, lerp(1, texNormal.b, saturate(_NormalStrength)));
    82.             float3 tangentNormal = scaledNormal;
    83.             float3 worldNormal;
    84.             worldNormal.x = dot(input.tbnX, tangentNormal);
    85.             worldNormal.y = dot(input.tbnY, tangentNormal);
    86.             worldNormal.z = dot(input.tbnZ, tangentNormal);
    87.  
    88.             float3 screenNormal = normalize(mul((float3x3)UNITY_MATRIX_V, worldNormal));
    89.  
    90.             float depth = input.positionCS.w;
    91.             float linearDepth = LinearEyeDepth(depth, _ZBufferParams);
    92.  
    93.             // normalDepth: 4-channel, 16-bits-per-channel
    94.             output.normalDepth = half4(screenNormal.xy * 0.5 + 0.5, linearDepth, 1.0);
    95.             output.miscData = half4(_ModifierA, _ModifierB, _ModifierC, 1); // unused alpha
    96.  
    97.             return output;
    98.         }
    99.  
    100.     ENDHLSL
    101.  
    102.     SubShader
    103.     {
    104.         Cull Back
    105.         ZWrite On
    106.         ZTest LEqual
    107.  
    108.         Tags
    109.         {
    110.             "RenderType"="Opaque"
    111.             "RenderPipeline"="UniversalPipeline"
    112.             "Queue"="Transparent"
    113.         }
    114.  
    115.         Pass // 0
    116.         {
    117.             Name "Gather Data"
    118.  
    119.             HLSLPROGRAM
    120.  
    121.             #pragma vertex Vert
    122.             #pragma fragment GatherData
    123.  
    124.             ENDHLSL
    125.         }
    126.     }
    127. }
    BlurComposite.shader

    Code (CSharp):
    1. Shader "Hidden/BlurComposite"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _BlurRange ("Blur Range", Range(0.0, 100.0)) = 20.0
    7.     }
    8.  
    9.     HLSLINCLUDE
    10.  
    11.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
    12.         #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"
    13.  
    14.         struct VertexInput
    15.         {
    16.             float4 positionOS : POSITION;
    17.             float2 uv : TEXCOORD0;
    18.             uint vertexID : SV_VertexID;
    19.             UNITY_VERTEX_INPUT_INSTANCE_ID
    20.         };
    21.  
    22.         struct VertexOutput
    23.         {
    24.             float4 positionCS : SV_POSITION;
    25.             float2 uv : TEXCOORD0;
    26.             float3 miscVertexData : TEXCOORD1;
    27.             UNITY_VERTEX_OUTPUT_STEREO
    28.         };
    29.  
    30.         struct BlurOutput
    31.         {
    32.             half4 blurredColor : SV_Target0;
    33.             half4 blurredNormalDepth : SV_Target1;
    34.             half4 blurredMiscData : SV_Target2;
    35.         };
    36.  
    37.         CBUFFER_START(UnityPerMaterial)
    38.             float4 _MainTex_TexelSize;
    39.             float4 _MainTex_ST;
    40.             float _BlurRange;
    41.         CBUFFER_END
    42.  
    43.         uniform float4 _BlitScaleBias;
    44.  
    45.         TEXTURE2D(_MainTex);
    46.         SAMPLER(sampler_MainTex);
    47.  
    48.         TEXTURE2D(_NormalDepthTex);
    49.         SAMPLER(sampler_NormalDepthTex);
    50.  
    51.         TEXTURE2D(_MiscDataTex);
    52.         SAMPLER(sampler_MiscDataTex);
    53.  
    54.         TEXTURE2D(_NormalDepthBlurTex);
    55.         SAMPLER(sampler_NormalDepthBlurTex);
    56.  
    57.         TEXTURE2D(_MiscDataBlurTex);
    58.         SAMPLER(sampler_MiscDataBlurTex);
    59.  
    60.         TEXTURE2D(_MainBlurTex);
    61.         SAMPLER(sampler_MainBlurTex);
    62.  
    63.         TEXTURE2D(_CameraOpaqueTexture);
    64.         SAMPLER(sampler_CameraOpaqueTexture);
    65.  
    66.         VertexOutput VertHorizontal(VertexInput input)
    67.         {
    68.             VertexOutput output;
    69.             UNITY_SETUP_INSTANCE_ID(input);
    70.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    71.  
    72. #if SHADER_API_GLES
    73.             float4 pos = input.positionOS;
    74.             float2 uv  = input.uv;
    75. #else
    76.             float4 pos = GetFullScreenTriangleVertexPosition(input.vertexID);
    77.             float2 uv  = GetFullScreenTriangleTexCoord(input.vertexID);
    78. #endif
    79.  
    80.             output.positionCS = pos;
    81.             // snip -> Setup screen blur coord offsets while it's more efficient
    82.             return output;
    83.         }
    84.  
    85.         VertexOutput VertVertical(VertexInput input)
    86.         {
    87.             VertexOutput output;
    88.             UNITY_SETUP_INSTANCE_ID(input);
    89.             UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
    90.  
    91. #if SHADER_API_GLES
    92.             float4 pos = input.positionOS;
    93.             float2 uv  = input.uv;
    94. #else
    95.             float4 pos = GetFullScreenTriangleVertexPosition(input.vertexID);
    96.             float2 uv  = GetFullScreenTriangleTexCoord(input.vertexID);
    97. #endif
    98.  
    99.             output.positionCS = pos;
    100.             // snip -> Setup screen blur coord offsets while it's more efficient
    101.             return output;
    102.         }
    103.  
    104.         BlurOutput HorizontalBlur(VertexOutput input)
    105.         {
    106.             BlurOutput output;
    107.  
    108.             float4 blurredColor = SAMPLE_TEXTURE2D(_MainTex, sampler_MainTex, input.uv);
    109.             float4 blurredNormalDepth = SAMPLE_TEXTURE2D(_NormalDepthTex, sampler_NormalDepthTex, input.uv);
    110.             float4 blurredMiscData = SAMPLE_TEXTURE2D(_MiscDataTex, sampler_MiscDataTex, input.uv);
    111.          
    112.             // snip -> prepare data and perform horizontal blur for two-pass approach
    113.  
    114.             output.blurredColor = blurredColor;
    115.             output.blurredNormalDepth = blurredNormalDepth;
    116.             output.blurredMiscData = blurredMiscData;
    117.             return output;
    118.         }
    119.  
    120.         half4 VerticalBlurComposite(VertexOutput input) : SV_TARGET
    121.         {
    122.             // --- COMPOSITE ---
    123.             // _MainTex        = original image
    124.             // _MainBlurTex        = original image, blurred
    125.             // _NormalDepthTex    = normalX, normalY, linear01Depth (16-bit), 1
    126.             // _NormalDepthBlurTex
    127.             // _MiscDataTex        = modifierA/B/C
    128.             // _MiscDataBlurTex
    129.             // _CameraOpaqueTexture = background opaque including transparent-cast shadows
    130.  
    131.             // Blur the previous three textures vertically
    132.             // Then blend and composite everything into a final image
    133.             return lerp(mainColor, compositedColor, ratio);
    134.         }
    135.  
    136.     ENDHLSL
    137.  
    138.     SubShader
    139.     {
    140.         Cull Off
    141.         ZWrite Off
    142.         ZTest Always
    143.  
    144.         Tags
    145.         {
    146.             "RenderType"="Opaque"
    147.             "RenderPipeline"="UniversalPipeline"
    148.             "Queue"="Transparent"
    149.         }
    150.  
    151.         Pass // 0
    152.         {
    153.             Name "Horizontal Blur"
    154.  
    155.             HLSLPROGRAM
    156.  
    157.             #pragma vertex VertHorizontal
    158.             #pragma fragment HorizontalBlur
    159.  
    160.             ENDHLSL
    161.         }
    162.      
    163.         Pass // 1
    164.         {
    165.             Name "Vertical Blur Composite"
    166.  
    167.             HLSLPROGRAM
    168.  
    169.             #pragma vertex VertVertical
    170.             #pragma fragment VerticalBlurComposite
    171.  
    172.             ENDHLSL
    173.         }
    174.     }
    175. }
    Note: I briefly attempted to switch to performing a Kawase Blur, but was unable to figure out any means of getting a multi-pass, multi-resolution blur to work adequately with Multiple Render Targets. It doesn't necessarily mean there isn't/wasn't a way to do so, but as was the general point of this thread in the first place, information about trickier rendering concepts is scarce to non-existent.

    ------------------------------------------

    Anyway, for all that's gone into this post, there's still a notable point to be made:

    I'm really not sure whether the RendererFeature I wrote is written well whatsoever. It works quite well (although not with increased render resolution or Multisample Anti-Aliasing, where it breaks visually with respect to the way I'm sampling pixels, but trying to force smaller distance sampling didn't seem to help), though there's obviously a noticeable limitation in not having managed to utilize a Kawase Blur for a better-scalable effect.

    Also, as has been mentioned (in numerous respects), the complete loss of batching currently has the potential to have meaningful performance implications if not carefully kept in check (for instance, having hundreds of objects, each with their own draw call in the designated Layer).
     
    Last edited: Apr 23, 2023
  37. AljoshaD

    AljoshaD

    Unity Technologies

    Joined:
    May 27, 2019
    Posts:
    281
    For everyones' awareness here, we've shared the first info on how to use RenderGraph in 23.3 here.
     
    nasos_333 and ElliotB like this.