Shaking Custom Post Processing in HDRP

Hello there,
I’m currently trying to make a outline post processing shader in HDRP.
I roughly follow the article from VertexFragment (Sobel Outline with Unity Post-Processing · Vertex Fragment) and it works (mostly), but I have this wierd issue, where the outline jumpes around all the time. Its really distracting. Additionally it only happens in game view, not in scene view.

I think it has something to do with the depth and GBuffer2 texture, since a different effect (also using the depth texture) has a similar problem.

To my testing enviroment: Sample HDRP-Scene and a simple post processing script that displays the parameters.

Perhaps someone can help me with that issue.

Shader:

/* Shader based on:
* https://www.vertexfragment.com/ramblings/unity-postprocessing-sobel-outline/
*/

Shader "Hidden/Shader/OutlineShader"
{
    HLSLINCLUDE

    #pragma target 4.5
    #pragma only_renderers d3d11 playstation xboxone xboxseries vulkan metal switch

    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
    #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/ShaderLibrary/ShaderVariables.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/FXAA.hlsl"
    #include "Packages/com.unity.render-pipelines.high-definition/Runtime/PostProcessing/Shaders/RTUpscale.hlsl"

    struct Attributes
    {
        uint vertexID : SV_VertexID;
        UNITY_VERTEX_INPUT_INSTANCE_ID
    };

    struct Varyings
    {
        float4 positionCS : SV_POSITION;
        float2 texcoord   : TEXCOORD0;
        UNITY_VERTEX_OUTPUT_STEREO
    };

    Varyings Vert(Attributes input)
    {
        Varyings output;
        UNITY_SETUP_INSTANCE_ID(input);
        UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);
        output.positionCS = GetFullScreenTriangleVertexPosition(input.vertexID);
        output.texcoord = GetFullScreenTriangleTexCoord(input.vertexID);
        return output;
    }

    // List of properties to control your post process effect
    float _Intensity;
    float _OutlineThickness;
    float _OutlineDepthMultiplier;
    float _OutlineDepthBias;
    float _OutlineNormalMultiplier;
    float _OutlineNormalBias;

    float3 _OutlineColor;

    TEXTURE2D_X(_InputTexture);  
    TEXTURE2D_X(_GBufferTexture1);

    float SobelDepth(float center, float left, float right, float up, float down) {
      
        return abs(left - center) + abs(right - center) + abs(up - center) + abs(down - center);
    }

    float SobelSampleDepth(float2 uv, float3 offset) {
        float pixelCenter = LinearEyeDepth(LOAD_TEXTURE2D_X(_CameraDepthTexture, uv).r, _ZBufferParams);
        float pixelLeft = LinearEyeDepth(LOAD_TEXTURE2D_X(_CameraDepthTexture, uv - offset.xz).r, _ZBufferParams);
        float pixelRight = LinearEyeDepth(LOAD_TEXTURE2D_X(_CameraDepthTexture, uv + offset.xz).r, _ZBufferParams);
        float pixelUp = LinearEyeDepth(LOAD_TEXTURE2D_X(_CameraDepthTexture, uv + offset.zy).r, _ZBufferParams);
        float pixelDown = LinearEyeDepth(LOAD_TEXTURE2D_X(_CameraDepthTexture, uv - offset.zy).r, _ZBufferParams);

        return SobelDepth(pixelCenter, pixelLeft, pixelRight, pixelUp, pixelDown);
    }

    float4 SobelNormal(float2 uv, float3 offset)
    {
        float4 pixelCenter = LOAD_TEXTURE2D_X(_GBufferTexture1, uv);
        float4 pixelLeft = LOAD_TEXTURE2D_X(_GBufferTexture1, uv - offset.xz);
        float4 pixelRight = LOAD_TEXTURE2D_X(_GBufferTexture1, uv + offset.xz);
        float4 pixelUp = LOAD_TEXTURE2D_X(_GBufferTexture1, uv + offset.zy);
        float4 pixelDown = LOAD_TEXTURE2D_X(_GBufferTexture1, uv - offset.zy);

        return abs(pixelLeft - pixelCenter) + abs(pixelRight - pixelCenter) + abs(pixelUp - pixelCenter) + abs(pixelDown - pixelCenter);
    }

    float4 CustomPostProcess(Varyings input) : SV_Target
    {
        UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

        uint2 positionSS = input.texcoord * _ScreenSize.xy;
        //return float4(input.texcoord, 0, 1);
        float3 outColor = LOAD_TEXTURE2D_X(_InputTexture, positionSS).xyz;
      
        //return float4(LOAD_TEXTURE2D_X(_GBufferTexture1, positionSS).rgb, 1);

        float3 offset = float3((1.0 / _ScreenParams.x), (1.0 / _ScreenParams.y), 0.0) * _OutlineThickness;
        float sobelDepth = SobelSampleDepth(positionSS, offset);
        sobelDepth = pow(saturate(sobelDepth) * _OutlineDepthMultiplier, _OutlineDepthBias);
      
        float4 sobelNormalCalc = SobelNormal(positionSS, offset);
        float sobelNormal = sobelNormalCalc.x + sobelNormalCalc.y + sobelNormalCalc.z;
        sobelNormal = pow(sobelNormal * _OutlineNormalMultiplier, _OutlineNormalBias);
      
        float outline = saturate(max(sobelDepth, sobelNormal));

        float3 outlineColor = lerp(outColor, _OutlineColor, _Intensity);
        outColor = lerp(outColor, outlineColor, outline);



        return float4(outColor, 1);
    }

    ENDHLSL

    SubShader
    {
        Pass
        {
            Name "OutlineShader"

            ZWrite Off
            ZTest Always
            Blend Off
            Cull Off

            HLSLPROGRAM
                #pragma fragment CustomPostProcess
                #pragma vertex Vert
            ENDHLSL
        }
    }
    Fallback Off
}

Found A solution, but not a great one, tbh.

The shaking comes from the Anti-Ailasing. Specifically from Temporal Anti-Aliasing.
My solution is switching from TAA to anything else (None, FXAA, SMAA) on my main camera.

1 Like