Encoding to png gives very dark results

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.

var projectionCameraGO = Instantiate(Resources.Load<GameObject>("DetailProjectionCamera"));
        projectionCameraGO.transform.position = transform.position - projectionCameraGO.transform.forward * 10f;
        var projectionCamera = projectionCameraGO.GetComponent<Camera>();

        var gfxFormat = GraphicsFormat.R8G8B8A8_UNorm;
        var rTex = projectionCamera.targetTexture;
        projectionCamera.orthographicSize = rTex.height / 2f / 16f;
        TextureCreationFlags flags = TextureCreationFlags.None;

        Texture2D tex = new Texture2D(rTex.width, rTex.height, gfxFormat, flags);
        RenderTexture.active = rTex;
        tex.ReadPixels(new Rect(0, 0, rTex.width, rTex.height), 0, 0);
        tex.Apply();
      
        tmpTestTexture = tex;
        byte[] bytes = tex.EncodeToPNG();
        System.IO.File.WriteAllBytes(Application.dataPath + "/" + gameObject.name + "_projection.png", bytes);

Thanks very much in advance for your help

8360391--1100658--Quad_projection.png
8360391--1100661--Capture.JPG

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 Unity - Scripting API: RenderTexture.RenderTexture

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???

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.

1 Like

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.

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?

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.

This has been answered many times before, for example here:

It’s not a bug. You have to understand linear vs. non-linear (SRGB) color space.

1 Like