Linear space for scene, gamma for UI?

So I switched to linear space from gamma, and my transparent UI doesn’t look so good anymore. I find it hard if not impossible to tweak the transparency to reach the same quality as before.

Is there a way to render the scene in linear space, but the UI in gamma? Maybe a shader?

Thank you.

Well, I tried changing row 85 in UI-Default.shader to:

                return pow(color, 1/2.2);

And that does make it look a bit more like before. Not the same but a bit better.

2 Likes

I have just moved over to Unity 5.0.1f1 and I have been looking into switching to linear color space. Likewise I have noticed the UI looks different, specifically with transparent elements.

I did a very quick test which confirms gamma vs linear are producing different results. In the test, without using any textures I have a black background (0,0,0,255) and over it a rectangle (255,255,255,128). In gamma (the square on the left) the resultant rectangle comes out at 128,128,128 as you would expect. In linear color space (the square on the right), it comes out 188, 188, 188.

Is this a bug? Is there a setting somewhere that I’m missing?

Thanks.

2082262--136112--GammaVsLinear.png

http://filmicgames.com/archives/299

Thanks, as it happens I was reading that very same page earlier :slight_smile:

I have created a variant of the UI-Default.shader with a fix similar to one you did which works. However, this complicates the work flow. I would effectively need to make sure every UI element has the correct material assigned to it (yes I could write a tool to assign override materials to every UI element but it seems a shame to have to resort to this). The big problem for me is that when you set Unity to linear color space, it affects everything. It would be nice if UI had its own unique color space or I had to the option to override the default UI shaders without having to replace materials for everything.

It’s a rather frustrating issue so I’ll wait to see if anyone else replies to this before deciding how to proceed.

For anyone who is interested, I believe I have identified the equation Unity are using to convert linear to gamma space which happens to be defined in UnityCG.cginc:

inline half3 LinearToGammaSpace (half3 linRGB)
{
return max(1.055 * pow(linRGB, 0.416666667) - 0.055, 0);
}

For those using pow(x, 2.2) - this is actually wrong! It’s only an approximation. What I have done is take this above equation and reverse it, because the goal is to undo this linear to gamma conversion. This gives us this equation:

pow(x + 0.055, 2.4) / 1.13711896582

The steps to implement this are as followed:

  1. Download the built-in shaders from Unity’s website.
  2. Get UI-Default.shader and UI-DefaultFont.shader.
  3. Modify their frag functions to include this line right before the colour is returned:
    col.a = pow(col.a + 0.055, 2.4) / 1.13711896582;
  4. Place these two shader files in your resources folder. These will automatically override the built-in shaders.

Although this much produces better results than using x^2.2, there is still an issue where it isn’t 100% accurate. I believe it’s down to loss of precision. The alpha is being transformed from gamma to linear in the shader and then linear back to gamma by the Unity engine. This means that as ARGB values get closer to zero, they lose more precision. I am still investigating to see if there is anything I can do to reduce this precision loss.

2 Likes

You can try importing sprites which be used in UI with “Bypass sRGB Sampling” set to true.
So you can render the scene in Linear but still keep your UI’s colour correctly.

Source: UI looks different in Linear color space - Unity Engine - Unity Discussions

I have tested this and it doesn’t work for me. Checking “Bypass sRGB Sampling” makes UI textures much brighter. I found a forum post that I believe explains why:

A quote from a Unity dev “The first issue is the bypassing of sRGB sampling. This should only be used for legacy IMGUI. IMGUI renders AFTER scene rendering when linear rendering has been disabled, so we need to mark the texture to not be linearized on sample. Your UI is rendered in the world so this is not applicable here. This may be changing for 5.0, the graphics team is talking about it currently.”

(Just to clarify, I am not using the immediate mode UI, I am using the new UI system.)

