Bug with Bypass sRGB Sampling?

We recently switched our project from Gamma space to Linear space to take advantage of its benefits for lighting. However, our UI looks significantly worse in Linear space.

We would like to fix this issue without re-authoring every UI texture in our game. The last section of this article from Unity seems to indicate that if you want to make your UI look like it did in Gamma space, you should be able to just set the texture to “Bypass sRGB Sampling”. However, this flag did not fix my problem; in fact, it made the UI look worse.

I have created and attached a small project to illustrate the problem. (The project was created in Unity5 beta 13, so it is not compatible with Unity4, sorry! However, I have observed this problem in Unity4 as well, so it seems independent of Unity version.)

There are six important elements on screen in the project:

  1. A UI icon from our game without “Bypass sRGB Sampling” set.
  2. A UI icon from our game with “Bypass sRGB Sampling” set.
  3. An alpha gradient (rgb is white, a goes from 0-1) without “Bypass sRGB Sampling” set.
  4. An alpha gradient (rgb is white, a goes from 0-1) with “Bypass sRGB Sampling” set.
  5. A greyscale gradient (rgb goes from 0-1, no a) without “Bypass sRGB Sampling” set.
  6. A greyscale gradient (rgb goes from 0-1, no a) with “Bypass sRGB Sampling” set.

By default, the project is set to Gamma space. Both versions of the UI icon look the same and look correct. (See attached image “GammaSpace.png”.)

If you switch the project to Linear space, you will see that the icon looks much worse. In particular, the alpha values are much higher, making the glow much more opaque than intended. If you look at the icon that is set to “Bypass sRGB Sampling”, you will see that it has the same problem with alpha, but it also has the problem of making the pink color much brighter than it should be. (See attached image “LinearSpace.png”.)

We can diagnose what is happening to the icon by looking at how the change from Gamma to Linear affects the alpha and greyscale gradients with and without “Bypass sRGB Sampling”.

If you look at the alpha gradient when switching from Gamma to Linear, you will notice they both have some kind of curve applied to them causing the alpha values to increase. I would expect that “Bypass sRGB Sampling” would make the alpha look the same as it did in Gamma. Is this a bug?

If you look at the greyscale gradient when switching from Gamma to Linear, you will notice that the gradient without the “Bypass sRGB Sampling” flag set looks the same. But the texture with the flag set has its greyscale values increased. This is the opposite of what I would expect. Is this a bug?

Can anyone confirm that this behavior is a bug, or justify why it behaves this way?

(As an aside, I have observed a bug that is less concerning, since it is easy to circumvent. When you switch the project from Gamma to Linear for the first time since launching the editor, rgb values seem to not be influenced by the “Bypass sRGB Sampling” flag at all. However if you switch back to Gamma, then back to Linear, it should appear how I described it previously. See the attached image “LinearSpace_Bug.png” for an example of what this looks like.)

1864370–119616–Bypass_sRGB_Bug.unitypackage (58.3 KB)

