Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

Question Seeking Pixel-Perfect and Smooth Scrolling in Unity UI

Discussion in 'Scripting' started by GuirieSanchez, Jul 25, 2023.

  1. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Hello everyone,

    I'm currently facing an issue related to the visual quality of Unity UI scrolling. The problem is quite similar to an issue raised previously on stack overflow:

    As the content of the Scroll Rect moves, thin horizontal lines (one-pixel height lines) don't maintain a constant appearance. The lines occasionally appear thicker or thinner and shimmer/flicker while scrolling. Ideally, we want these horizontal lines not to visibly change, maintaining a pixel-perfect appearance throughout. Please note that setting the Canvas to "Pixel Perfect" does not alleviate the issue.

    EDIT:


    To address the line shimmering, I applied the following solution that ensures pixel-perfect scrolling:
    Code (CSharp):
    1. public class PixelPerfectScrollRect : ScrollRect
    2. {
    3.     [SerializeField] private Camera m_Camera;
    4.  
    5.     protected override void LateUpdate ()
    6.     {
    7.         base.LateUpdate();
    8.         EnsurePixelPerfectScroll();
    9.     }
    10. //...
    11.  
    Code (CSharp):
    1. void EnsurePixelPerfectScroll()
    2. {
    3.     if (!m_Camera || !m_Content) return;
    4.  
    5.  
    6.     var pos = m_Content.position;
    7.     var screenPoint = m_Camera.WorldToScreenPoint(pos);
    8.  
    9.     screenPoint.y = Mathf.Round(screenPoint.y);
    10.     screenPoint.x = Mathf.Round(screenPoint.x);
    11.  
    12.     pos = m_Camera.ScreenToWorldPoint(screenPoint);
    13.     m_Content.position = pos;
    14. }

    This method effectively eliminates the line shimmering, but it introduces a new problem: the content movement now appears jerky, especially at low velocities. The jerkiness seems to arise from the immediate adjustment of the position that happens when we round the position to the nearest pixel.

    EDIT:


    There are many apps, like WhatsApp or various Android/iOS contact list apps, that feature thin divider lines yet still maintain pixel-perfect and smooth scrolling. This suggests that achieving both pixel-perfect appearance and smooth scrolling simultaneously is feasible.

    I'm looking for any advice or insight into how this might be accomplished in Unity. If you've encountered a similar issue or have any ideas, I would be very grateful to hear from you.

    Thank you in advance for your help.
     
    Last edited: Jul 26, 2023
  2. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    461
    Have you checked out these?
     
    GuirieSanchez likes this.
  3. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Hi @KillDashNine. I've just check most of them right now. Unfortunately, none of them seem to address any of the issues.
     
  4. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    Not Unity UI expert, but I think that might be not possible to fix it with C# code. If I am correct the problem comes from aliasing the edge of mesh, I mean you probably have images as elements and edges of those elements can "land" between pixels, what results in stretchy behaviour. You fixed this by putting the whole thing perfectly aligned with the pixels on the screen.

    Now, I suspect that if you rendered perfectly aligned list to render texture, then you used that render texture as your actual viewport (but with original scroll offset), then you would end up with perfect smooth movement (becuase it would be one mesh/texture and it can interpolate color between list elements).
    In any case I guess it might be good idea to ask in some graphics/shader section about this problem, plus if you attach video of this behaviour it can be helpful to everyone to see if you are talking about the same problem as they think.

    //edit: or actually... try to create list elements (the background or whatever makes those lines) with at least 1px of spacing from the edge - in theory it should prevent bleeding color from the edge if I understood the problem correctly.
     
    Last edited: Jul 26, 2023
    Stardog likes this.
  5. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    461
    You're a tough customer.
     
    Kurt-Dekker likes this.
  6. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Here are the videos (please set them to full screen):

    (1) The first video highlights the issue with divider lines shimmering during scrolling. As you can see, the thin lines don't maintain a constant appearance and exhibit a shimmering effect:



    (2) The second one shows that the shimmering effect is gone with the pixel-perfect solution. However, the content moves in a noticeably jerky manner at slower speeds, especially when it's about to stop:



    I hope these videos provide a clearer picture of the issues.
     
  7. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    Can you show us how did you make those lines? I mean the image structure or whatever that is.
     
  8. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    461
    How about just make your divider lines 2 pixels wide...
     
  9. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Thanks for your responses. A two-pixel height line is undesired for my specific use-case, Furthermore, the issue still persists even with a line thickness of up to 3 pixels

    The setup for the divider line is rather straightforward as shown below:

    upload_2023-7-26_18-53-10.png

    Note that the issue of line inconsistency is independent of whether a sprite is used for the Source Image or not. The only way to prevent it (that I know of) is by ensuring the content moves by whole pixels.
     
  10. KillDashNine

    KillDashNine

    Joined:
    Apr 19, 2020
    Posts:
    461
    I believe the issue in theory is solved by VSync.

    Have you tried Project Settings -> Quality -> VSync Count?
     
  11. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    Try to do what I said in previous post. Take the sprite I attached here, set it as divider image, but set the height to be also the height of the image (3px) and see if the problem is still there.
     

    Attached Files:

  12. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    It still happens with 3px and using your image. Is not about the sprite attached. Also, I would preferably use thinner lines, so thickening them to avoid line shimmering won't be a good fit here.
     
  13. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    But the line I gave you is 1px wide, image has 3px height, but there are empty pixels around the line.
     
    GuirieSanchez likes this.
  14. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    I suspected that. I increased the alpha to something very big (16px), and the shimmering starts to disappear when I set the image component's height to that amount. I need to do more testing, but thanks for the suggestion, maybe that sort of practice can do the trick.

    EDIT: Unfortunately, it still shimmers. The only way to avoid shimmering completely is to thicken the line enough. Although I must admit that it did improve it a bit.
     
    Last edited: Jul 26, 2023
  15. Qriva

    Qriva

    Joined:
    Jun 30, 2019
    Posts:
    1,358
    Actually the image I gave you was somehow without the margin, I don't know why.
    I made list with background color the same as single element, I added lines between elements as images (3px height, but actual line on it has 1px, image attached), and disabled pixel perfect canvas. This is how it looks like on my PC:

    https://i.imgur.com/QtJoQlx.mp4

    Obviously not perfect, but there are no those wandering double lines or jumping. What do you think?
     

    Attached Files:

    GuirieSanchez likes this.
  16. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,928
    Increasing alpha is the solution. It's also how you have to do the lines on a football pitch to allow bilinear filtering to work.
     
    GuirieSanchez and Qriva like this.
  17. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    I think it looks pretty nice. At least way better than having a one-pixel line with no transparency for sure.

    I've been testing it and the shimmering's barely noticeable, thanks!


    Now I'm trying to figure out which of the 2 following approaches will work better:

    (1) A perfect one-pixel line with 1px alpha on both sides (@Qriva's suggestion)

    (2) A thin line (possibly 1-2 pixels thick) with blurred edges and 1px alpha on both sides.

    I think visually the 2nd looks more consistent, but maybe it's just me. @Stardog when you mentioned the alpha, were you referring to either of these approaches?
     
  18. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Btw, I don't know if the following happened to you in your test, but some of the lines appear thicker than others even when the content is static:

    upload_2023-7-26_23-2-6.png

    Notice how the second line appears thicker (and blurrier) than the rest.

    PS: I'm using your line_margin.png.
     
  19. Stardog

    Stardog

    Joined:
    Jun 28, 2010
    Posts:
    1,928
    I was meaning (1) except with an 8x8 image (power of two, 7 transparent pixels above, 8 below). From testing, it looks the same as the 3px, so this will only make a difference when the object is rotated on the X axis, or needs 3D perspective. Rotating Z and translating Y it looks the same as the 1px of alpha.

    Maybe you didn't make the height of the gameobject match the line (3px)? And it should be imported as a Sprite.

    I recommend just buying this asset - Procedural UI Image. It's 1px high image will be antialiased by default, even when rotated on Z axis.
     
    Last edited: Jul 28, 2023
    GuirieSanchez likes this.
  20. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    I understand that this asset may not be particularly performant, as it uses a single draw call per image and some CPU-intensive operations. This was somewhat off-putting for me. They may have improved performance since I last checked, but I haven't seen any reviews addressing this. If you own this asset and can confirm any improvements, or share your experiences with it, I'd greatly appreciate it.

    I'm curious about how this asset handles shimmering and anti-aliasing. Could you explain why their approach is effective?


    Regarding my specific issue, the game object in question is a prefab, and its height is set to 3px to match the 3px sprite. Therefore, each line should appear identical, but they do not. I can confirm that the image has been imported as a Sprite:

    upload_2023-7-28_19-49-12.png
     
  21. KnightWhoSaysNi

    KnightWhoSaysNi

    Joined:
    Jan 21, 2017
    Posts:
    13
    Have you managed to fix the issue? I'm seeing something similar in my game as well.
    When I zoom in in the SceneView everything seems normal, but in GameView and in Build there are visual artefacts
     
  22. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Nothing really effective, unfortunately. Using a 3px image for a 1px line (2px of padding on each side) seems fine in certain scenarios, but sometimes there is still flickering when scrolling and, as you seem to be concerned about, also introduces some artifacts, such as a thicker line, when the 3px line falls in between pixels. d

    This also applies to a sprite (imagine you have a sprite for a button) with a 1px border, the same flickering and artifacts will happen unless you add some padding to the image before importing it, which is not so appealing.

    The only solution that works for sure is using a scroll rect that moves the content only on whole pixels. This way, you can have a 1px line with no flickering or artifacts. But of course, this comes at the cost of a crappy and choppy movement as you scroll, more like a stuttering effect.
     
  23. KnightWhoSaysNi

    KnightWhoSaysNi

    Joined:
    Jan 21, 2017
    Posts:
    13
    I'm not even moving anything, I just have Outline around some squares in a grid and the lines aren't the same everywhere. Even though they look perfect in the SceneView.

    Have you tested how it looks using UI Toolkit by any chance? I'm considering skimming through the documentation to learn how to replicate a simple version of what I have with IMGUI.
     
  24. GuirieSanchez

    GuirieSanchez

    Joined:
    Oct 12, 2021
    Posts:
    462
    Are you using 1px lines and having artifacts like the one below?
     
  25. KnightWhoSaysNi

    KnightWhoSaysNi

    Joined:
    Jan 21, 2017
    Posts:
    13
    Similar to that yes, some lines are thinner, some thicker. I've tried various thicknesses, but the problem persists.