Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

Question Problem with UV interpolation

Discussion in 'Shaders' started by kiselevsereja, Aug 19, 2022.

  1. kiselevsereja

    kiselevsereja

    Joined:
    Sep 22, 2019
    Posts:
    4
    Hello.
    I'm trying to make a procedurally generated river and a shader for it. I created a simple GetDirection() function that I call for each vertex of the river mesh. In it, I return the direction of the river for the given point. I'm passing the values returned by this function to the mesh's uv array.

    Code (CSharp):
    1. public Vector2 GetDirection(Vector2 position)
    2.     {
    3.         float minv = 0.0f;
    4.         Vector2 direction = Vector2.zero;
    5.  
    6.         for (int i = 0; i < Resolution; i++)
    7.         {
    8.             Vector2 P = _spline.GetPoint((float)i / (float)(Resolution - 1));
    9.            
    10.             Vector2 E = position;
    11.  
    12.             float reqAns = Vector2.Distance(E, P);
    13.  
    14.             if (i == 0)
    15.             {
    16.                 minv = reqAns;
    17.  
    18.                 Vector2 uv;
    19.  
    20.                 Vector3 dir = _spline.GetDirection((float)i / (float)(Resolution - 1));
    21.  
    22.                 uv = new Vector2(dir.x, dir.z);
    23.  
    24.                 direction = uv;
    25.  
    26.             }
    27.             else
    28.             {
    29.                 if (reqAns <= minv)
    30.                 {
    31.                     minv = reqAns;
    32.  
    33.                     Vector2 uv;
    34.  
    35.                     Vector3 dir = _spline.GetDirection((float)i / (float)(Resolution - 1));
    36.  
    37.                     uv = new Vector2(dir.x, dir.z);
    38.  
    39.  
    40.                     direction = uv;
    41.                 }
    42.             }
    43.         }
    44.  
    45.  
    46.         return direction ;
    47.     }
    My shader looks like this:

    struct appdata {
    float4 vertex : POSITION;
    float2 direction : TEXCOORD0;
    };
    struct v2f {
    float4 pos : SV_POSITION;
    float2 direction : TEXCOORD0;
    float2 worlduv : TEXCOORD1;
    };
    v2f vert(appdata v)
    {
    v2f o;
    float4 s;
    o.pos = UnityObjectToClipPos(v.vertex);

    float4 wpos = mul (unity_ObjectToWorld, v.vertex);

    o.direction = v.direction;

    o.worlduv = wpos.xz;

    return o;
    }

    half4 frag( v2f i ) : COLOR
    {
    return tex2D(_MainTex, i.worlduv + direction * _Time.y);
    }

    I am getting wrong result:



    With a high resolution mesh, it looks like this:





    I get weird zigzag stitches.

    How can this be fixed? I tried to pass UV from 0 to 1 instead of directions, but I had the same problem.
     
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,255
    This is a much harder thing to solve than it might seem like it would be. The short version is what you're trying to do is impossible (in the way you want it to be done).

    The correct way to handle this would be to set one dimension of the UVs to be the mesh to be the distance along the closest point along the spline, and then pan that UV. Alternatively you'd need a much more complicated shader that breaks up the surface into squares or hexagonal shapes and you get the direction from the quantized center of those shapes, and then blend between the shapes.

    https://catlikecoding.com/unity/tutorials/flow/directional-flow/
     
  3. kiselevsereja

    kiselevsereja

    Joined:
    Sep 22, 2019
    Posts:
    4
    Sorry, I don't quite understand what exactly is meant here. Can you please explain a little more simply or give some example? Do you mean that I need to pass an array of all spline points to the shader? And calculate direction by this array and t in uv? Wouldn't that be too resource expensive?
     
    Last edited: Aug 23, 2022
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,255
    No. I mean you can't just use the world position as the base UVs in the shader and offset them by a vector. That'll never work if the directions are different on each vertex.

    It has to work something like this for generating the mesh:
    1. calculate vertex position (which I assume is calculated using get point on spline + offset width * spline tangent)
    2. calculate UV where the x is the +/- offset width, and y is the distance along the spline (could be length(current point on spline - previous point on splint)

    In the shader it's then
    uv.y += frac(_Time.y * speed);
     
  5. kiselevsereja

    kiselevsereja

    Joined:
    Sep 22, 2019
    Posts:
    4
    I tried to implement, but I still get strange glitches.

    uv.x:
    Screenshot_1.png

    uv.y:
    Screenshot_15.png

    with texture and uv.y *= 3:

    Screenshot_2.png
    Screenshot_3.png
    Screenshot_4.png
     
  6. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,255
    This is where things get “fun”. You can look at the uv.y gradient image and see that there are places where it’s going backwards or pinching heavily. Those are causing your problems. You can try getting the distance from the previous vertex on that edge, though that might cause some issues with the texture getting warped in other ways.

    Ultimately these are the kinds of problems that anyone doing UV mapping will experience. And the “solution” is … there isn’t one. You just have to fudge the UVs or the mesh itself until you can accept some visual weirdness.

    Though, again, I’d recommend that link to flow mapping.