Screenshot from a rendertexture (non-main camera) to fit sprite (in 3D space) exactly – Unity

How do you adjust or otherwise set up a screenshot to have the exact dimensions of a particular sprite that is in 3D space (so, Gameobject with SpriteRenderer).

The render camera is set up directly above the sprite but has some blank space outside the boundaries of the sprite. I’m not sure how to precisely adjust the viewport of the rendercamera.

Here’s what the scene looks like. (Actually this is a quick and simplified version of my current scene, to showcase my specific problem) And yes, I’m trying to take a specific screenshot at runtime. The main camera is at an arbitrary angle but the render camera can be centered to the sprite renderer. In other words, the screenshot I am taking looks like the one in the Camera Preview in bottom right corner in the pic below. How do I crop out (or adjust the viewport to exclude) the extra part beyond the size of the sprite renderer?

enter image description here

Answer

You can calculate the size of the frustum in world space:

float frustumHeight = 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
float frustrumWidth = frustumHeight * camera.aspect;

Then if you know the painting’s height and width in world units, you can calculate how big of the center piece of the screen it’s occupying:

float widthPortion = paintingWidth / frustrumWidth;
float heightPortion = paintingHeight / frustrumHeight;

Then you know how much of the center of the renderTexture to hide:

float leftRightCrop = (1f-widthPortion)/2f;
float topBottomCrop = (1f-heightPortion)/2f;

So to copy it from your camera’s render texture you can do this in OnPostRender:

RenderTexture currentRT = RenderTexture.active;
RenderTexture.active = Cam.targetTexture;
 
Cam.Render();

int croppedPixelWidth = widthPortion*camera.pixelWidth;
int croppedPixelHeight = heightPortion*camera.pixelHeight;

int leftPixelPadding = leftRightCrop*camera.pixelWidth;
int bottomPixelPadding = topBottomCrop*camera.pixelHeight;

Texture2D cropped = new Texture2D(croppedPixelWidth, croppedPixelHeight);
cropped.ReadPixels(new Rect(
        leftPixelPadding, 
        bottomPixelPadding, 
        leftPixelPadding + croppedPixelWidth, 
        bottomPixelPadding + croppedPixelHeight), 0, 0);

cropped.Apply();

RenderTexture.active = currentRT;

Or as @ina used it,

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

public class SpritePerfectScreenshot : MonoBehaviour
{

    bool ScreenshotIt;
    float widthPortion; float heightPortion;
    float leftRightCrop; float topBottomCrop;
    Camera camera;

    public void TakeScreencap(Camera c,float distance,float pw,float ph)
    {
        camera = c;c.enabled = true;

        SetItUp(distance, pw, ph);
    }

    void SetItUp(float distance,float paintingWidth,float paintingHeight)
    {
        float frustrumHeight = 2.0f * distance * Mathf.Tan(camera.fieldOfView * 0.5f * Mathf.Deg2Rad);
        float frustrumWidth = frustrumHeight * camera.aspect;

          widthPortion =  (paintingWidth / frustrumWidth);
          heightPortion = (paintingHeight / frustrumHeight);

          leftRightCrop =   (1f -  widthPortion) / 2f;
          topBottomCrop =  (1f -  heightPortion) / 2f;


        ScreenshotIt = true;
        print("SetItUp " + distance);
    }

    private void OnPostRender()
    {
        if (ScreenshotIt)
        {
            int croppedPixelWidth =(int) (widthPortion * camera.pixelWidth);
            int croppedPixelHeight =(int) (heightPortion * camera.pixelHeight);

            int leftPixelPadding =(int)( leftRightCrop * camera.pixelWidth);
            int bottomPixelPadding =(int)( topBottomCrop * camera.pixelHeight);

            print(croppedPixelWidth + " " + croppedPixelHeight);

            Texture2D cropped = new Texture2D(croppedPixelWidth, croppedPixelHeight);
            cropped.ReadPixels(new Rect(
        leftPixelPadding,
        bottomPixelPadding,
        leftPixelPadding + croppedPixelWidth,
        bottomPixelPadding + croppedPixelHeight), 0, 0);
            cropped.Apply();

            string uuid = UUID.GetUUID(); string localPath = Application.persistentDataPath + "/" + uuid + ".png";

#if !UNITY_EDITOR
            NativeGallery.SaveImageToGallery(cropped.EncodeToPNG(), "A Beautiful Letter",uuid);
#else
            File.WriteAllBytes(localPath,cropped.EncodeToPNG());
            print(localPath);
#endif 

            //TODO server (?)

            ScreenshotIt = false;camera.enabled = false;
        }
    }
}