Search Unity

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

Discussion in 'Editor & General Support' started by trappist-1, Sep 14, 2019.

  1. trappist-1

    trappist-1

    Joined:
    Mar 26, 2017
    Posts:
    480
    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:
    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.IO;
    5.  
    6. #if UNITY_EDITOR
    7. using UnityEditor;
    8.  
    9. [CustomEditor(typeof(TakeScreenshot))]
    10. class TakeScreenshotEditor : Editor {
    11.    public override void OnInspectorGUI() {
    12.        TakeScreenshot baseScript = (TakeScreenshot)target;
    13.        if(GUILayout.Button("Take Screenshot")) {
    14.            var folder = Path.Combine( Application.dataPath, "Screenshots");
    15.            if (!Directory.Exists (folder)) {
    16.                Directory.CreateDirectory (folder);
    17.            }
    18.  
    19.            var path = Path.Combine (folder, "screenshot.png");
    20.            Debug.Log (path);
    21.            baseScript.CamCapture(path);
    22.        }
    23.  
    24.        if(GUILayout.Button("Set Frustum To Scene Object")) {
    25.            baseScript.SetFrustumToSceneObject();
    26.        }
    27.  
    28.        if(GUILayout.Button("Evaluate Curve")) {
    29.            if (baseScript.t > 1) {
    30.                baseScript.t = 0;
    31.            }
    32.  
    33.            baseScript.EvaluateCurve(baseScript.m_sceneObject, baseScript.t);
    34.            baseScript.t += 0.025f;
    35.        }
    36.        DrawDefaultInspector ();
    37.    }
    38. }
    39. /*Add this script to any camera which also has a randertexture*/
    40. [ExecuteInEditMode]
    41. public class TakeScreenshot : MonoBehaviour {
    42.  
    43.    public Camera m_camera { get {return GetComponent<Camera>();} }
    44.  
    45.  
    46.     public Texture2D CamCapture()
    47.    {
    48.        m_camera.Render();
    49.  
    50.        Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
    51.        image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
    52.        image.Apply();
    53.  
    54.        return image;
    55.    }
    56.  
    57.    public void CamCapture(string destPath)
    58.    {
    59.        RenderTexture currentRT = RenderTexture.active;
    60.        RenderTexture.active = m_camera.targetTexture;
    61.  
    62.        Texture2D image = CamCapture ();
    63.  
    64.        RenderTexture.active = currentRT;
    65.  
    66.        var Bytes = image.EncodeToPNG();
    67.        DestroyImmediate(image);
    68.  
    69.        var destDir = Path.GetDirectoryName (destPath);
    70.        if (!Directory.Exists (destDir)) {
    71.            Directory.CreateDirectory (destDir);
    72.        }
    73.        File.WriteAllBytes(destPath, Bytes);
    74.    }
    75.  
    76. }
    ---

    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?
     
    Last edited: Sep 14, 2019
  2. trappist-1

    trappist-1

    Joined:
    Mar 26, 2017
    Posts:
    480
    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??
     
  3. trappist-1

    trappist-1

    Joined:
    Mar 26, 2017
    Posts:
    480
    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?
     
    Last edited: Sep 14, 2019
  4. trappist-1

    trappist-1

    Joined:
    Mar 26, 2017
    Posts:
    480
    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:
    Code (csharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using System.IO;
    5.  
    6. #if UNITY_EDITOR
    7. using UnityEditor;
    8.  
    9. [CustomEditor(typeof(TakeScreenshot))]
    10. class TakeScreenshotEditor : Editor {
    11.    public override void OnInspectorGUI() {
    12.        TakeScreenshot baseScript = (TakeScreenshot)target;
    13.        if(GUILayout.Button("Take Screenshot")) {
    14.            var folder = Path.Combine( Application.dataPath, "Screenshots");
    15.            if (!Directory.Exists (folder)) {
    16.                Directory.CreateDirectory (folder);
    17.            }
    18.  
    19.            var path = Path.Combine (folder, "screenshot.png");
    20.            Debug.Log (path);
    21.            baseScript.CamCapture(path);
    22.        }
    23.  
    24.        if(GUILayout.Button("Set Frustum To Scene Object")) {
    25.            baseScript.SetFrustumToSceneObject();
    26.        }
    27.  
    28.        if(GUILayout.Button("Evaluate Curve")) {
    29.            if (baseScript.t > 1) {
    30.                baseScript.t = 0;
    31.            }
    32.  
    33.            baseScript.EvaluateCurve(baseScript.m_sceneObject, baseScript.t);
    34.            baseScript.t += 0.025f;
    35.        }
    36.        DrawDefaultInspector ();
    37.    }
    38. }
    39. /*Add this script to any camera which also has a randertexture*/
    40. [ExecuteInEditMode]
    41. public class TakeScreenshot : MonoBehaviour {
    42.  
    43.     public Camera m_camera { get {return GetComponent<Camera>();} }
    44.  
    45.  
    46.     public void FixColorSpace(Texture2D image)
    47.     {
    48.         for (int y = 0; y < image.height; y++)
    49.         {
    50.             for (int x = 0; x < image.width; x++)
    51.             {
    52.                 image.SetPixel(x, y, new Color(
    53.                     Mathf.Pow(image.GetPixel(x, y).r, 1f / 2.2f),
    54.                     Mathf.Pow(image.GetPixel(x, y).g, 1f / 2.2f),
    55.                     Mathf.Pow(image.GetPixel(x, y).b, 1f / 2.2f),
    56.                     Mathf.Pow(image.GetPixel(x, y).a, 1f / 2.2f)
    57.                 ));
    58.             }
    59.         }
    60.     }
    61.  
    62.     public Texture2D CamCapture()
    63.    {
    64.         m_camera.Render();
    65.  
    66.         Texture2D image = new Texture2D(m_camera.targetTexture.width, m_camera.targetTexture.height);
    67.        image.ReadPixels(new Rect(0, 0, m_camera.targetTexture.width, m_camera.targetTexture.height), 0, 0);
    68.  
    69.         FixColorSpace(image);
    70.         image.Apply();
    71.  
    72.        return image;
    73.    }
    74.  
    75.    public void CamCapture(string destPath)
    76.    {
    77.        RenderTexture currentRT = RenderTexture.active;
    78.        RenderTexture.active = m_camera.targetTexture;
    79.  
    80.        Texture2D image = CamCapture ();
    81.  
    82.        RenderTexture.active = currentRT;
    83.  
    84.        var Bytes = image.EncodeToPNG();
    85.        DestroyImmediate(image);
    86.  
    87.        var destDir = Path.GetDirectoryName (destPath);
    88.        if (!Directory.Exists (destDir)) {
    89.            Directory.CreateDirectory (destDir);
    90.        }
    91.        File.WriteAllBytes(destPath, Bytes);
    92.    }
    93.  
    94. }

    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.
     
    Last edited: Sep 14, 2019