Unity Web Shader IDE
Full source <URP>/Shaders/PostProcessing/CameraMotionBlur.shader ShaderLab / HLSL 229 lines 68 symbols 4 includes Hide source Show source
CameraMotionBlur.shader <URP>/Shaders/PostProcessing/CameraMotionBlur.shader
Language
ShaderLab / HLSL
Lines
229
Symbols
68
Includes
4
Open IDE root

<URP>/Shaders/PostProcessing/CameraMotionBlur.shader full source

Shader "Hidden/Universal Render Pipeline/CameraMotionBlur"
{
    HLSLINCLUDE
        #pragma vertex VertCMB
        #pragma fragment FragCMB
        #pragma multi_compile_fragment _ _ENABLE_ALPHA_OUTPUT

        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"
        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Random.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
        #include "Packages/com.unity.render-pipelines.core/Runtime/Utilities/Blit.hlsl"
        #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DeclareDepthTexture.hlsl"

#if defined(USING_STEREO_MATRICES)
            float4x4 _ViewProjMStereo[2];
            float4x4 _PrevViewProjMStereo[2];
#define _ViewProjM _ViewProjMStereo[unity_StereoEyeIndex]
#define _PrevViewProjM  _PrevViewProjMStereo[unity_StereoEyeIndex]
#else
        float4x4 _ViewProjM;
        float4x4 _PrevViewProjM;
#endif
        half _Intensity;
        half _Clamp;
        half4 _SourceSize;

        TEXTURE2D_X(_MotionVectorTexture);

        struct VaryingsCMB
        {
            float4 positionCS    : SV_POSITION;
            float4 texcoord      : TEXCOORD0;
            UNITY_VERTEX_OUTPUT_STEREO
        };

        VaryingsCMB VertCMB(Attributes input)
        {
            VaryingsCMB output;
            UNITY_SETUP_INSTANCE_ID(input);
            UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(output);

            float4 pos = GetFullScreenTriangleVertexPosition(input.vertexID);
            float2 uv  = GetFullScreenTriangleTexCoord(input.vertexID);

            output.positionCS  = pos;
            output.texcoord.xy = DYNAMIC_SCALING_APPLY_SCALEBIAS(uv);

            float4 projPos = output.positionCS * 0.5;
            projPos.xy = projPos.xy + projPos.w;
            output.texcoord.zw = projPos.xy;

            return output;
        }

        half2 ClampVelocity(half2 velocity, half maxVelocity)
        {
            half len = length(velocity);
            return (len > 0.0) ? min(len, maxVelocity) * (velocity * rcp(len)) : 0.0;
        }

        half2 GetVelocity(float2 uv)
        {
            // Unity motion vectors are forward motion vectors in screen UV space
            half2 offsetUv = SAMPLE_TEXTURE2D_X(_MotionVectorTexture, sampler_LinearClamp, uv).xy;
            return -offsetUv;
        }

        // Per-pixel camera velocity
        half2 GetCameraVelocity(float4 uv)
        {
            #if UNITY_REVERSED_Z
                half depth = SampleSceneDepth(uv.xy).x;
            #else
                half depth = lerp(UNITY_NEAR_CLIP_VALUE, 1, SampleSceneDepth(uv.xy).x);
            #endif

            float4 worldPos = float4(ComputeWorldSpacePosition(uv.xy, depth, UNITY_MATRIX_I_VP), 1.0);

            float4 prevClipPos = mul(_PrevViewProjM, worldPos);
            float4 curClipPos = mul(_ViewProjM, worldPos);

            half2 prevPosCS = prevClipPos.xy / prevClipPos.w;
            half2 curPosCS = curClipPos.xy / curClipPos.w;

            // Backwards motion vectors
            half2 velocity = (prevPosCS - curPosCS);
            #if UNITY_UV_STARTS_AT_TOP
                velocity.y = -velocity.y;
            #endif
            return ClampVelocity(velocity, _Clamp);
        }

        half4 GatherSample(half sampleNumber, half2 velocity, half invSampleCount, float2 centerUV, half randomVal, half velocitySign)
        {
            half  offsetLength = (sampleNumber + 0.5h) + (velocitySign * (randomVal - 0.5h));
            float2 sampleUV = centerUV + (offsetLength * invSampleCount) * velocity * velocitySign;
            return SAMPLE_TEXTURE2D_X(_BlitTexture, sampler_PointClamp, sampleUV);
        }

        half4 DoMotionBlur(VaryingsCMB input, int iterations, int useMotionVectors)
        {
            UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(input);

            float2 uv = UnityStereoTransformScreenSpaceTex(input.texcoord.xy);

            half2 velocity;
            if(useMotionVectors == 1)
            {
                velocity = GetVelocity(uv) * _Intensity;
                // Scale back to -1, 1 from 0..1 to match GetCameraVelocity. A workaround to keep existing visual look.
                // TODO: There's bug in GetCameraVelocity, which is using NDC and not UV
                velocity *= 2;
            }
            else
                velocity = GetCameraVelocity(float4(uv, input.texcoord.zw)) * _Intensity;

            half randomVal = InterleavedGradientNoise(uv * _SourceSize.xy, 0);
            half invSampleCount = rcp(iterations * 2.0);

            half4 color = 0.0;

            UNITY_UNROLL
            for (int i = 0; i < iterations; i++)
            {
                color += GatherSample(i, velocity, invSampleCount, uv, randomVal, -1.0);
                color += GatherSample(i, velocity, invSampleCount, uv, randomVal,  1.0);
            }

            #if _ENABLE_ALPHA_OUTPUT
                return color * invSampleCount;
            #else
                  // NOTE: Rely on the compiler to eliminate .w computation above
                return half4(color.xyz * invSampleCount, 1.0);
            #endif
        }
    ENDHLSL

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline"}
        LOD 100
        ZTest Always ZWrite Off Cull Off

        Pass
        {
            Name "Camera Motion Blur - Low Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 2, 0);
                }

            ENDHLSL
        }

        Pass
        {
            Name "Camera Motion Blur - Medium Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 3, 0);
                }

            ENDHLSL
        }

        Pass
        {
            Name "Camera Motion Blur - High Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 4, 0);
                }

            ENDHLSL
        }

        Pass
        {
            Name "Camera And Object Motion Blur - Low Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 2, 1);
                }

            ENDHLSL
        }

        Pass
        {
            Name "Camera And Object Motion Blur - Medium Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 3, 1);
                }

            ENDHLSL
        }

        Pass
        {
            Name "Camera And Object Motion Blur - High Quality"

            HLSLPROGRAM

                half4 FragCMB(VaryingsCMB input) : SV_Target
                {
                    return DoMotionBlur(input, 4, 1);
                }

            ENDHLSL
        }
    }
}