SRP Full Screen Blit Help

Dear Unity community,

When trying to use CommandBuffer.Blit in Single Pass Instanced VR mode I get this incorrect result


(Code at bottom of post)
I have tried all the suggestions in the doc about adding support to the shader for GPU instancing and add the defs to support texture arrays which did not help. I found that calling ScriptableRenderPass.Blit or CommandBuffer.Blit in any way causes the issue, even if it doesn’t blit into the CameraTargetColorTarget (back buffer?), for example,

this.Blit(cmd, m_textureResult.Identifier(), m_texture2Result.Identifier())
I have since move on to try this tutorial

How to perform a full screen blit in Single Pass Instanced rendering in XR | Universal RP | 12.1.7 (unity3d.com)

This leads to more success, however, then doesn’t render any objects with Transparent materials.

Without feature:

With Feature:

Using SRP is a new area for me so I apologies for my lack of in-depth knowledge/explanations. Please help me with a solution for simple full screen SRP blitting that works for both VR and NoneVR Single Instanced (Multi View works with first solution already)?

Thanks!

Code for first attempt:

public class FadeSRP : ScriptableRendererFeature
{
    public bool Enabled = false;

    [Serializable]
    public class FadeSettings
    {
        public RenderPassEvent renderPassEvent = RenderPassEvent.AfterRenderingTransparents;
        public Material EffectMaterial;
    }

    private FadePass m_fadePass;  
    public FadeSettings Settings;

    public override void Create()
    {
        if(Settings.EffectMaterial == null)
        {
            Debug.LogWarning("Fade Shader null");
            return;
        }

        m_fadePass = new FadePass(Settings.EffectMaterial, Settings.renderPassEvent);
    }

    public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
    {
        if (renderingData.cameraData.cameraType == CameraType.Game)
        {  
            if (Settings.EffectMaterial != null && Enabled)
            {
                // Calling ConfigureInput with the ScriptableRenderPassInput.Color argument ensures that the opaque texture is available to the Render Pass
                m_fadePass.ConfigureInput(ScriptableRenderPassInput.Color);
                m_fadePass.Setup(renderer.cameraColorTarget);
                renderer.EnqueuePass(m_fadePass);
            }
        }
    }

    public void SetFadeDelta(float delta)
    {
        m_fadePass.SetDelta(delta);
    }
}
public class FadePass : ScriptableRenderPass
{
    private readonly Material m_effectMaterial;
    private RenderTargetIdentifier m_cameraTarget;  
    private const string TagName = "FadePass";

    public FadePass(Material effectMaterial, RenderPassEvent renderPassEvent)
    {
        this.renderPassEvent = renderPassEvent;
        m_effectMaterial = effectMaterial;
    }

    public void Setup(RenderTargetIdentifier cameraTarget)
    {
        m_cameraTarget = cameraTarget;
    }

