How to save manually save a PNG of a camera view

Someone may find this useful. I looked for something similar and ran into all kinds of problems, so thought I would share my eventual solution.

If you attach this script to any camera that has a valid target texture in its settings, it will save a PNG of that snapshot to your hard drive (note the folder name in my path) … which you can later use for whatever purpose. I have this script attached to a camera that only renders the game background, and I use the images later to create randomized backgrounds.

This demo script uses the F9 key to capture the image, but you can make it automatically capture at random times (start of each level or some other time when there’s no action) if you wish.

using System.IO;
using UnityEngine;

public class SR_RenderCamera : MonoBehaviour {

    public int FileCounter = 0;

    private void LateUpdate()
    {
        if (Input.GetKeyDown(KeyCode.F9))
        {
            CamCapture();   
        }
    }

    void CamCapture()
    {
        Camera Cam = GetComponent<Camera>();

        RenderTexture currentRT = RenderTexture.active;
        RenderTexture.active = Cam.targetTexture;

        Cam.Render();

        Texture2D Image = new Texture2D(Cam.targetTexture.width, Cam.targetTexture.height);
        Image.ReadPixels(new Rect(0, 0, Cam.targetTexture.width, Cam.targetTexture.height), 0, 0);
        Image.Apply();
        RenderTexture.active = currentRT;

        var Bytes = Image.EncodeToPNG();
        Destroy(Image);

        File.WriteAllBytes(Application.dataPath + "/Backgrounds/" + FileCounter + ".png", Bytes);
        FileCounter++;
    }
   
}
20 Likes

Nice, thanks!

A bit old but I approve it to be dug up. :slight_smile:

I hope this script works with 2019.1.

About Application.DataPath: Unity - Scripting API: Application.dataPath

1 Like

Thanks so much for this script! I can confirm it works in Unity 2019.3.0f6. I’ve made some performance, ease of use, and convention improvements to the script:

using System.IO;
using UnityEngine;

public class CameraCapture : MonoBehaviour
{
    public int fileCounter;
    public KeyCode screenshotKey;
    private Camera Camera
    {
        get
        {
            if (!_camera)
            {
                _camera = Camera.main;
            }
            return _camera;
        }
    }
    private Camera _camera;

    private void LateUpdate()
    {
        if (Input.GetKeyDown(screenshotKey))
        {
            Capture();
        }
    }

    public void Capture()
    {
        RenderTexture activeRenderTexture = RenderTexture.active;
        RenderTexture.active = Camera.targetTexture;

        Camera.Render();

        Texture2D image = new Texture2D(Camera.targetTexture.width, Camera.targetTexture.height);
        image.ReadPixels(new Rect(0, 0, Camera.targetTexture.width, Camera.targetTexture.height), 0, 0);
        image.Apply();
        RenderTexture.active = activeRenderTexture;

        byte[] bytes = image.EncodeToPNG();
        Destroy(image);

        File.WriteAllBytes(Application.dataPath + "/Backgrounds/" + fileCounter + ".png", bytes);
        fileCounter++;
    }
}

Let me know if you have any problems with it! Again, big thanks to @wileyjerkins for making the original script! :smile:

11 Likes

I am stealing these improvements! Good job!

1 Like

Thanks! Keep in mind that like the original, it unfortunately doesn’t have any way to render gizmos since it is still using a render texture. Any ideas regarding taking a screenshot with gizmos?

That is a complicated thing. Essentially you have to create your own gizmos, but fortunately someone else has done it for you. GitHub - popcron/gizmos: Used for drawing runtime gizmos in builds and editor (Unity3D)

1 Like

Neat! Can’t wait to try it out. Too bad there isn’t any setting to make vanilla gizmos render in the camera/builds, though. :frowning:

Thank you so much for writing these scripts. I do however hit an odd issue with the color of the output PNG.

The output PNG is considerably darker than the actual camera/game feed in Unity. Any ideas?

This is the script I’ve used, as per the examples included here. I created a completely new Scene, added a RenderTexture to the Main Camera, hit Play and pressed the relevant key to trigger it. The attached images are the output png (darker) and a screengrab of my Unity Editor’s Game window.

using System.IO;
using UnityEngine;

public class CameraCapture : MonoBehaviour
{
    public KeyCode screenshotKey;
    private Camera _camera;

    void Start () {
      _camera = GetComponent<Camera>();
    }

    private void LateUpdate()
    {
        if (Input.GetKeyDown(screenshotKey))
        {
            Capture();
        }
    }

    public void Capture()
    {
        RenderTexture activeRenderTexture = RenderTexture.active;
        Debug.Log(_camera);
        RenderTexture.active = _camera.targetTexture;

        _camera.Render();

        Texture2D image = new Texture2D(_camera.targetTexture.width, _camera.targetTexture.height);
        image.ReadPixels(new Rect(0, 0, _camera.targetTexture.width, _camera.targetTexture.height), 0, 0);
        image.Apply();
        RenderTexture.active = activeRenderTexture;

        byte[] bytes = image.EncodeToPNG();
        Destroy(image);

        Debug.Log(bytes);

        File.WriteAllBytes(Path.Combine(Application.persistentDataPath, "output.png"), bytes);
    }
}

Thanks in advance for any help.

Just to post a fix to my own problem. My project (not sure how) had switched to Linear Color Space. It’s in the Project Settings -> Player -> Other Settings in case anyone else hits this issue, this should be set to Gamma

1 Like

Your project didn’t switch to linear by itself, you switched it. You need linear to use Post Processing properly.

So is there a way to use Linear AND fix the “darkened”-issue?

1 Like

If you’re taking a regular screenshot you could just use the ScreenCapture API

2 Likes

It doesn’t work on Unity 2019.4.7f1. There is NullReferenceException on the line “Texture2D image = new Texture2D(Camera.targetTexture.width, Camera.targetTexture.height);” (Camera.targetTexture is null)

3 Likes

That happened to me too. Then I realized you need to render the camera into a valid texture for it to work. Read the original post carefully again and you will see it mentioned this

1 Like

What is the use of fileCounter here ?

Hey Dude it’s totally normal to have this issue because you need to create a Render Texture using [Assets > Create > Render Texture] and assign it to Target Texture in your Camera component. It’s from here where it take your Screenshot Size —Texture2D(Camera.targetTexture.width, Camera.targetTexture.height)—.
For more details about RenderTexture:

1 Like

I second this, I’m having the same issue. I need output images to have the proper coloring, but also use post-processing!

1 Like

Looks like fileCounter is used to name the captured files, saved under the “Backgrounds” folder. I think is just so you can press the capture function key (or call the API) lots of times and it just increments a counter to name the captured file and avoid re-saving over top of others. So, this will work alright for each time you run the app, but between starts it will reset to zero and begin writing back over old files.

Hey this is pretty great! If the default hotkey is F9, there was a conflict as that key was already in use for unity 2019.4 (for screen recording) which was doubly confusing. I remapped it to F10 and am able to take a screenshot.