We have prototyped a UI for our Unity game which relies heavily on transparent gradients and brush spots. We were planning to create this UI using UI Toolkit, but we are having difficulties showing transparent gradients in our game. We have a simple texture which is basically a white radial background which fades out to transparent, and while the preview looks perfectly fine in the UI Builder, the Game View renders a heavily banded version of the texture:
Update #3: When rendering onto a RenderTexture placed on a Plane in front of the camera, the issue does not reproduce.
How much of a performance hit should we expect when working with UIs rendered onto RenderTextures? We are willing to use this workaround until this issue is fixed. Unfortunately, we found it incredibly difficult to make pixel perfect UIs with Unity’s UI tools.
Could someone from the team please inform us whether this is something worth being looked at (and potentially fixed) in the future?
Update #4: Very interesting: one of my friends does not encounter this issue, even though we are running exactly the same executable of the game. He seems to be a special case though, because several other people who tried experience the same issue that I have on my screen.
No. But I’ve gathered some system specifications from computers which do not encounter issues, and from computers which do. I’ll also send you a private message with a bug report, where the systems’ specifications are much more detailed.
Does NOT work fine on: - NVIDIA GeForce RTX 3060 (Driver Version 31.0.15.4633, DirectX 12, DirectX Database Version 1.4.7), NVIDIA GeForce GTX 1060 (Driver Version 31.0.15.3598, DirectX 12, DirectX Database Version 1.4.7), NVIDIA GeForce GTX 1660 SUPER (Driver Version 31.0.15.4633, DirectX 12, DirectX Database Version 1.4.7)
Works fine on: AMD Radeon RX 6650 XT (Driver version 31.0.23013.1023, DirectX 12, DirectX Database Version 1.0.8), Intel(R) Iris(R) Xe Graphics Family (Driver Version 31.0.101.4502, DirectX 12, DirectX Database Version 1.4.7) (last one is a work laptop with no dedicated video card)
Thanks for submitting a formal support case. I will keep an eye on your case as QA processes it.
In linear color space, it is actually expected to see a difference in blending between the UI Builder and the Game View because the UI Builder is running in gamma color space. This is a known issue that we will address at some point. In the meantime, I would recommend that you use live reload to see your changes live in the game view.
Turns out the formal support case has been closed, with a resolution stating that the difference between the colorspaces is the culprit. This doesn’t seem to be the actual reason for the banding, but rather an issue with how Unity renders the UI in Game View. We don’t necessarily mind having a different appearance in Game View vs. the view in UI Builder. The main issue is the banding, which is much more noticeable than it should be.
The banding still appears and is as noticeable even after we switch our project to Gamma colorspace;
The fact that the banding does NOT appear in Game View on some graphics cards apparently has not been tackled in the resolution
The resolution specifies that the provided texture has some banding in it. It does, but Unity enhances the banding a lot. Also, the exact same issue appears even if instead of a using a texture, we draw the gradient using MeshGenerationContext upon subscribing to generateVisualContent.
Could you provide more information about these rendering differences on some graphic cards? I can’t find the info in the case. The rendering has been consistent across our testing devices on our side.
I will send you the link to the JIRA ticket privately - I’ve left a lot of comments there with system diagnostics of systems on which the banding can be seen, and of systems on which it is unnoticeable.
The gradient banding issue is most noticeable when having a light-colored, transparent gradient on a dark background. Our issue happens when applying a white, transparent (most have an opacity value between 5 and 20%) on a black background.
I believe reply #4 in this thread best showcases the issue. Both screenshots have been done on the Game View (not UI Builder preview!). However, in the first screenshot (which is my system, with an Nvidia graphics card), banding can be clearly noticed in the bottom-left-side of the panel. In the second screenshot (which is my friend’s system, with an AMD graphics card), there’s no banding. Both screenshots have been done on the exact same build.
The banding is caused by a texture which is basically a gradient from white to fully transparent, with lowered opacity. Initially I thought the texture was the reason this happened, but using MeshGenerationContext in order to draw the gradient (instead of having the texture) rendered the same result.
The issue can be easily reproduced on a blank scene by having a camera which renders a solid black color, and having a UIDocument which covers all the screen space and uses the .png attached (DiamondCircleDecoration.png). If you’d like to test this without the texture, add a VisualElement to the UI document, draw a gradient from white opaque to white transparent with MeshGenerationContext, and lower the opacity of the VisualElement. The same thing happens.
Having the UI Builder show something different than the Game View does not bother us. But the fact that the banding appears on certain graphics cards - but on other graphics cards it works fine - forces us to deliver a game where a good majority of our players encounter visual issues which we don’t have any control of.
The problem is that your gradient is being interpreted in linear color space. I can see the bands in RenderDoc and the first band jumps from (1,1,1,1/255) to (1,1,1,2/255). This is from the source data which is 8 bits per channel, so the alpha cannot encode more precision than 1/255. Now let’s see how 1/255 to 2/255 in linear space means in gamma (perceptual) color space:
This gives a perceptual jump of 8/255 which is large and explains why you see the bands. So if you want to keep using a texture with alpha as you do, you will need to have more bits for your alpha channel. Otherwise, you could try rebalancing your texture channels to use more range of the alpha channel and maybe make the RGB values darker. Also, dithering could help as well.
The gradient over the tint color does have some banding, but is otherwise reasonable compared to the Big-Diamond-10 asset. Your element has a 1% opacity (from what I can see in the UI Builder), which makes for a long gradient with only 8-bits of per-channel precision. Banding is expected in this situation.
However, an example comparable to the Big-Diamond-10 asset is even worse. Using a fully-white element and do the gradient on the alpha channel instead leads to this result:
As Alex mentioned above, this is caused by the fact that the alpha channel is not gamma-corrected, which leads to jumps in the perceptual gradient. Doing the gradient on the RGB channels would probably help a lot on the Big-Diamond-10 asset.
As for the difference on different GPUs, it seems that GPU drivers have somme leeway on blending precision, as we can see in the Vulkan spec: basic blend operations are performed with a precision and dynamic range no lower than that used to represent destination components
In your case, the destination components have 8 bits per channel, so precision and dynamic range are 1/255 which are insufficient for the alpha values that you have.
@mcoted3d , @AlexandreT-unity , thank you for your information and sharing your valuable knowledge. It looks like we have some work to do on fixing our textures. Also, looking forward to the colorspace selection feature. Cheers!
Glad it helped. Another alternative would be to draw your UI panel to a RenderTexture with a color format R16G16B16A16_SFLOAT and draw the final result with a quad on a second panel. But that would eat-up a significant amount of bandwidth which might not be desirable especially if you’re on mobile.