Actually I don't know whether it is a bug or not.

I’m writing a shader to split an image horizontally into two pieces and offset the upper half by _Offset.

I set the tilling and offset of the texture to (2, 2, -0.5, -0.5), so I could scale the image down by 0.5 and center it in the middle of the plane. I set the wrap mode of this texture to Clamp and cropped it a little bit.

The result looks like this, a strange horizontal line appears in the middle of the plane.
7065391--839545--2021422-171705.jpg

And this is what I expected.
7065391--839551--2021422-174130.jpg

Here is the shader I am using, the upper fragment is the correct one, but it samples two times and might have some extra performance cost when I want to have more slices.

Shader "Unlit/SimpleShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _Offset ("Offset", Range(-1, 1)) = 0.5
        [Toggle(_USE_CORRECT_FRAGMENT_SHADER)] _UseCorrect ("Use Correct Shader", float) = 0
    }

    HLSLINCLUDE
    #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    #pragma multi_compile _ _USE_CORRECT_FRAGMENT_SHADER
         
    sampler2D _MainTex;
    float4 _MainTex_ST;
    float _Offset;
    float _UseCorrect;


    struct Attributes
    {
        float4 positionOS   : POSITION;
        float2 uv           : TEXCOORD0;
    };

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

    Varyings vert(Attributes input)
    {
        Varyings output = (Varyings)0;
        VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
        output.positionCS = vertexInput.positionCS;
        output.texcoord = input.uv * _MainTex_ST.xy + _MainTex_ST.zw;
        return output;
    }

#if _USE_CORRECT_FRAGMENT_SHADER
    float4 frag(Varyings input) : SV_Target
    {
        float2 uvOffset = input.texcoord;
        float4 mainTex = tex2D(_MainTex, uvOffset);
        if (input.texcoord.y >= 0.5)
        {
            uvOffset.x += _Offset;
            mainTex = tex2D(_MainTex, uvOffset);
        }
        return mainTex;
    }
#else
    float4 frag(Varyings input) : SV_Target
    {
        float2 uvOffset = input.texcoord;
        if (input.texcoord.y >= 0.5)
        {
            uvOffset.x += _Offset;
        }
        float4 mainTex = tex2D(_MainTex, uvOffset);
        return mainTex;
    }
#endif

    ENDHLSL

    SubShader
    {
        Tags{ "Queue" = "Transparent" }

        Pass
        {
            Blend SrcAlpha OneMinusSrcAlpha
            ZWrite Off

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            ENDHLSL
        }
    }
}

Is it a bug related to branches in unity?
How should I fix it if I only want to make one sample?
I am using Unity 2020.3.2f1, DX11, Universal Render Pipeline. Haven’t tested on other platforms yet.

Thanks in advance.

I just found out that uncheck “Generate Mip Maps” in Texture Import Settings can get rid of that horizontal line.

Yep. The mip map level used for rendering by the GPU is calculated by looking at the UV change (derivatives) in each 2x2 pixel area of the screen. If the UV changes significantly within that group of pixels, the GPU thinks it should be a lower mip level (and would be correct most of the time). Modifying the UV like you’re doing means it’s possible for the split to be in the middle of that 2x2 group, and thus you get what you’re seeing in the first example.

Disabling mip maps is certainly an option, especially if this is for UI where it’s unlikely you’ll ever need a lower mip level. Alternatively you can use tex2Dlod() to enforce the top mip level, or tex2Dgrad() to use the ddx() and ddy() derivative values from the UV prior to “slicing”. If you search the forums you can find examples of using either of those.

But, again, disabling mip maps for this case is likely totally fine.

1 Like

Thanks for your detailed and clear response!