Search Unity

  1. We are migrating the Unity Forums to Unity Discussions. On July 12, the Unity Forums will become read-only.

    Please, do not make any changes to your username or email addresses at id.unity.com during this transition time.

    It's still possible to reply to existing private message conversations during the migration, but any new replies you post will be missing after the main migration is complete. We'll do our best to migrate these messages in a follow-up step.

    On July 15, Unity Discussions will become read-only until July 18, when the new design and the migrated forum contents will go live.


    Read our full announcement for more information and let us know if you have any questions.

Question Encoding to png gives very dark results

Discussion in 'General Graphics' started by gwelkind, Aug 13, 2022.

  1. gwelkind

    gwelkind

    Joined:
    Sep 16, 2015
    Posts:
    66
    Within the editor, I'm trying to instantiate a Camera, capture it's frame to a premade render texture, blit these onto a temporary texture, then send bytes from the temporary texture to a png file. Ultimately, I'm trying to get a specialized screenshot of objects when I press a certian button in edit mode. Using Unity 2021.3.6f1 fwiw

    Where I'm running into issues seems to be when I actually call `tex.EncodeToPNG()`

    The reason I believe this is that I'm storing the intermediate texture as a public serialized variable, and inspecting it, and it looks OK. The png produced is really really dark, though colors are still distinguishable.

    I've included pictures of the produced png and the intermediate texture.

    I've tried using several other graphics formats for my render texture (while keeping them in sync to the Texture2D), none of them seem to make a difference.


    Code (CSharp):
    1.        
    2. var projectionCameraGO = Instantiate(Resources.Load<GameObject>("DetailProjectionCamera"));
    3.         projectionCameraGO.transform.position = transform.position - projectionCameraGO.transform.forward * 10f;
    4.         var projectionCamera = projectionCameraGO.GetComponent<Camera>();
    5.  
    6.         var gfxFormat = GraphicsFormat.R8G8B8A8_UNorm;
    7.         var rTex = projectionCamera.targetTexture;
    8.         projectionCamera.orthographicSize = rTex.height / 2f / 16f;
    9.         TextureCreationFlags flags = TextureCreationFlags.None;
    10.  
    11.         Texture2D tex = new Texture2D(rTex.width, rTex.height, gfxFormat, flags);
    12.         RenderTexture.active = rTex;
    13.         tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
    14.         tex.Apply();
    15.      
    16.         tmpTestTexture = tex;
    17.         byte[] bytes = tex.EncodeToPNG();
    18.         System.IO.File.WriteAllBytes(Application.dataPath + "/" + gameObject.name + "_projection.png", bytes);
    19.  
    20.  

    Thanks very much in advance for your help
     

    Attached Files:

  2. gwelkind

    gwelkind

    Joined:
    Sep 16, 2015
    Posts:
    66
    I suspect this has to do with colorspace/sRGB, but MAN does Unity make this hard to understand.

    There's RenderTextureFormat, DefaultFormat, GraphicsFormat and TextureFormat, all of which contain enum names which aren't consistent or clearly intercompatible.

    It seems to enforce RenderTexture as sRGB space, I'd have to use this constructor https://docs.unity3d.com/ScriptReference/RenderTexture-ctor.html

    but it's not at all clear to me which RenderTextureFormat I should use and how to build a Texture2D which is compatible with my selected RenderTextureFormat... Why can't these just share one enum???
     
  3. arkano22

    arkano22

    Joined:
    Sep 20, 2012
    Posts:
    2,063
    RenderTextures are objects that live in GPU video memory. They are not compressed and cannot be serialized to disk. Texture2D however lives in CPU memory and can be compressed, so they have a very different set of formats available. These are what RenderTextureFormat and TextureFormat are, respectively.

    DefaultFormat returns the default platform format for a specific kind of texture content (HDR, LDR, stencil, depth, etc).

    GraphicsFormat is relatively new, and I suspect it's supposed to eventually replace both RenderTextureFormat and TextureFormat.

    Your graphics format is GraphicsFormat.R8G8B8A8_UNorm, which is a normalized format with 8 bit precision for each channel. Instead of using this to create your Texture2D, try using rTex.graphicsFormat.
     
    Torbach78 likes this.
  4. gwelkind

    gwelkind

    Joined:
    Sep 16, 2015
    Posts:
    66
    This is extremely helpful for my understanding. In the end, I needed to use sRGB for both in order to get this to work.

    Thank you very much for this response, I couldn't find a decent explanation of it anywhere and you outlined it with perfect clarity.
     
  5. xxKakazexx

    xxKakazexx

    Joined:
    Nov 9, 2022
    Posts:
    2
    I am running into the same issue as you have, Gwelkind. Would it be possible to speak more on how you have managed to create a Texture2D with graphicsFormat rather than with a TextureFormat?
     
  6. xxKakazexx

    xxKakazexx

    Joined:
    Nov 9, 2022
    Posts:
    2
    I had some success with creating a CustomRenderTexture that has the SRGB bool set to true. It has the interesting effect of displaying an image in editor that is too light, but encoding a PNG that is visually correct.
     
  7. georgerh

    georgerh

    Joined:
    Feb 28, 2020
    Posts:
    72
    tok23 likes this.