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
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Weird seams on my tiled UV map

Discussion in 'General Graphics' started by LastDude, Nov 25, 2015.

  1. LastDude

    LastDude

    Joined:
    Aug 5, 2015
    Posts:
    15
    I generate a procedural mesh and create the UV accordingly. The UV is supposed to represent a portion of the texture (tile-sheet). It does the job almost perfectly (see below).

    See the dotted lines on the borders (Click to enlarge). They get worse depending o the camera position.


    Here is the texture (tile-sheet):

    Import settings:
    Texture Type = Advanced
    Generate Mip Maps = False
    Filter Mode = Point (no filter)

    The weird seams is coming (leaking) from the neighbor quadrant in texture (red on the right and orange on the bottom). The seams also show if I only generate one (plane) tile with 4 vertices and 2 triangles.

    The UV calculated for this tile is:
    Vector2(0, 1) //Top Left
    Vector2(0.5f, 1) //Top Right
    Vector2(0.5f, 0.5f) //Bottom Right
    Vector2(0, 0.5f) // Bottom Left

    What is causing this problem and how can I solve it?

    Thanks.
     
    Last edited: Nov 25, 2015
  2. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    This is because floating point numbers aren't perfect.
    http://floating-point-gui.de/basic/

    The fix; don't try to use exactly 0.0, 0.5, and 1.0 (especially since 0.5 and 1.0 aren't actually those numbers), use an epsilon to slightly offset the UVs to inside the rectangle you want.

    An example would be:
    Code (CSharp):
    1.  
    2. float epsilon = 0.0001f;
    3. Vector2(0.0f + epsilon, 1.0f - epsilon);
    4. Vector2(0.5f - epsilon, 1.0f - epsilon);
    5. Vector2(0.5f - epsilon, 0.5f + epsilon);
    6. Vector2(0.0f + epsilon, 0.5f + epsilon);
    Play with that epsilon number until it goes away.

    Note: There's a Mathf.Epsilon, but this won't necessarily work as you expect. Mathf.Epsilon is the smallest number that can be represented that's not 0. 0.0f + Mathf.Epsilon will be a value greater than zero, but the precision of a number gets worse and worse as the number gets bigger, so 1.0f + Mathf.Epsilon might not actually change the number since Mathf.Epsilon is smaller than the precision available to the next largest representable number.
     
    Last edited: Nov 26, 2015
  3. LastDude

    LastDude

    Joined:
    Aug 5, 2015
    Posts:
    15
    Thanks for you answer, but this was the first thing I tried. I removed the equivalent of half a pixel from each side, but if I zoom out far enough it still "leaks".

    Close


    Zoom Out

    (The lines "change" depending on the camera angle and zoom level)

    One more piece of info: each tile has 4 vertices (no shared vertices between tiles).

    Still no clue why this happens.
     
  4. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    You may need to clamp the UVs in the shader to that half pixel to get rid of it entirely. You might also check that anisotropic filtering isn't forced on in your quality settings. It's unfortunately still likely to just be an issue of float precision, not directly in the UVs, but the entire rasterization pipeline being a little off here and there and eventually your UV coordinate is sampling +/- part of a screen pixel's uv range. If something is far away such that the pixels in the texture are smaller than the screen pixels you might need 8 pixel gutters between color changes, and mip maps can make this worse.

    Clamping in the fragment shader prevents the compounding float precision error from letting it go beyond the ranges you want, which means now you only have to deal with the precision errors in the UVs and shader properties. It's not something you can really do with complex geometry, you just have to eat the gutters or use multiple materials, but for simple quads it shouldn't be too bad.
     
  5. jvo3dc

    jvo3dc

    Joined:
    Oct 11, 2013
    Posts:
    1,520
    It's not the imprecision of floating point numbers that cause the issue here. (0.0, 0.5 and 1.0 can all be represented exactly by floating point numbers.)

    Anisotropic filtering does impose significant issues with tiling textures from an atlas, but just simple bilinear filtering and mipmapping in itself give rise to some issues.

    Case 1, bilinear filtering: u coordinate is 0.5 and texture is 128 pixels wide. The pixel coordinate is actually 63.5, so bilinear filtering will take half of pixel 63 and half of pixel 64. Taking away half a pixel from the u coordinate fixes this. Exactly what you did.

    Case 2, mipmapping: This all depends on the mipmap filter used, but the further away from the surface, the higher the mipmap that is selected. The higher the mipmap, the more pixels that influence the result. Half a pixel in mipmap 1 is a full pixel in mipmap 0. Half a pixel in mipmap 4 is 8 pixels in mipmap 0. No way to fully offset or clamp your way around that.

    Here's a highly colorful image that shows why bilinear filtering with mipmapping will always bleed into the next tile. The higher the mipmap, the larger the bleed.

    mipmapping.png

    The solution is to disable all filtering (point) and filter it manually. Select your mipmap by the appropriate ddx/ddy calculations. You should be able to find those calculations online. Then calculate the 4 sample points on the texture and weights for your bilinear filtering. Now clamp all 4 of those sample points based on the tile. Then sample and combine.

    And in the range of colorful images, here's a small visual aid. Yellow shows the original sample location and white the actual sample locations that are combined for bilinear filtering. On the left the normal situation and on the right the clamped situation.

    sampling.png
     
  6. LastDude

    LastDude

    Joined:
    Aug 5, 2015
    Posts:
    15
    Thanks. The problem was being caused by Anti Aliasing, so turning it off solves it (until I have to turn it on again).
     
    LaireonGames likes this.
  7. bgolus

    bgolus

    Joined:
    Dec 7, 2012
    Posts:
    12,248
    Hey, we were both wrong as to what the problem was! Glad you figured it out.