    public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
    {
        CommandBuffer cmd = CommandBufferPool.Get(TagName);
        cmd.Clear();

        this.Blit(cmd, ref renderingData, m_effectMaterial, 0);

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

    public void SetDelta(float delta)
    {
        m_effectMaterial.SetFloat("_Delta", delta);
    }
}
Shader "SRP/SRPFade"
{
    Properties
    {
        _MainTex("Texture", any) = "" {}
        _Color("Multiplicative color", Color) = (1.0, 1.0, 1.0, 1.0)
        _Delta("Delta", Float) = 0
    }
    SubShader{
        Pass {
            ZTest Always Cull Off ZWrite Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_instancing
            #include "UnityCG.cginc"

            UNITY_DECLARE_SCREENSPACE_TEXTURE(_MainTex);
            uniform float4 _MainTex_ST;
            uniform float _Delta;

            struct appdata_t {
                float4 vertex : POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
            };

            struct v2f {
                float4 vertex : SV_POSITION;
                float2 texcoord : TEXCOORD0;
                UNITY_VERTEX_INPUT_INSTANCE_ID
                UNITY_VERTEX_OUTPUT_STEREO
            };

            UNITY_INSTANCING_BUFFER_START(Props)
                UNITY_DEFINE_INSTANCED_PROP(float4, _Color)
            UNITY_INSTANCING_BUFFER_END(Props)

            v2f vert(appdata_t v)
            {
                v2f o;
                UNITY_SETUP_INSTANCE_ID(v);
                UNITY_TRANSFER_INSTANCE_ID(v, o);
                UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.texcoord = TRANSFORM_TEX(v.texcoord.xy, _MainTex);
                return o;
            }

            fixed4 frag(v2f i) : SV_Target
            {
                UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(i);
                UNITY_SETUP_INSTANCE_ID(i);
                float4 rawColor = UNITY_SAMPLE_SCREENSPACE_TEXTURE(_MainTex, i.texcoord);
                return lerp(rawColor, _Color, _Delta);
            }
            ENDCG

        }
    }
    Fallback Off
}

Look through the ongoing blit forum post for all quests regarding CommandBuffer.Blit

Thanks for the reply @daneobyrd . I had previously done that. However, lacked the knowledge to fully understand everything. However, after much learning and help I know have a working solution!

A big shout out to Cian Noonan in the Unity Support team for all his help educating me :slight_smile:

To summaries what I learned: Blitting in XR is a mixed bag of results in Unity3D 2021. Especially, when using Single Instance Rendering. A warning I recently found on the URP XR blit page:

“Using cmd.Blit might implicitly enable or disable XR shader keywords, which breaks XR SPI rendering.”

To my best deduction this is because URP is mid way through a transition to the new Blitter.Blit which uses RTHandles rather than RenderTargetIdentifiers in 2021.

The first part of the solution is to use DrawMesh instead of Blit:

cmd.SetRenderTarget(new RenderTargetIdentifier(m_cameraColorCopy, 0, CubemapFace.Unknown, -1));
cmd.DrawMesh(RenderingUtils.fullscreenMesh, Matrix4x4.identity, m_copyMaterail);

The second part was to update my shaders to support texture arrays using TEXTURE2D_X and SAMPLE_TEXTURE2D_X.

TEXTURE2D_X(_CameraColorCopy);
SAMPLER(sampler_CameraColorCopy);
...
float4 color = SAMPLE_TEXTURE2D_X(_CameraColorCopy, sampler_CameraColorCopy, input.uv);

I have attached the 3 complete files for clarity

8259882–1081017–ColorBlitRendererFeature.cs (1.34 KB)
8259882–1081020–ColorBlitPass.cs (2.54 KB)
8259882–1081023–ColorBlit.shader (1.94 KB)
8259882–1081026–SRPCopy.shader (1.86 KB)

2 Likes

That’s great news! Yeah, that SAMPLE_TEXTURE2D_X macro eluded me for a while when I first started looking into URP blitting. My situation back then was that I was adapting an HDRP compute shader to work in URP and hadn’t noticed the #pragma multi-compile UNITY_TEXTURE2D_X_ARRAY_SUPPORTED which kept giving me errors.

I hoped they have improved the documentation about those XR macros.

Was anyone able to get this to work with transparent materials as well? I copied the code from the Unity URP 12.1 code but it does not display any transparencies.

Thanks for your message, I was also running into trouble… spent many hours figuring out why it just didn’t work. Changing it to opaque immediately makes it appear. I tried a lot with rendering orders and when to call the blit pass, but nothing works so far with transparent.

The “_CameraOpaqueTexture” is copied after opaque objects rendering, so when you use them in a post-processing shader, all transparent objects will not be considered.

I believe that this texture is designed to be used in transparent shaders or post-processing that happens right after opaque rendering.

If you want to access scene color in post-processing effects, a better way is to copy the current scene color and then pass it to your shader.

How would I do this? Asking for a friend and not totally for me ;). Any articles would be super helpful. I am good at C# but not at shaders at all.

This can be done in the same custom renderer feature.

  • Create a temporary render texture. (usually use HDR format like B10G11R11)
  • Use blit function to copy the scene color. (blit source is camera.“color target”, destination is the temporary RT)
  • Now the temporary RT will contain current scene color.
  • Pass this RT to the post-processing material or shader. (can be set material’s property / global texture)
  • Execute the post-processing code.
  • Release the temporary RT if needed.

However, there’re some changes to the Blit and Temporary RT parts in different URP versions. (cmd.Blit, Blitter, RTHandle, RenderTargetIdentifier, RT.GetTemporary)

I am looking forward to any articles or examples too.

I just found that things can be very easy if you use the full screen pass feature in 2022.2.

The requirements option:

  • Depth: Depth Texture
  • Normal: Normals Texture (like SSAO’s)
  • Color: Not Opaque Texture, but a “CopyColor” right before your effect.

8628603--1159602--FullScreenPass_CopyColorjpg.jpg

You can also check it in Frame Debugger.
8628603--1159605--FrameDebugger_CopyColor.jpg

1 Like

Thanks so much for the detailed update @wwWwwwW1 . I am using XR which had a warning somewhere so I’ll have to figure out if this works with XR by Unity. Thanks!

1 Like

I suggest reporting it as a bug if not, and they should fix this as it’s an URP feature.

What I currently found was that it doesn’t support adding multiple features, but I only tested this on 2023.1.a20.

@wwWwwwW1 it was the warning on this page

https://docs.unity3d.com/Packages/com.unity.render-pipelines.universal@15.0/manual/renderer-features/how-to-fullscreen-blit.html

So I think this is a known limitation of URP and XR.

I think the full screen pass (0-code) feature I mentioned in #10 should support XR, because the Blit method is controlled by URP internally.

By the way, it seems that the Pass Index in the picture doesn’t do anything currently, it’s always a “Draw Procedure” in Frame Debugger.

If you would like to write custom renderer features, you can try using Blitter() rather than cmd.Blit().