To follow up on the post I made back in April, my shader fix still isn’t enough. Even with the hack, the way alpha blending works is different in linear colorspace and so you still get undesirable results for example with overlapping elements. There are no bugs here as far as I can tell, the problem as I see it is that there is a lack of flexibility with the way colorspace works in Unity. I can’t for example switch the engine to gamma colorspace just for the UI and have the rest as linear. I still intend to move my project over to linear colorspace but before I can do that, I need to come up with a strategy for my UI since I make heavy use of alpha blending :frowning:

Good solution, but i have changed your function to
color.a = pow(color.a, 2.2);
and now the gradients in UI sprites look mush better.

2328504–157215–UI-Default.shader (1.77 KB)

Is there any update on this? I’ve got a new material for transparent elements similar to @SunnySunshine .

#if !defined(UNITY_COLORSPACE_GAMMA)
    col.a = pow(col.a, 2.2);
#endif

I know this is just an approximation but is close enough for now. The trouble comes when I am using other shaders / assets for other UI such as TextMeshPro.

Is there not a global flag for fixing this across all UI?

I just tried switching to Linear, Text Mesh Pro, does not like this.

I helped myself as follows:

  1. I made a helper class
using UnityEngine;

public class UiColors
{
    public static Color FixUiColor(Color col)
    {
        if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
            return col;
       
        float r = Mathf.GammaToLinearSpace(col.r);
        float g = Mathf.GammaToLinearSpace(col.g);
        float b = Mathf.GammaToLinearSpace(col.b);
        float a = col.a;
        return new Color(r,g,b,a);
    }
}
  1. I used the helper class to convert colors passed as params in my custom UI-shaders:
        material.SetColor("glowColor", UiColors.FixUiColor(glowColor));

For those of you searching online for a solution, use the following shader. It simply works.

5514685–566122–UI-Default.shader (1.85 KB)

1 Like

This is still broken in 2019.4.16. I mean sure I can use that fix, but why do I need to do that.
Godot can handle that, why can’t Unity?

Just wanted to post an update, as I’ve been messing with colors trying to fix them and get them working right. If you multiply the alpha by pow(2.2) or use the more complex formula posted above, you wind up breaking the alpha channel. After switching to linear, and having weird halo/transparency issues in my UI, I implemented that fix. But then all my background darkening overlays (for dimming the background behind a modal window, for example) became way to light and had to be adjusted darker. Applying a solid black image over a white image needed to have a transparency of .9 or so to approximate a 50% gray.

You want to multiply the rgb values by the modified alpha (pow(alpha, 2.2)) but do not actually change the alpha. @mndcr made a post with a helper class to adjust the colors. How his code handles it is almost correct - it does the color correction but does not account for alpha.

Multiply rgb by pow(alpha, 2.2) but leave alpha at whatever value it was before.

I applied this to my UI shaders and I no longer have the large ugly halos AND my overlays are back to the way they were before.

Hi @UnbridledGames , unfortunately, I don’t archive your solution to fix UI issue.
Here is my result (I used red as color and process it only on the red channel):
code :

return half4(1.h * pow(i.uv.x, 2.2), 0.h, 0.h, i.uv.x);

With white background:
Result :

Target :

With black background:
Result :

Target :

Unity UI needs to be processed in sRGB mode (gamma). A very good article gives solutions about it that are not free, but it works if you need to stay in linear color space: 3D scene need Linear but UI need Gamma – Ming Wai Chan. The principle is to render your scene into render texture with sRGB mode and render it into your screen. “When Linear color space is used, render textures can perform Linear to sRGB conversions when rendering into them and sRGB to Linear conversions when sampling them in the shaders.” (Unity - Scripting API: RenderTexture.sRGB)

I decided to report a bug and what I got:

Unity Team doesn’t see it as a problem.

untiy 2022.3 .the problem is still there.This problem should be given a very convenient configuration solution, rather than looking for a solution on Google

untiy 2022.3 .the problem is still there.This problem should be given a very convenient configuration solution, rather than looking for a solution on Google

1 Like

I can also see this is still a problem which is a little shocking - but another reason I have not used Linear yet, even thought it is the URP default!
The TMP Text is particular poor when Linear is on compared to gamma.
Why is it like this? Why would you render overlay UI without Gamma space by default?