Blurry TextMeshPro text at lower resolutions in fullscreen

When using a lower resolution than the optimal monitor resolution, the TextMeshPro texts are blurry.
The lower the resolution, the blurrier the text.
I have created a bug report about 10 days ago, case 1337180.

In order to reproduce the problem please follow these steps:

  • Unzip the attached project
  • Load the attached project in Unity 2020.2.4f1
  • Build the standalone
  • Execute the standalone
  • Press the ‘Switch resolution’
    You’ll see very blurry text.

When pressing ‘Switch resolution’ again, you’ll get back to the optimal resolution.
The current resolution is displayed in the upper-left corner.
You can choose the lower resolution (the one you’ll switch to) using the dropdown.
You can choose if you;ll switch to fullscreen or window mode using the toggle.
The ‘Refresh resolutions’ buttons is used to re-generate the list of resolutions in the dropdown (in case you change monitor).
The ‘Exit’ button is used to exit the application.

There are 3 different sizes of text, which apparently behave exactly the same.

7192378–863191–UI text blur bug.zip (1.27 MB)

Still no answer to my bug report or here.

Does anyone know how to avoid having blurry text with TextMeshPro when using lower resolutions in fullscreen?

Here is the associated bug report:

There is no easy solution for this as this behavior is the result of rendering the content of the screen at some potential small resolution and then scaling this content to some larger resolution.

For instance, if the target resolution is 640 x 480 and your screen resolution is 2560 x 1920, the text and everything else is rendered at 640 x 480 and then using some algorithm scaled to the target resolution. When going from lower resolution to higher resolution, you will get this blurriness pretty much regardless of the algorithm used.

Here is an example of native content rendered at 640 x 480, scaled to 2560 x 1920 in Photoshop using 2 different algorithm designed for enlarging the content.

Original 640 x 480 at 800% zoom
7245659--872819--upload_2021-6-16_18-54-26.png

Same image scaled to 2560 x 1920 at 200% to keep the same physical size.

7245659--872825--upload_2021-6-16_18-55-12.png

Same but using a different algorithm
7245659--872828--upload_2021-6-16_18-56-6.png

Then the original source at 1:1 640 x 480. Text is 36 point size.
7245659--872831--upload_2021-6-16_18-58-31.png

and the same text if full screen was 2560 x 1920 or 4x the size of the source
7245659--872834--upload_2021-6-16_18-59-2.png

As you pointed out the smaller the source resolution the blurrier the result and that is simply due to having less data to sample from which is then interpolated to make up the larger image. Some of those algorithms can do ok with images but with anything that has sharp edges like text or shapes / frames, it would get fuzzier and fuzzier.

The Canvas Scaler does play into this and can make things worst as it causes the source to potentially be rendered at some scale of the original (640 x 480) for instance. which is then again scaled to some larger size.

When NOT in full screen mode, the content is scaled where the text is rendered at the higher target which is why it renders nicely. But when using Full Screen, it is interpolated / scaled which is what causes this blurriness.

1 Like

Here is a simple example using a circle which is a simple shape that it much more forgiving then a character can.

Native resolution is 20 x 20 pixels at 800% zoom
7245677--872843--upload_2021-6-16_19-11-55.png

Now scaled 4x and at 200% zoom. This is simulating going from 640 x 480 to 2560 x 1920
7245677--872846--upload_2021-6-16_19-12-41.png

I will talk to the UGUI team about the Canvas Scaler to see if there might be some better handling / improvements that we could make there but with regards to Full Screen mode when everything is rendered at native small resolution and then scaled to some larger (without re-rendering at the new target resolution) we are pretty much stuck with that result.

I think that if instead of going full screen, you were to change the window size to fill the screen resulting in the content being re-rendered at that larger size, then everything would look fine.

Thank you for looking at that problem.

With the repro-project in the bug report you can untick ‘Fullscreen’ to try in windowed mode, and you’ll see that it’s also blurred, and enlarging the window will in fact enlarging the rendering resolution which would defeat the goal of using a low resolution.

