HDRP: Reading from _ColorTextureMS inside CustomPass

I’m still new to what can be done with CustomPasses in HDRP, and I’m trying to read the pixel-fragment values from _ColorTextureMS.

I was looking at the code found in Graphics/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/MSAA/ColorResolve.shader at v7.5.1 · Unity-Technologies/Graphics · GitHub to see how they did this. I threw the code into a shader (using posInput.positionSS as a substitute for texCoord) that I’m passing to the FullScreenShaderPass, but when I pull the values, all I get are zero values resulting in a black screen.

The shader is compiling with zero errors or warnings. I’ve set things to Forward Rendering. I’ve checked and the image is rendering with MSAA (at X4) when the CustomPass isn’t enabled. I also make sure that there isn’t any other kind of Anti-aliasing going on. I checked the Frame Debugger Window, and because of this, I know that the Custom Pass is running after the call to ResolveMSAAColor.

Like I said, I’m still new to working with CustomPasses and the documentation that Unity has isn’t really that helpful for people that want (or need) to learn this.

Here’s the shader so far. I haven’t really done anything with what I’m values I’m pulling because I actually need to pull the values first.

Shader "FullScreen/AlterMSAA"
{
    HLSLINCLUDE

    #pragma vertex Vert

    #pragma target 4.5
    #pragma only_renderers d3d11 ps4 xboxone vulkan metal switch

    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"

    // The PositionInputs struct allow you to retrieve a lot of useful information for your fullScreenShader:
    // struct PositionInputs
    // {
    //     float3 positionWS;  // World space position (could be camera-relative)
    //     float2 positionNDC; // Normalized screen coordinates within the viewport    : [0, 1) (with the half-pixel offset)
    //     uint2  positionSS;  // Screen space pixel coordinates                       : [0, NumPixels)
    //     uint2  tileCoord;   // Screen tile coordinates                              : [0, NumTiles)
    //     float  deviceDepth; // Depth from the depth buffer                          : [0, 1] (typically reversed)
    //     float  linearDepth; // View space Z coordinate                              : [Near, Far]
    // };

    // To sample custom buffers, you have access to these functions:
    // But be careful, on most platforms you can't sample to the bound color buffer. It means that you
    // can't use the SampleCustomColor when the pass color buffer is set to custom (and same for camera the buffer).
    // float4 SampleCustomColor(float2 uv);
    // float4 LoadCustomColor(uint2 pixelCoords);
    // float LoadCustomDepth(uint2 pixelCoords);
    // float SampleCustomDepth(float2 uv);

    // There are also a lot of utility function you can use inside Common.hlsl and Color.hlsl,
    // you can check them out in the source code of the core SRP package.

    TEXTURE2D_X_MSAA(float4, _ColorTextureMS);

    float4 LoadColorTextureMS(float2 pixelCoords, uint sampleIndex)
    {
        return LOAD_TEXTURE2D_X_MSAA(_ColorTextureMS, pixelCoords, sampleIndex);
    }

    float4 FullScreenPass(Varyings varyings) : SV_Target
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
        float depth = LoadCameraDepth(varyings.positionCS.xy);
        PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
        float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
        float4 color = float4(0.0, 0.0, 0.0, 0.0);

        // Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
        if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
            color = float4(CustomPassLoadCameraColor(varyings.positionCS.xy, 0), 1);

        // Add your custom pass code here
        int2 pixelCoords = int2(posInput.positionSS);
        float4 msaa0 = FastTonemapInvert(FastTonemap(LoadColorTextureMS(pixelCoords, 0)));
        float4 msaa1 = LoadColorTextureMS(pixelCoords, 1);
        float4 msaa2 = LoadColorTextureMS(pixelCoords, 2);
        float4 msaa3 = LoadColorTextureMS(pixelCoords, 3);

        return msaa1;


        /*float4 msaaStuff = FastTonemapInvert((FastTonemap(LoadColorTextureMS(pixelCoords, 0))
                                             + FastTonemap(LoadColorTextureMS(pixelCoords, 1))
                                             + FastTonemap(LoadColorTextureMS(pixelCoords, 2))
                                             + FastTonemap(LoadColorTextureMS(pixelCoords, 3))) * 0.25f);*/


        // Fade value allow you to increase the strength of the effect while the camera gets closer to the custom pass volume
       // float f = 1 - abs(_FadeValue * 2 - 1);

        //return float4(color.rg, 0.5, color.a);
        //return float4(color.rgb + f, color.a);
    }

    ENDHLSL

    SubShader
    {
        Pass
        {
            Name "Custom Pass 0"

            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment FullScreenPass
            ENDHLSL
        }
    }
    Fallback Off
}

So, I figured it out.

It turns out that I had failed to set the MSAA RenderTexture. Even worse, it’s a real pain to get my hands on said RenderTexture.

  • Create a CustomPass that’s designed to do nothing except to grab the MSAA RenderTexture.
  • Run the MSAA GrabPass inside a Custom Pass Volume with an Injection Point of “After Opaque Depth And Normal” or “Before Transparent”. It has to be one of those two, anything else will grab the non-MSAA-RenderTexture.
  • Use the GetCameraBuffers(out RTHandle msaaColor, out RTHandle depth) method from CustomPass to get your hands on the RTHandles (I think the depth will also be MSAA, but I didn’t check because I didn’t need it).
  • Get the RenderTexture from msaaColor.rt.

You might need to do things a bit differently if you’re targeting a Custom Texture instead of the Camera Texture, but for the camera’s color buffer, this works just fine. Now, if you need to use the MSAA Color Render Texture at a different injection point, then I would advise you to stash it inside of a static attribute, but do be careful with changes to the MSAA level, those will invalidate the current MSAA Color Render Texture.

Also, be careful when playing around with the clear flags; I was playing around with them and the GPU jumped to 100% usage and the Editor stopped responding. I had to shut down the Unity Editor from the Task Manager to get a handle on things.

1 Like

I’ve searched through many a forum and read many an article trying to find an applicable solution to HDR and MSAA resolving in the URP on VR.

https://github.com/Unity-Technologi…rPipeline/RenderPass/MSAA/ColorResolve.shader

Is exactly what I need to be integrated into the URP at the point where it draws renderers. Tonemapping each sample before the filter and then reversing that tonemap will fix AntiAliasing issues with HDR and MSAA. Just gotta wait for the support now I guess, I am not about to re-write the entire rendering system >.<