'Something goes weird with my decals when I change FOV

I'm on v2019.4.30f1, am using the old render pipeline (i.e. not urp nor hdrp) and using the blood decals from this pack: https://assetstore.unity.com/packages/vfx/particles/volumetric-blood-fluids-173863

The blood decals are done at runtime, like when a play dies some blood spurts out and the decal is created.

Here is how the decal looks at FOV 20, and this is good, how I expect it to look:

enter image description here

Next if I merely change the camera FOV to 15 you'll see something goes really weird with the decal. There are parts that look like they are in the wrong place, almost like some parts have been shifted either up or left or whatever and some parts have been clipped off. But then other parts seems like they are still in the correct place:

enter image description here

If I switch back to FOV 20 again it looks normal again, so it doesn't seem like anything is changing with the decal itself when I change FOV, just that viewed from different FOV it looks completely different.

Here is a screenshot from scene view, showing the decal as well as some inspector info about it:

enter image description here

I've been tearing my hair out this entire week over this and can't seem to figure it out :/ Any advice would be much appreciated. Of course I can provide any other info required if there isn't enough to go on here.

edit: here is the decal shader

Shader "KriptoFX/BFX/BFX_Decal"
{
    Properties
    {
        [HDR] _TintColor("Tint Color", Color) = (1,1,1,1)
        _MainTex("NormalAlpha", 2D) = "white" {}
        _LookupFade("Lookup Fade Texture", 2D) = "white" {}
        _Cutout("Cutout", Range(0, 1)) = 1
        _CutoutTex("CutoutDepth(XZ)", 2D) = "white" {}
    [Space]
        _SunPos("Sun Pos", Vector) = (1, 0.5, 1, 0)
    }


    SubShader
    {
        Tags{ "Queue" = "AlphaTest"}
        Blend DstColor SrcColor
        //Blend SrcAlpha OneMinusSrcAlpha
        Cull Front
        ZTest Always
        ZWrite Off

        Pass
        {

            CGPROGRAM
                #pragma vertex vert
                #pragma fragment frag

                #pragma multi_compile_fog
                #pragma multi_compile_instancing
                #pragma multi_compile _ USE_CUSTOM_DECAL_LAYERS

                #include "UnityCG.cginc"

                sampler2D _MainTex;
                sampler2D _Flowmap;
                sampler2D _LookupFade;
                sampler2D _CutoutTex;

                float4 _MainTex_ST;
                float4 _MainTex_NextFrame;
                float4 _CutoutTex_ST;

                UNITY_INSTANCING_BUFFER_START(Props)
                    UNITY_DEFINE_INSTANCED_PROP(half4, _TintColor)
                    UNITY_DEFINE_INSTANCED_PROP(half, _Cutout)
                    UNITY_DEFINE_INSTANCED_PROP(float, _LightIntencity)
                UNITY_INSTANCING_BUFFER_END(Props)


                half4 _CutoutColor;
                half4 _FresnelColor;
                half4 _DistortionSpeedScale;

                sampler2D _CameraDepthTexture;
                sampler2D _LayerDecalDepthTexture;
                half InterpolationValue;
                half _AlphaPow;
                half _DistortSpeed;
                half _DistortScale;
                float4 _SunPos;
                half _DepthMul;

                struct appdata_t {
                    float4 vertex : POSITION;
                    float4 normal : NORMAL;
                    half4 color : COLOR;
                    UNITY_VERTEX_INPUT_INSTANCE_ID
                };

                struct v2f {
                    float4 vertex : SV_POSITION;
                    half4 color : COLOR;

                    float4 screenUV : TEXCOORD0;
                    float3 ray : TEXCOORD1;
                    float3 viewDir : TEXCOORD2;
                    float4 screenPos : TEXCOORD3;

                    UNITY_FOG_COORDS(4)

                    UNITY_VERTEX_INPUT_INSTANCE_ID
                    UNITY_VERTEX_OUTPUT_STEREO
                };


                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.color = v.color;

                    o.ray = UnityObjectToViewPos(v.vertex) * float3(-1, -1, 1);
                    o.screenUV = ComputeScreenPos(o.vertex);
                    o.viewDir = normalize(ObjSpaceViewDir(v.vertex));
                    o.screenPos = ComputeGrabScreenPos(o.vertex);
                    UNITY_TRANSFER_FOG(o,o.vertex);

                    return o;
                }


                half4 frag(v2f i) : SV_Target
                {
                    UNITY_SETUP_INSTANCE_ID(i);


                    i.ray *= (_ProjectionParams.z / i.ray.z);

#if USE_CUSTOM_DECAL_LAYERS
                    float depth = Linear01Depth(tex2Dproj(_LayerDecalDepthTexture, i.screenUV));
                    float depthMask = Linear01Depth(tex2Dproj(_CameraDepthTexture, i.screenUV));
                    float fade = 1- saturate(100000 * (depth - depthMask));

#else
                    float depth = Linear01Depth(tex2Dproj(_CameraDepthTexture, i.screenUV));
#endif

                    float3 wpos = mul(unity_CameraToWorld, float4(i.ray * depth, 1)).xyz;
                    float3 opos = mul(unity_WorldToObject, float4(wpos, 1)).xyz;

                    float3 stepVal = saturate((0.5 - abs(opos.xyz)) * 10000);
                    half lookupHeight = tex2D(_LookupFade, float2(opos.y + 0.5, 0));

                    float projClipFade = stepVal.x * stepVal.y * stepVal.z * lookupHeight;
#if USE_CUSTOM_DECAL_LAYERS
                    projClipFade *= fade;
#endif
                    float2 uv = opos.xz + 0.5;
                    float2 uvMain = uv * _MainTex_ST.xy + _MainTex_ST.zw;
                    float2 uvCutout = (opos.xz + 0.5) * _CutoutTex_ST.xy + _CutoutTex_ST.zw;

                    half4 normAlpha = tex2D(_MainTex, uvMain);
                    half4 res = 0;
                    res.a = saturate(normAlpha.w * 2);
                    if (res.a < 0.1) discard;

                    normAlpha.xy = normAlpha.xy * 2 - 1;
                    float3 normal = normalize(float3(normAlpha.x, 1, normAlpha.y));

                    half3 mask = tex2D(_CutoutTex, uvCutout).xyz;
                    half cutout = 0.5 + UNITY_ACCESS_INSTANCED_PROP(Props, _Cutout) * i.color.a * 0.5;

                    half alphaMask = saturate((mask.r - (cutout * 2 - 1)) * 20) * res.a;
                    half colorMask = saturate((mask.r - (cutout * 2 - 1)) * 5) * res.a;
                    res.a = alphaMask;
                    res.a = saturate(res.a * projClipFade);


                    float intencity = UNITY_ACCESS_INSTANCED_PROP(Props, _LightIntencity);
                    float light = max(0.001, dot(normal, normalize(_SunPos.xyz)));
                    light = pow(light, 150) * 3 * intencity;
                    light *= (1 - mask.z * colorMask);

                    float4 tintColor = UNITY_ACCESS_INSTANCED_PROP(Props, _TintColor);
                    #if !UNITY_COLORSPACE_GAMMA
                            tintColor = tintColor * 1.35;
                    #endif
                    res.rgb = lerp(tintColor.rgb, tintColor.rgb * 0.25, mask.z * colorMask) + light;


                    half fresnel = (1 - dot(normal, normalize(i.viewDir)));
                    fresnel = pow(fresnel + 0.1, 5);

                    UNITY_APPLY_FOG_COLOR(i.fogCoord, res, half4(1, 1, 1, 1));
                    return lerp(0.5, res, res.a);

                    return res;
                }

            ENDCG
    }

    }

}


Solution 1:[1]

Comparing two pictures, I see that BFXShaderAnamation does some precomputation in the beginning of animation, and the results depend on camera settings and geometry. You should look for the code that does this initialization and call it again each time your camera settings or geometry change.

Solution 2:[2]

Idea: check if the decal plugin creates an additional hidden camera to do some computations (look into the hierarchy and search by "Camera" type).

How to search for cameras

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Pavelko
Solution 2 Horea Trinca