The goal in the en is to provide the players with the possibility to play in 1280x800 on a 2560x1600 monitor (for example) so that the game won’t take as much load on the GPU than in full resolution.
So any trick making the game rendering at full resolution and then down-scaling it would defeat that purpose.

I have tried a few things, and updated the repro-project accordingly (attached in this post).
Here are the 2 things I’ve seen:

Note that it mostly un-blur large texts, but I think it’s still interesting and may help to reduce blurriness a bit.
Also, I have added an image at 2 different sizes to check how non-text behaves, I know it’s not your specialty but it is interesting to note that the images are immune to the different resolutions at startup.

Finally, maybe some ‘trick’ could be done when using exactly half-resolution, in that case easy scaling can be done, and wouldn’t be blurry (it would be aliased like hell, but it’s worth trying it to see if it’s any better than blurry as hell).

7246094–872948–UI text blur bug v1.zip (1.36 MB)

Just a quick reply while I continue to look into this.

This specific blurriness (as shown in the 2nd image) is caused by the SDF Scale not getting updated correctly. This will be fixed in the next TMP release. However, to confirm that is in fact the case, switch to using one of the SSD shaders. Those are included in the TMP Essential Resources with the other TMP shaders and have the suffix SSD at the end of the names. There is a mobile SDF SSD and a full featured one as well.

Thanks for the trick!
It works better with the SSD shader.
Is there any downside about using the SSD shader over the standard one?

The SSD shaders have a higher performance overhead but on most devices, that should not be noticeable.

Having said that, in the next TMP package release, the SDF Scale issue that you ran into with the 2nd image, will be resolved so you will be able to go back to the normal SDF shader if desired.

I understand that there’s no magic and that we won’t have zero blur.
But when I choose to use 1280x800 resolution on my 2560x1600 monitor, it is really blurry.
I remember a time where my Windows was using a 800x600 resolution, and I was able to read the texts without any problem.
1280x800 is a resolution which is more than enough to display texts, upscaling it will of course generate some blur, but it should be very limited, especially since I am upscaling to a resolution exactly 2 times larger in both axis.

In addition, I have just tried a Unity game using IMGUI, at a 1280x800 resolution, it is very slightly blurry, but a lot less that using TextMeshPro.

On these screenshots you can see that the game is less blurry than the test I’ve made.
7311706--886441--From_The_Depths_PBmjKXIqMY.jpg 7311706--886432--TextUI_NZz2XFWUCl.jpg

I think that closing the bug report ‘by design’ was a bit premature, if IMGUI can do better then I’m sure that the UI team can do something better too.

I’ve been pondering over this thread from time to time and wanted to add some thoughts that might be useful.

From my understanding of what has been written the main issue is that when setting the Unity output resolution to some small dimension ( say 640x480 ) via applying screen.setResolution() and using fullscreen set to true, text becomes understandably blurry.

Fixing this in Unity does not seem possible since using setResolution in fullscreen mode will be changing the display/monitor resolution directly, whereby the monitor will be stretching the input to fill the physical dimension of the display. This obviously means any blur caused by this is out of the hands of Unity at this point. Its simply not addressable and looking to try and improve the text rendering and maybe enforcing a limit to how low you can set the resolution is just trying to work around the problem instead of solving the cause - more on that later.

Now to be fair it does seem there is a bug that has been fixed in TMP where changes to screen resolution were not always reflected, but beyond that it doesn’t seem to be anything that can be solved on Unity’s end. As to why IMGUI looks sharper, its probably due to the difference in how the font characters are rendered, since IMGUI will render out AA characters at a specific resolution, while TMPro is using SDF we are just observing the inherent differences in sharpness between the methods at low resolutions.

I would imagine that the TMPro font rendering could be improved at these lower resolutions with some tweaking, but its never going to be great and possibly couldn’t match the old pre-rendered Unity fonts due to differences in how they are displayed. However again in my opinion its the wrong place to focus on as its not solving the cause of the problem.

