Search Unity

Using 2 textures and mask from vertices

Discussion in 'Shaders' started by VictorKs, Feb 7, 2019.

  1. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    136
    So I have 1 object textured by 2 texArrays, I've stored tex Ids in a 4x4matrix and I use UV0 z/w to access the ids.
    Thing is I want the parts with UV0.w<2 to be textured from texArray 1 (is 1K resolution to save memory)

    I get some black artifacts is it a branching issue in the pixel shader?
    EDIT: OK I fixed it, the problem was a precision issue, 2 is stored as 1.999999 and 2.000001 on certain vertices.

    Code (CSharp):
    1. void surf (Input IN, inout SurfaceOutputStandardSpecular o){
    2.             float4x4 texIds=UNITY_ACCESS_INSTANCED_PROP(Props, _TexIds);      
    3.             float no=texIds[IN.texcoord.z][IN.texcoord.w];         //get layer No
    4.             float3 fUV=float3(IN.texcoord.x,IN.texcoord.y, no);
    5.  
    6.             if(IN.texcoord.w<2){   //texture from texArray1
    7.                 fixed4 c=UNITY_SAMPLE_TEX2DARRAY(_MainTex, fUV);
    8.                 o.Albedo = c.rgb;
    9.                 o.Alpha = 1;
    10.             }
    11.             else{         //texture from texArray2
    12.                 fixed4 c=UNITY_SAMPLE_TEX2DARRAY(_MainTex2, fUV);
    13.                 o.Albedo = c.rgb;
    14.                 o.Alpha = 1;
    15.             }
     
    Last edited: Feb 7, 2019
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    7,311
    The issue isn't the data on the vertices, the issue is interpolation and floating point math.

    The texcoord value you get in each pixel is a barycentric interpolated value from the 3 vertices of that triangle. Due to the fun of floating point, even if all 3 vertices have exactly the same value, at different points along that interpolation the value might be slightly higher, slightly lower, or exactly the value that the three vertices have.

    In the image below, all vertex shaders are outputting exactly "3.0", which is set on the shader itself. Areas that are green are exactly that value, red is less than that and blue is greater. Moving the camera around results in the noise changing just because the floating point math is changing. You can get it to be a solid color by having the quad facing the camera dead on, but it'll still randomly be all red, all green, or all blue.
    upload_2019-2-7_12-40-58.png
    Code (CSharp):
    1. Shader "Interpolation Error"
    2. {
    3.     Properties {
    4.         _Value ("Value", Float) = 3.0
    5.     }
    6.  
    7.     SubShader
    8.     {
    9.         Tags { "Queue"="Transparent" }
    10.      
    11.         Pass {
    12.  
    13.             CGPROGRAM
    14.             #pragma vertex vert
    15.             #pragma fragment frag
    16.  
    17.             #include "UnityCG.cginc"
    18.  
    19.             struct v2f
    20.             {
    21.                 float4 pos : SV_POSITION;
    22.                 // nointerpolation // uncomment this line to remove interpolation from the below value
    23.                 float value : TEXCOORD0;
    24.             };
    25.  
    26.             float _Value;
    27.  
    28.             v2f vert (appdata_full v)
    29.             {
    30.                 v2f o;
    31.                 o.pos = UnityObjectToClipPos(v.vertex);
    32.                 o.value = _Value;
    33.                 return o;
    34.             }
    35.  
    36.             float4 frag (v2f i) : SV_Target
    37.             {
    38.                 float4 col = float4(0,1,0,1);
    39.                 if (i.value < _Value)
    40.                     col = float4(1,0,0,1);
    41.                 else if (i.value > _Value)
    42.                     col = float4(0,0,1,1);
    43.                 return col;
    44.             }
    45.  
    46.             ENDCG
    47.         }
    48.     }
    49. }

    The usual solution is to store the integer values that need to be interpolated with a bit of a buffer, like 2.5 instead of 2.0. Alternatively you can use the nointerpolation modifier, but that's often less efficient use of the interpolants when you're only needing one value to not have floating point errors, and isn't something you can do in surface shaders.
     
    VictorKs likes this.
  3. VictorKs

    VictorKs

    Joined:
    Jun 2, 2013
    Posts:
    136
    Thanks for the in-depth explanation that explains why my vertex shaders work as expected even with equality comparisons, anyway I changed my expressions from <2 to <1.5 and everything works as expected. Thanks again!