Search Unity

Linear space for scene, gamma for UI?

Discussion in 'UGUI & TextMesh Pro' started by SunnySunshine, Mar 10, 2015.

  1. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    976
    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.
     
  2. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    976
    Well, I tried changing row 85 in UI-Default.shader to:

    Code (CSharp):
    1.                 return pow(color, 1/2.2);
    And that does make it look a bit more like before. Not the same but a bit better.

     
    Last edited: Mar 10, 2015
    robal1991 and SparrowGS like this.
  3. RV1

    RV1

    Joined:
    Nov 18, 2012
    Posts:
    68
    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.
     

    Attached Files:

  4. SunnySunshine

    SunnySunshine

    Joined:
    May 18, 2009
    Posts:
    976
  5. RV1

    RV1

    Joined:
    Nov 18, 2012
    Posts:
    68
    Thanks, as it happens I was reading that very same page earlier :)

    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.
     
  6. RV1

    RV1

    Joined:
    Nov 18, 2012
    Posts:
    68
    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.
     
    Last edited: Apr 26, 2015
    mkapahnke and SunnySunshine like this.
  7. Kayac-TrungDV

    Kayac-TrungDV

    Joined:
    Mar 19, 2013
    Posts:
    2
  8. RV1

    RV1

    Joined:
    Nov 18, 2012
    Posts:
    68
    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:

    http://forum.unity3d.com/threads/bug-with-bypass-srgb-sampling.282469/

    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 :(
     
  9. v.nikolaev

    v.nikolaev

    Joined:
    Sep 11, 2015
    Posts:
    7
    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.
     

    Attached Files:

  10. glitchers

    glitchers

    Joined:
    Apr 29, 2014
    Posts:
    64
    Is there any update on this? I've got a new material for transparent elements similar to @SunnySunshine .
    Code (CSharp):
    1.  
    2. #if !defined(UNITY_COLORSPACE_GAMMA)
    3.     col.a = pow(col.a, 2.2);
    4. #endif
    5.  
    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?
     
  11. renman3000

    renman3000

    Joined:
    Nov 7, 2011
    Posts:
    6,697
    I just tried switching to Linear, Text Mesh Pro, does not like this.
     
  12. mndcr

    mndcr

    Joined:
    Jun 13, 2013
    Posts:
    3
    I helped myself as follows:

    1) I made a helper class

    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. public class UiColors
    4. {
    5.     public static Color FixUiColor(Color col)
    6.     {
    7.         if (QualitySettings.activeColorSpace == ColorSpace.Gamma)
    8.             return col;
    9.        
    10.         float r = Mathf.GammaToLinearSpace(col.r);
    11.         float g = Mathf.GammaToLinearSpace(col.g);
    12.         float b = Mathf.GammaToLinearSpace(col.b);
    13.         float a = col.a;
    14.         return new Color(r,g,b,a);
    15.     }
    16. }
    17.  
    2) I used the helper class to convert colors passed as params in my custom UI-shaders:

    Code (CSharp):
    1.         material.SetColor("glowColor", UiColors.FixUiColor(glowColor));
     
  13. Zero-Lu

    Zero-Lu

    Joined:
    Dec 31, 2015
    Posts:
    13
    For those of you searching online for a solution, use the following shader. It simply works.
     

    Attached Files:

    DwinTeimlon likes this.
  14. DwinTeimlon

    DwinTeimlon

    Joined:
    Feb 25, 2016
    Posts:
    300
    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?
     
  15. UnbridledGames

    UnbridledGames

    Joined:
    May 12, 2020
    Posts:
    139
    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.
     
  16. JonathanUbi

    JonathanUbi

    Joined:
    Nov 12, 2021
    Posts:
    1
    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 :
    Code (CSharp):
    1. return half4(1.h * pow(i.uv.x, 2.2), 0.h, 0.h, i.uv.x);
    With white background:
    Result :
    upload_2022-3-8_18-13-47.jpeg

    Target :
    upload_2022-3-8_18-13-47.jpeg

    With black background:
    Result :
    upload_2022-3-8_18-13-47.jpeg

    Target :
    upload_2022-3-8_18-13-47.jpeg


    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: https://cmwdexint.com/2019/05/30/3d-scene-need-linear-but-ui-need-gamma/. 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." (https://docs.unity3d.com/ScriptReference/RenderTexture-sRGB.html)
     
  17. Deleted User

    Deleted User

    Guest

    I decided to report a bug and what I got:
    Unity Team doesn't see it as a problem.