Search Unity

sRGB shader color space conversion

Discussion in 'Shaders' started by Longh10, May 3, 2017.

  1. Longh10

    Longh10

    Joined:
    May 3, 2017
    Posts:
    3
    I'm creating two RenderTextures as RenderTextureFormat.Default + RenderTextureReadWrite.sRGB, rendering into the first, then blitting from the first to the second with Graphics.Blit. When I do this, the first texture is correct (viewed in the inspector), but the second texture is much darker than the first.

    From the documentation, it sounds like this shouldn't happen. Reading the first texture should convert from sRGB to linear, the shader should operate in linear space (the default blit shader in this case), then the output should be converted from linear to sRGB to write the second texture. That's described at https://docs.unity3d.com/Manual/LinearRendering-GammaTextures.html ("Linear color space and non-HDR"), but it seems like only one of the two conversions is happening.

    https://forum.unity3d.com/threads/s...-rt-in-linear-mode-cause-wrong-colors.355713/ works around this by setting the textures to linear, but that seems wrong, since storing linear color in an 8BPP texture will lose color resolution and cause banding (that's the whole point of the sRGB conversions).
     
  2. Longh10

    Longh10

    Joined:
    May 3, 2017
    Posts:
    3
    If I return fixed4(0,0.5,0,1) when rendering into the viewport (no intermediary texture) and sample the color, I get 0.737. Linear 0.5 was correctly converted to sRGB 0.737.

    If I do the same into an sRGB RenderTexture and view it in the inspector, I get 0.5. It seems like it's not converting the output back to sRGB when writing to the sRGB RenderTexture. If I read that color back in the next pass (to make sure any conversions happening in the inspector aren't confusing things) I get 0.212 instead of 0.5. The read is converting from sRGB to linear (since the previous texture is supposed to be sRGB), but it was never converted from linear to sRGB when it was written.

    I'm guessing RenderTexture sRGB conversions are broken and only happen in one direction.
     
  3. Longh10

    Longh10

    Joined:
    May 3, 2017
    Posts:
    3
    Not that there's anyone out there, but for whoever else reads this in the future...

    RenderTextureReadWrite.sRGB is supposed to turn on sRGB conversions for reading and writing, but it actually only turns on conversions for reads. To turn them on for writes, you need to set GL.sRGBWrite to true manually (or set it to GL.sRGBWrite = renderTexture.sRGB).

    It makes sense that reading and writing are controlled separately, but it doesn't make sense that one is a RenderTexture property and one is an immediate property, and this isn't documented (the documentation claims sRGB affects reads and writes).
     
    Alex_May, gz7190, SimonCh and 3 others like this.
  4. AwesomeAlexx

    AwesomeAlexx

    Joined:
    Jan 30, 2016
    Posts:
    18

    Hi,

    Thanks for this I'm losting my soul trying to make it work, I also understood I should use GL.sRGBWrite but to be honest I don't understand how it work and the doc doesn't helped me.
    Can you send a piece of code using that properly to help ?

    I'm doing this :

    Code (CSharp):
    1. void OnPreRender()
    2.     {
    3.         RenderTexture.ReleaseTemporary(renderTexture);
    4.         renderTexture = RenderTexture.GetTemporary(_outputTexture.width, _outputTexture.height, 24);
    5.         currentCam.targetTexture = renderTexture;
    6.     }
    7.  
    8.  
    9.  
    10. private void OnRenderImage(RenderTexture source, RenderTexture destination)
    11.     {
    12.                 currentCam.targetTexture = null;
    13.                 GL.sRGBWrite = true;
    14.                 Graphics.Blit(source, null as RenderTexture, _distortionMat);
    15.  
    16.             }
    17.         }
    18.     }

    PS : Forgot to say I'm loading the texture from server like this :

    Code (CSharp):
    1. _blendMap = new Texture2D(_DistortionMapWidth, _DistortionMapHeight, TextureFormat.ARGB32, false, true);
    2.  
    3. using (UnityWebRequest imgRequest = UnityWebRequestTexture.GetTexture(filename))
    4.         {
    5.             yield return imgRequest.SendWebRequest();
    6.  
    7.             _blendMap = ((DownloadHandlerTexture)imgRequest.downloadHandler).texture;
    8.             _blendMap.anisoLevel = 0;
    9.             _blendMap.filterMode = FilterMode.Point;
    10.             _blendMap.wrapMode = TextureWrapMode.Clamp;
    11.         }