teaching machines

Capturing High-resolution Screenshots in Unity

May 21, 2018 by . Filed under howto, public, unity.

A student and I were collaborating on a poster. We needed an image of a Unity scene that for a 4-foot-by-4-foot poster. Capturing our screens at their normal resolutions resulted in images that got fuzzy when scaled. So, we worked up a quick method for taking screenshots at higher resolutions with a render texture. Perhaps they shouldn’t be called screenshots when they don’t match the screen…

These are the steps we followed:

  1. Create a new Render Texture asset. Set its resolution, antialiasing, and other properties according to your whimsy.
  2. We need a camera to render the scene into this texture on demand. We could create a camera specifically dedicated to this task, or we could just borrow the main camera for a moment. The latter choice means less duplication of configuration, so that’s what we’ll do here. Create the following script:
    using UnityEngine;
    using System.IO;
    
    public class Screenshotter : MonoBehaviour {
      public RenderTexture renderTexture;
    
      void Update() {
        // Here we take screenshots when the player hits the S key, but it could
        // just as well have been a button click, time elapsing, or some other
        // condition.
        if (Input.GetKeyDown(KeyCode.S)) {
          TakeScreenshot();
        }
      }
    
      public void TakeScreenshot() {
        // Force a render to the target texture.
        Camera.main.targetTexture = renderTexture;
        Camera.main.Render();
    
        // Texture.ReadPixels reads from whatever texture is active. Ours needs to
        // be active. But let's remember the old one so we can restore it later.
        RenderTexture oldRenderTexture = RenderTexture.active;
        RenderTexture.active = renderTexture;
    
        // Grab ALL of the pixels.
        Texture2D raster = new Texture2D(renderTexture.width, renderTexture.height);
        raster.ReadPixels(new Rect(0, 0, renderTexture.width, renderTexture.height), 0, 0);
        raster.Apply();
    
        // Write them to disk. Change the path and type as you see fit.
        File.WriteAllBytes("screenshot.png", raster.EncodeToPNG());
    
        // Restore previous settings.
        Camera.main.targetTexture = null;
        RenderTexture.active = oldRenderTexture;
    
        Debug.Log("Screenshot saved.");
      }
    }
    
  3. Add the script to your main camera—or any singleton object in your scene. Wire in the render texture to its public variable.

This will capture normal GameObjects visible to the camera. But it will not capture any Canvas that is a screen-space overlay. I don’t know if this is a bug or technical impossibility. Hopefully you can switch to camera or world space.

I assure you, the poster looked sharp.