Why Camera.Render() different than "Camera Preview"??

I see the “camera preview” looks perfect and the colors and brightness appear exactly as I would hope.

But when I use camera.render() to save the rendertexture to a file, the saved image is always super dark and looks completely different than the “camera preview”.

Here is the code I use to create the image texture:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

#if UNITY_EDITOR
using UnityEditor;

[CustomEditor(typeof(TakeScreenshot))]
class TakeScreenshotEditor : Editor {
   public override void OnInspectorGUI() {
       TakeScreenshot baseScript = (TakeScreenshot)target;
       if(GUILayout.Button("Take Screenshot")) {
           var folder = Path.Combine( Application.dataPath, "Screenshots");
           if (!Directory.Exists (folder)) {
               Directory.CreateDirectory (folder);
           }

           var path = Path.Combine (folder, "screenshot.png");
           Debug.Log (path);
           baseScript.CamCapture(path);
       }

       if(GUILayout.Button("Set Frustum To Scene Object")) {
           baseScript.SetFrustumToSceneObject();
       }

       if(GUILayout.Button("Evaluate Curve")) {
           if (baseScript.t > 1) {
               baseScript.t = 0;
           }

           baseScript.EvaluateCurve(baseScript.m_sceneObject, baseScript.t);
           baseScript.t += 0.025f;
       }
       DrawDefaultInspector ();
   }
}
/*Add this script to any camera which also has a randertexture*/
[ExecuteInEditMode]
public class TakeScreenshot : MonoBehaviour {

   public Camera m_camera { get {return GetComponent<Camera>();} }


    public Texture2D CamCapture()
   {
       m_camera.Render();

       Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
       image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
       image.Apply();

       return image;
   }

   public void CamCapture(string destPath)
   {
       RenderTexture currentRT = RenderTexture.active;
       RenderTexture.active = m_camera.targetTexture;

       Texture2D image = CamCapture ();

       RenderTexture.active = currentRT;

       var Bytes = image.EncodeToPNG();
       DestroyImmediate(image);

       var destDir = Path.GetDirectoryName (destPath);
       if (!Directory.Exists (destDir)) {
           Directory.CreateDirectory (destDir);
       }
       File.WriteAllBytes(destPath, Bytes);
   }

}

What’s even stranger is how Unity “almost” got it when I changed the colorformat of the Rendertextur, I see the colors and brightness are correct, but yet again there is a bug and Unity generates artifacts in the saved image:

I notice that one of my UI panels is showing in the image - it’s just a black space if I take screenshot while game is running.

I can’t get Unity to work correctly, no matter how I change lighting settings, or camera settings, or rendertexture settings, or meshrenderer settings, the final image is always darker than the preview.

What should I do? All I want to do is save an image exactly as it appears in the render preview, this should be easy, what am I doing wrong?

1 Like

I just tested this: SaveRenderTextureToFile.cs script from a GitHub member to verify if I was making a mistake. I get the exact same result in all of my projects where the selected rendertexture saves as a png that’s too dark to see.

Is this a bug? Has it ever been reported before? I am currently using Unity 2019.2.4f1 - win10.
But I have been having this issue since Unity 5. How can I fix this??

1 Like

I just changed the color space and the issue flipped!! Now the saved file is bright and colorful but the camera preview is too dark to see?

How can i have the camera preview and the rendertexture look exactly the same?

I have no Idea why Unity trying to flip color spaces… but I just solved my issue by manually converting the colorspace for each pixel.

Here’s the updated code:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System.IO;

#if UNITY_EDITOR
using UnityEditor;

[CustomEditor(typeof(TakeScreenshot))]
class TakeScreenshotEditor : Editor {
   public override void OnInspectorGUI() {
       TakeScreenshot baseScript = (TakeScreenshot)target;
       if(GUILayout.Button("Take Screenshot")) {
           var folder = Path.Combine( Application.dataPath, "Screenshots");
           if (!Directory.Exists (folder)) {
               Directory.CreateDirectory (folder);
           }

           var path = Path.Combine (folder, "screenshot.png");
           Debug.Log (path);
           baseScript.CamCapture(path);
       }

       if(GUILayout.Button("Set Frustum To Scene Object")) {
           baseScript.SetFrustumToSceneObject();
       }

       if(GUILayout.Button("Evaluate Curve")) {
           if (baseScript.t > 1) {
               baseScript.t = 0;
           }

           baseScript.EvaluateCurve(baseScript.m_sceneObject, baseScript.t);
           baseScript.t += 0.025f;
       }
       DrawDefaultInspector ();
   }
}
/*Add this script to any camera which also has a randertexture*/
[ExecuteInEditMode]
public class TakeScreenshot : MonoBehaviour {

    public Camera m_camera { get {return GetComponent<Camera>();} }


    public void FixColorSpace(Texture2D image)
    {
        for (int y = 0; y < image.height; y++)
        {
            for (int x = 0; x < image.width; x++)
            {
                image.SetPixel(x, y, new Color(
                    Mathf.Pow(image.GetPixel(x, y).r, 1f / 2.2f),
                    Mathf.Pow(image.GetPixel(x, y).g, 1f / 2.2f),
                    Mathf.Pow(image.GetPixel(x, y).b, 1f / 2.2f),
                    Mathf.Pow(image.GetPixel(x, y).a, 1f / 2.2f)
                ));
            }
        }
    }

    public Texture2D CamCapture()
   {
        m_camera.Render();

        Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
       image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
  
        FixColorSpace(image);
        image.Apply();

       return image;
   }

   public void CamCapture(string destPath)
   {
       RenderTexture currentRT = RenderTexture.active;
       RenderTexture.active = m_camera.targetTexture;

       Texture2D image = CamCapture ();

       RenderTexture.active = currentRT;

       var Bytes = image.EncodeToPNG();
       DestroyImmediate(image);

       var destDir = Path.GetDirectoryName (destPath);
       if (!Directory.Exists (destDir)) {
           Directory.CreateDirectory (destDir);
       }
       File.WriteAllBytes(destPath, Bytes);
   }

}

Is this workaround the only way to fix Unity’s color space bug? It runs for all pixels (which is very expensive) and I don’t like it much.

3 Likes