Can you place log a bug for this so we can investigate in more detail (please paste the bug# here)

Is this on Unity5 or 4.6?

The attached packed is not working with Unit 4.6

Sorry for the sluggish reply; I’m in the U.S. so I was out for Thanksgiving.

I just submitted a bug report through Unity (case number 652537).

As I said before, the project was created in Unity 5 beta, so unfortunately, it will not work in Unity 4. However, I have done small tests and confirmed that it is indeed a problem in Unity 4 as well. If you would like, I can recreate the project in Unity 4 to demonstrate that it is an issue there as well. (It will just take me an hour or so to set everything up again.)

I sent an email response, but in case anyone is curious:

Hi,

So what you are seeing is actually intended behavior, even though it really may not seem that way. Let me explain.

*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.

*When switching to linear mode it (greatly) changes how blending works, this is because when sampling only the rgb channels are linearised as it is assumed the alpha is a linear scale representation already. That’s just how it’s designed (http://http.developer.nvidia.com/GPUGems3/gpugems3_ch24.html). This is all well and good for normal scene rendering, but it does cause some issues when it comes to UI. Essentially in your example whats happening is that alpha is getting pushed out because it is assumed to be in linear when it’s been authored in gamma space. If you take a look at this link you can see that when alpha blending gamma vs linear the results are quite different:
linear thingy - Google Sheets
Feel free to input your own values and have a play.

The issue is basically that the content has been authored for gamma blending but not linear. There are two ways this can be fixed:

  1. Reauthor the content for linear blending (this is supported in photoshop).
  2. Add a workaround in the shader if in linear space to ‘pretend’ the alpha from the source texture is in the correct format. An example of how to do that is here (http://hastebin.com/webexacepa.avrasm).

-Tim C

Note: the shader workaround requires Unity 5.

Thank you for the detailed reply, Tim!

Unfortunately, fixing this with option 1 (reauthoring our UI textures for Linear), is not a good solution for our project. We still need to support gamma space for the mobile version of our game. So this would require us to develop a system for swapping textures at both build time (for maximum space efficiency) and run time (for easy testing in the editor).

For us, option 2 (using a custom shader to correct the issue) is the most feasible solution. However, the shader at the link that you provided does not actually fix the issue. I have modified the project I attached in my first post to use the custom shader to demonstrate the problem.

In the below image, the project was set to Gamma color space. As expected, the icon with the normal Unlit/Transparent shader and the icon with the correction shader you provided look the same

In the below image, the project was set to Linear color space. As expected, the icon with the normal Unlit/Transparent shader looks different in Linear space. However, even the icon with the correction shader looks different-its semitransparent glow has been virtually eliminated.

Basically, all the correction shader is doing is raising the alpha channel to the 2.2 power if we are in Linear color space. Based on the results of testing this, I think it is safe to say that this is not the correct way of fixing the issue.

Can you think of any other calculations we would need to do in the shader to get this to work?

1891723–121793–GammaLinearCustomShaderTest.unitypackage (66.4 KB)

Well the blending IS different between the two modes and the only way to get them to look exactly the same is to reauthor the content. The shader trick is a workaround which gets you some of the way there, but it’s not perfect.

It seems strange to me that there is no way to fix this perfectly with a shader. Reauthoring a texture amounts to you opening up Photoshop and applying some function to each texel. If this were in fact able to fix the issue, then it would be possible to put that function in the shader instead.

Perhaps the reason this can’t be fixed elegantly in the shader is that the function to correct the textures cannot be represented elegantly with a simple curve (ex: polynomial, exponential, etc…)? Wouldn’t it at least be possible to get a better approximation of the correction function than the exponential with the power of 2.2 by using some sort of piecewise function?

Unfortunately it can’t be emulated in the shader due to blend units being fixed function. We don’t have the control to modify incoming destination values pre blending, it’s all configured via the sRGB texture format / float texture format :frowning:

semi related and interesting.

I see. So if the main problem is that we can’t apply a function on the destination values read for blending, then this probably can’t be “perfectly” fixed (meaning that when rendering a sprite to the screen, each pixel will look exactly the same as it did in gamma space) with a shader or reauthoring textures.

I guess the best we can do is try to fake it either with a curve in the shader or reauthoring textures, but it will never be exactly the same in all cases.

Hopefully one day we’ll have programmable blending!

A few weeks ago I discovered a workaround for this, so I thought I would post it here in case others run into this problem.

As we know, the blending does not look the same because previously we were bending in gamma space, but now we are blending in linear space.

To get around this for the UI, what we can do is use a post effect to encode gamma into the back buffer right before the UI camera renders. Then, assuming the UI textures are set to “Bypass sRGB Sampling”, the colors returned by the pixel shaders for the UI will be in gamma space and will blend properly with the gamma space back buffer. Finally, once the UI camera has finished rendering, we simply apply another post effect to decode the gamma from the back buffer.

I don’t know if this has changed lately, but I’m seeing big problems with the new UI and linear mode.

Using Unity 5.0.1f1 in linear color space.

I’ve isolated one problem to be the alpha that you set as the sprite color.


So for some reason the white square ends up too bright on linear mode, while in gamma it looks as expected.

This has nothing to do with the texture sRGB bypass, as the texture is unaffected by the gamma/linear mode.

What could be reason for this? It also makes tweaking colors for the sprites in the linear space a bit pain, as the expected 50% is way too strong, and the actual noticiable alpha fading happens from 50% ↔ 0%.

For example I made a full screen sprite that is used to fade the screen to black. I animate the color alpha linearly, but the result looks really weird. The longer the fade is, the more wrong it looks as first nothing seems to happen, and then suddenly it fades to black.

Here’s another example. White hexagon sprite where the color has been set to red.

Bottom one has color (1,0,0,1). Top-left has color (1,0,0,0.5) i.e. 50% opacity. It looks almost as bright as the 100% opacity one. Top-right has color (0.5, 0, 0, 1) i.e 100% opacity but intensity has been reduced to 50%.

In linear mode both upper icons look exactly the same.

Your images look broken. Did you embed them into your post correctly?

Hi, maybe you can tell how to change color space for textures in photoshop?

Also, i have modified unity’s UI-Default.shader, and now it seems to display UI sprites with alpha gradient almost correct. The shader, that you proposed does not hide the sprite by the Mask ui-component.

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

So, what the resolution? Is there any way to fix this problem without reauthoring content?

Could you please share this workaround or explained a bit more? I tried to write PostEffect that simply do pow(color, 2.2) on the src render texture, but it applies this on the background scene too. And how do you apply this solution in edit mode?