Search Unity

Question (Unity 2022.2.0b12, URP 14.0.3) Multiple Render Target Blur

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

  1. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    First, a note: I'm using a beta version of Unity to have access to DrawingSettings.overrideShader since my plan includes a variety of independently-textured objects processed in a "single" additional step.

    I'm facing a roadblock, however, in attempting to get multiple render target (MRT) output for a blur step, consisting of multiple passes and multiple rescalings. Using this github example (and its respective links) as a basis for the general algorithm concept, I intend to work down in resolution, then back up, taking a few blur samples at each step along the way. The catch? I have three Render Textures I want to do this with simultaneously, using data from each (at least one specific one, anyway) to influence the effect on the others.

    To clarify the basic idea, I'm rendering a select set of GameObjects (ScriptableRenderContext.DrawRenderers(...), focused on a single layer) into two data textures (I needed 6 channels, and 1-to-3 of those needed to be 16-bit anyway for things like normal/depth precision). Then, I intend to blur those, as well as the original, main render, using the previous two as a guide to modify the per-pixel influence on the blurs (any pixels that aren't part of the single-layer render would have their colors pre-multiplied out of the blending, basically). Then, in a final pass, I'll use the collective textures and composite everything back into the final frame.

    Thing is, I *can* make it work, to a degree, with a two-pass Gaussian Blur, where the second pass and compositing stage are combined into a single Shader pass. If I can help it, however, I'm more interested in trying a different approach to the blur. Maybe it won't look as good as I'm hoping, but I'd still like to give it a shot.

    Anyway, this brings us to the real problem: I haven't been able to perform the "Blur" step under these conditions so far. Here's what I'm currently trying (simplified a bit for readability):

    Code (CSharp):
    1. using UnityEngine;
    2. using UnityEngine.Rendering;
    3. using UnityEngine.Rendering.Universal;
    4. using System.Collections.Generic;
    5.  
    6. class BlurPass : ScriptableRenderPass
    7. {
    8.     const string profilerTag = "Custom Pass: Blur";
    9.     ProfilingSampler sampler = new ProfilingSampler(profilerTag);
    10.  
    11.     // Comprised of Shaders, Materials, and pass indices
    12.     CustomFeature.PassSettings passSettings;
    13.  
    14.     public RTHandle mainRender; // Original render, untouched
    15.     public RTHandle mainBlurTex; // Original render, blurred output
    16.     public RTHandle normalDepthTex; // DrawRenderers output 1, untouched
    17.     public RTHandle normalDepthBlurTex;// DrawRenderers output 1, blurred output
    18.     public RTHandle miscDataTex; // DrawRenderers output 2, untouched
    19.     public RTHandle miscDataBlurTex; // DrawRenderers output 2, blurred output
    20.  
    21.     RenderTargetIdentifier[] mrtIdentifiers; // Details later
    22.     // The full series of RTHandle inputs per texture
    23.     RTHandle[] blurPassMainHandles;
    24.     RTHandle[] blurPassNormalDepthHandles;
    25.     RTHandle[] blurPassMiscDataHandles;
    26.  
    27.     public BlurPass(PassSettings settings)
    28.     {
    29.         // When creating this pass, snag the reference to its settings for convenience
    30.         // and set the ScriptableRenderPass.renderPassEvent value (if necessary)
    31.         passSettings = settings;
    32.         renderPassEvent = passSettings.renderPassEvent;
    33.     }
    34.  
    35.     // All of the textures are allocated in the Renderer Feature, since they're
    36.     // passed between each of the three major steps
    37.     public void Setup(RTHandle mainBlurTex, RTHandle normalDepthTex, RTHandle normalDepthBlurTex, RTHandle miscDataTex, RTHandle miscDataBlurTex)
    38.     {
    39.         this.mainBlurTex = mainBlurTex;
    40.         this.normalDepthTex = normalDepthTex;
    41.         this.normalDepthBlurTex = normalDepthBlurTex;
    42.         this.miscDataTex = miscDataTex;
    43.         this.miscDataBlurTex = miscDataBlurTex;
    44.     }
    45.  
    46.     // Called by the Renderer Feature for thorough cleanup
    47.     public void Dispose()
    48.     {
    49.         mainBlurTex?.Release();
    50.         normalDepthTex?.Release();
    51.         normalDepthBlurTex?.Release();
    52.         miscDataTex?.Release();
    53.         miscDataBlurTex?.Release();
    54.         if(blurPassMainHandles != null)
    55.         {
    56.             for(int i = 0; i < blurPassMainHandles.Length; i++)
    57.             {
    58.                 blurPassMainHandles[i]?.Release();
    59.             }
    60.         }
    61.         if(blurPassNormalDepthHandles != null)
    62.         {
    63.             for(int i = 0; i < blurPassNormalDepthHandles.Length; i++)
    64.             {
    65.                 blurPassNormalDepthHandles[i]?.Release();
    66.             }
    67.         }
    68.         if(blurPassMiscDataHandles != null)
    69.         {
    70.             for(int i = 0; i < blurPassMiscDataHandles.Length; i++)
    71.             {
    72.                 blurPassMiscDataHandles[i]?.Release();
    73.             }
    74.         }
    75.     }
    76.  
    77.     public override void OnCameraSetup(CommandBuffer cmd, ref RenderingData renderingData)
    78.     {
    79.         mainRender = renderingData.cameraData.renderer.cameraColorTargetHandle;
    80.  
    81.         // This will supply the Material/Shader with its three Render Targets
    82.         // ConfigureTarget() (and RTHandle[] usage) is expected at around this timing,
    83.         // and doesn't update the target output on the fly during Execute()
    84.         if(mrtIdentifiers == null || mrtIdentifiers.Length != 3)
    85.         {
    86.             mrtIdentifiers = new RenderTargetIdentifier[3];
    87.         }
    88.  
    89.         // Set the first texture to be the original,
    90.         // the last texture to be the final destination,
    91.         // and every pair leading to the middle of the array as the next resolution-changed
    92.         // texture, to be reused when scaling back up in the second half
    93.         RenderTextureDescriptor baseMainDesc = renderingData.cameraData.cameraTargetDescriptor;
    94.         if(blurPassMainHandles == null || blurPassMainHandles.Length != passSettings.blurPasses * 2 + 1)
    95.         {
    96.             blurPassMainHandles = new RTHandle[passSettings.blurPasses * 2 + 1];
    97.         }
    98.         blurPassMainHandles[0] = mainRender;
    99.         blurPassMainHandles[blurPassMainHandles.Length - 1] = mainBlurTex;
    100.  
    101.         for(int i = 1; i <= passSettings.blurPasses; i++)
    102.         {
    103.             RenderTextureDescriptor mainDesc = new RenderTextureDescriptor(baseMainDesc.width / (i * 2), baseMainDesc.height / (i * 2), baseMainDesc.colorFormat, 0, 0);
    104.             RenderingUtils.ReAllocateIfNeeded(ref blurPassMainHandles[i], mainDesc, FilterMode.Bilinear, TextureWrapMode.Clamp, name: string.Format("_MainBlur{0}", i));
    105.             if(i < passSettings.blurPasses)
    106.             {
    107.                 blurPassMainHandles[blurPassMainHandles.Length - 1 - i] = blurPassMainHandles[i];
    108.                 blurPassNormalDepthHandles[blurPassNormalDepthHandles.Length - 1 - i] = blurPassNormalDepthHandles[i];
    109.                 blurPassMiscDataHandles[blurPassMiscDataHandles.Length - 1 - i] = blurPassMiscDataHandles[i];
    110.             }
    111.         }
    112.         // The same is repeated for the other two arrays, but cut for space here
    113.     }
    114.  
    115.     public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    116.     {
    117.         if(passSettings.blurMat == null)
    118.         {
    119.             return;
    120.         }
    121.  
    122.         CommandBuffer cmd = CommandBufferPool.Get();
    123.         using(new ProfilingScope(cmd, sampler))
    124.         {
    125.             for(int i = 0; i < blurPassMainHandles.Length - 1; i++)
    126.             {
    127.                 // Set the extra textures in the Blur Material/Shader to
    128.                 // the most-recently-modified versions
    129.                 // Note: This step seems to be failing... the Frame Debugger shows these as
    130.                 // half-resolution on every iteration, and the "main" texture as 4x4
    131.                 passSettings.blurMat.SetTexture(normalDepthTex.name, blurPassNormalDepthHandles[i]);
    132.                 passSettings.blurMat.SetTexture(miscDataTex.name, blurPassMiscDataHandles[i]);
    133.  
    134.                 // Gather the destination RenderTargetIdentifiers into the array, since
    135.                 // ConfigureTarget() does nothing here and CoreUtils.SetRenderTarget()
    136.                 // doesn't have a RTHandle[] variant
    137.                 mrtIdentifiers[0] = blurPassMainHandles[i + 1].nameID;
    138.                 mrtIdentifiers[1] = blurPassNormalDepthHandles[i + 1].nameID;
    139.                 mrtIdentifiers[2] = blurPassMiscDataHandles[i + 1].nameID;
    140.                 CoreUtils.SetRenderTarget(cmd, mrtIdentifiers, mrtIdentifiers[0]);
    141.                 // The first half runs the Shader's "Downsample" pass and
    142.                 // the second half runs the "Upsample" pass
    143.                 Blitter.BlitTexture(cmd, blurPassMainHandles[i], Vector2.one, passSettings.blurMat, i < passSettings.blurPasses ? passSettings.blurDownPassIndex : passSettings.blurUpPassIndex);
    144.             }
    145.         }
    146.  
    147.         context.ExecuteCommandBuffer(cmd);
    148.         cmd.Clear();
    149.  
    150.         CommandBufferPool.Release(cmd);
    151.     }
    152.  
    153.     public override void OnCameraCleanup(CommandBuffer cmd)
    154.     {
    155.         if(cmd == null)
    156.         {
    157.             throw new System.ArgumentNullException("cmd");
    158.         }
    159.  
    160.         mainRender = null;
    161.     }
    162. }
    In its current state, the Frame Debugger reports that the destination texture properly changes resolution at each step (where it never changes from the original if I attempt to use RTHandle[] with ConfigureTarget() during Execute() instead). However, the Material/Shader's "Texture Inputs" describe as [half resolution] for the two custom ones and [4x4] for the "_MainTex" input ("blurPassMainHandles", when passed into BlitTexture()).

    Basically, my question(s) is(are):

    Am I simply overlooking something? Do I need to do something different to assign the additional textures for each subsequent pass for this multiple-input-and-output process?

    It seems like this is only barely not working as-is (which *IS* the case, considering that using RTHandle[] and ConfigureClear() to execute a single pass a single time is working here), but it also seems rather odd that the Render Target seems correctly identified (correct dimensions) where the "source" for Blitter.BlitTexture() keeps reporting as 4x4 (so it's presumably not loading in correctly to begin with).

    Edit: Another thought occured to me... Many classes in Unity's Github listings for URP don't seem to use ScriptableRenderContext.ExecuteCommandBuffer() very aggressively. It could be related to when the passes are used in normal rendering stages rather than as Renderer Features (I plan to look into this more tomorrow). But, while I don't know what to try and pursue yet, I may as well see if its usage would have any relation to this.
     
    Last edited: Nov 27, 2022
  2. Eno-Khaon

    Eno-Khaon

    Joined:
    Oct 31, 2013
    Posts:
    10
    Well, the [4x4] _MainTex was thoroughly my own fault. In some of the changes I made up to this point, I was never fully reassigning it as the active material's mainTexture. Nevertheless, since I couldn't figure out a way around the general problems of selecting the correct textures actively, I've decided to cut my losses and change it back to a single (in this case horizontal) blur pass.