With all this in mind the obvious approach to fix the issue would be to separate the rendering resolution of the game vs the UI. This is standard practice these days as dynamically altering the render resolution of the game but not the UI can provide a good solution to performance where fillrate is an issue.

The basic premise is to render your main game camera to a custom renderTexture where you can set its dimensions to reflect the size that you would have previously set via SetResolution. Then this renderTexture is rendered back to the screen either in a second camera postRender method or maybe by applying it to a RawImage in another canvas that is rendered before the UI canvas.

In this fashion you have made the UI resolution independent to the main game renderer and thus you can keep the screen resolution to match the native display resolution ( the UI renders at this resolution ) whilst the game itself can be rendered at any other resolution for the performance gains of reducing fillrate. You can still change the output resolution though in practice it shouldn’t be required.

Unity does offer this ability natively via ScalableBufferManager that can be enabled directly on cameras and avoid the whole RenderTexture aspect, but sadly this is not support for older 3d api’s or older devices which is usually precisely where you need this functionality!

Setting up the custom RenderTexture is heavily project dependent though and you’ll have to read up in the forums for the best way to approach it for your specific project.

Well, in fact there are 2 ways of rendering in fullscreen with Unity, one of those will do as you said, change the actual resolution of the monitor, while the other will actually keep the resolution as it is.
I am using the second one (so that it won’t break the positioning of the icons and the other monitors).
The screenshots I took are in 2560x1600 despite the fact that the resolution in Unity was 1280x800, that’s because the screen resolution hasn’t changed in Windows.

In that case, it is Unity who is responsible for the upscaling.
I don’t know why IMGUI is sharper, maybe it’s the font, maybe it’s not the same upscaling algorithm, maybe it’s for a completely different reason, but having noticed that may help to find out a better rendering for TextMeshPro.

That’s actually a good idea.
I’m not sure it can be applied to my game, as I’ll have lots of world-space UIs, but I like the idea and keep it in a corner of my mind, thanks for the tips.

Whoops, just checked this and you’re right, taking a screenshot does indeed show the window has remained unchained in pixel dimensions. Even more embarrassing is it even says so in the documentation, guess I just assumed it was running in Exclusive mode. Sorry about that.

btw - I was able to fix the clear bug with TMPro by running a script on all TextMeshProUGUI elements and using 'ForceMeshUpdate( true, true) ( not sure if it needs both bools ) after switching resolutions. You could try this code and call it from a button, but I suspect it just addresses the TMPro bug discussed before.

public void TMProForceMeshUpdate()
    {
        TextMeshProUGUI[] texts = canvasRoot.GetComponentsInChildren<TextMeshProUGUI>();

        foreach( TextMeshProUGUI t in texts )   
            t.ForceMeshUpdate( true, true );
    }

I guess the same idea could be achieved with worldSpace UI, but its likely to be a real pain. Then again do you have the same issue with worldSpace UI as the scales are likely to be pretty different to screen space? I do remember on a recent project there was a problem with text completely disappearing with TMPro and worldSpace UI when the scale got too small.

Hello, I fixed this by changing the shards code.:smile:

I think on line 15.

(before.)

_OutlineSoftness("Suavidade do contorno", Range(0,1)) = 0

(later.)

_OutlineSoftness("Suavidade do contorno", Range(-1,1)) = 0

now you can adjust the Softness down to -1, thus decreasing the blur as you wish.

[Warning]
But this is for smaller texts, if you are going to use smaller and larger texts, you will have to use two materials and configure Softness according to your need.

Hello, I fixed this by changing the shards code.:smile:

I think on line 15.

(before.)

_OutlineSoftness("Suavidade do contorno", Range(0,1)) = 0

(later.)

_OutlineSoftness("Suavidade do contorno", Range(-1,1)) = 0

now you can adjust the Softness down to -1, thus decreasing the blur as you wish.

[Warning]
But this is for smaller texts, if you are going to use smaller and larger texts, you will have to use two materials and configure Softness according to your need.