Search Unity

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

Discussion in 'Shaders' started by zznewclear, Apr 22, 2021.

  1. zznewclear

    zznewclear

    Joined:
    Sep 4, 2019
    Posts:
    6
    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.
    2021422-171705.jpg

    And this is what I expected.
    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.
    Code (Boo):
    1. Shader "Unlit/SimpleShader"
    2. {
    3.     Properties
    4.     {
    5.         _MainTex ("Texture", 2D) = "white" {}
    6.         _Offset ("Offset", Range(-1, 1)) = 0.5
    7.         [Toggle(_USE_CORRECT_FRAGMENT_SHADER)] _UseCorrect ("Use Correct Shader", float) = 0
    8.     }
    9.  
    10.     HLSLINCLUDE
    11.     #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
    12.     #pragma multi_compile _ _USE_CORRECT_FRAGMENT_SHADER
    13.          
    14.     sampler2D _MainTex;
    15.     float4 _MainTex_ST;
    16.     float _Offset;
    17.     float _UseCorrect;
    18.  
    19.  
    20.     struct Attributes
    21.     {
    22.         float4 positionOS   : POSITION;
    23.         float2 uv           : TEXCOORD0;
    24.     };
    25.  
    26.     struct Varyings
    27.     {
    28.         float4 positionCS   : SV_POSITION;
    29.         float2 texcoord     : TEXCOORD0;
    30.     };
    31.  
    32.     Varyings vert(Attributes input)
    33.     {
    34.         Varyings output = (Varyings)0;
    35.         VertexPositionInputs vertexInput = GetVertexPositionInputs(input.positionOS.xyz);
    36.         output.positionCS = vertexInput.positionCS;
    37.         output.texcoord = input.uv * _MainTex_ST.xy + _MainTex_ST.zw;
    38.         return output;
    39.     }
    40.  
    41. #if _USE_CORRECT_FRAGMENT_SHADER
    42.     float4 frag(Varyings input) : SV_Target
    43.     {
    44.         float2 uvOffset = input.texcoord;
    45.         float4 mainTex = tex2D(_MainTex, uvOffset);
    46.         if (input.texcoord.y >= 0.5)
    47.         {
    48.             uvOffset.x += _Offset;
    49.             mainTex = tex2D(_MainTex, uvOffset);
    50.         }
    51.         return mainTex;
    52.     }
    53. #else
    54.     float4 frag(Varyings input) : SV_Target
    55.     {
    56.         float2 uvOffset = input.texcoord;
    57.         if (input.texcoord.y >= 0.5)
    58.         {
    59.             uvOffset.x += _Offset;
    60.         }
    61.         float4 mainTex = tex2D(_MainTex, uvOffset);
    62.         return mainTex;
    63.     }
    64. #endif
    65.  
    66.     ENDHLSL
    67.  
    68.     SubShader
    69.     {
    70.         Tags{ "Queue" = "Transparent" }
    71.  
    72.         Pass
    73.         {
    74.             Blend SrcAlpha OneMinusSrcAlpha
    75.             ZWrite Off
    76.  
    77.             HLSLPROGRAM
    78.             #pragma vertex vert
    79.             #pragma fragment frag
    80.             ENDHLSL
    81.         }
    82.     }
    83. }
    84.  
    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.
     
    Last edited: Apr 22, 2021
  2. zznewclear

    zznewclear

    Joined:
    Sep 4, 2019
    Posts:
    6
    I just found out that uncheck "Generate Mip Maps" in Texture Import Settings can get rid of that horizontal line.
     
  3. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,342
    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.
     
    zznewclear likes this.
  4. zznewclear

    zznewclear

    Joined:
    Sep 4, 2019
    Posts:
    6
    Thanks for your detailed and clear response!