Trying to capture an area (Rect) of the screen

Hi All

I am attempting to capture an area of the screen and save it as a Texture2D. This is a 2D app so uses an Orthographic camera (No need to worry about perspective). I have managed to create a GUI.Box at the exact place I want the texture grab to occur but cant now work out how to actually get the pixels from the Rect of the GUI.Box as a texture.

I know I have to use a RenderTexture but not sure how I can convert GUI space to a coordinate system that works with Texture2D.ReadPixels. Any hints please?

Many thanks
Geoff

public IEnumerator grabTexture() {	

yield return new WaitForEndOfFrame();


   // aMesh is a planar object used for selection, the user moves it around before a capture is performed.
	
    Bounds b = aMesh.renderer.bounds; 
    Camera cam = Camera.main;
 
    // All 8 vertices of the bounds
    pts[0] = cam.WorldToScreenPoint (new Vector3 (b.center.x + b.extents.x, b.center.y + b.extents.y, b.center.z + b.extents.z));
    pts[1] = cam.WorldToScreenPoint (new Vector3 (b.center.x + b.extents.x, b.center.y + b.extents.y, b.center.z - b.extents.z));
    pts[2] = cam.WorldToScreenPoint (new Vector3 (b.center.x + b.extents.x, b.center.y - b.extents.y, b.center.z + b.extents.z));
    pts[3] = cam.WorldToScreenPoint (new Vector3 (b.center.x + b.extents.x, b.center.y - b.extents.y, b.center.z - b.extents.z));
    pts[4] = cam.WorldToScreenPoint (new Vector3 (b.center.x - b.extents.x, b.center.y + b.extents.y, b.center.z + b.extents.z));
    pts[5] = cam.WorldToScreenPoint (new Vector3 (b.center.x - b.extents.x, b.center.y + b.extents.y, b.center.z - b.extents.z));
    pts[6] = cam.WorldToScreenPoint (new Vector3 (b.center.x - b.extents.x, b.center.y - b.extents.y, b.center.z + b.extents.z));
    pts[7] = cam.WorldToScreenPoint (new Vector3 (b.center.x - b.extents.x, b.center.y - b.extents.y, b.center.z - b.extents.z));
 
    //Convert to GUI Space so can draw box around area to capture (for testing mostly)
    for (int i=0;i<pts.Length;i++) pts_.y = Screen.height-pts*.y;*_

//Calculate the min and max positions
Vector3 min = pts[0];
Vector3 max = pts[0];
for (int i=1;i<pts.Length;i++) {
min = Vector3.Min (min, pts*);*
max = Vector3.Max (max, pts*);*
}

//Construct a rect
ScrRect = Rect.MinMaxRect (min.x,min.y,max.x,max.y);

* // Note: GUI.Box(ScrRect,“Captured Data”) works great from this Rect *

* // Invert the Y for ScreenSpace not GUI space*
* ScrRect.y = Screen.height - ScrRect.y;*

* // Grab texture from Rect*

* // This is a factor that enlarges the snapshot’s resolution. It sets the resolution of the rendertexture to a multiple of the Screen’s resolution.*
// If you don’t want that, just set it to 1.
int superSamplingFactor = 1;

Texture2D snapShot = new Texture2D((int)ScrRect.width * superSamplingFactor, (int)ScrRect.height * superSamplingFactor, TextureFormat.ARGB32, false);

RenderTexture snapShotRT = new RenderTexture(Screen.width * superSamplingFactor, Screen.height * superSamplingFactor, 24, RenderTextureFormat.ARGB32); // We’re gonna render the entire screen into this
RenderTexture.active = snapShotRT;
Camera.main.targetTexture = snapShotRT;
Camera.main.Render();

Rect lassoRectSS = new Rect(ScrRect.xMin * superSamplingFactor, ScrRect.yMin * superSamplingFactor, ScrRect.width * superSamplingFactor, ScrRect.height * superSamplingFactor);

snapShot.ReadPixels(lassoRectSS, 0, 0);
snapShot.Apply();
RenderTexture.active = null;
Camera.main.targetTexture = null;

// This is another planar mesh in the view so I can see the captured image
* display.material.mainTexture = snapShot;*

}

1 Answer

1

You have to render the camera manually into a RenderTexture, as you have correctly guessed, and then read out the pixels into a normal texture afterwards using Texture2D.ReadPixels. I implemented pretty much exactly what you’re requesting a while ago, and did it like this (simplified for demonstration here):

    // This is a factor that enlarges the snapshot's resolution. It sets the resolution of the rendertexture to a multiple of the Screen's resolution.
    // If you don't want that, just set it to 1.
    int superSamplingFactor = 3;

    Texture2D snapShot = new Texture2D((int)LassoRect.width * superSamplingFactor, (int)LassoRect.height * superSamplingFactor, TextureFormat.ARGB32, false);

    RenderTexture snapShotRT = new RenderTexture(Screen.width * superSamplingFactor, Screen.height * superSamplingFactor, 24, RenderTextureFormat.ARGB32); // We're gonna render the entire screen into this
    RenderTexture.active = snapShotRT;
    YourCamera.targetTexture = snapShotRT;
    YourCamera.Render();

    Rect lassoRectSS = new Rect(LassoRect.xMin * superSamplingFactor, LassoRect.yMin * superSamplingFactor, LassoRect.width * superSamplingFactor, LassoRect.height * superSamplingFactor);

    snapShot.ReadPixels(lassoRectSS, 0, 0);
    snapShot.Apply();
    RenderTexture.active = null;
    YourCamera.targetTexture = null;

The variable called LassoRect is the rectangle the user has selected on the screen. You mentioned you had that already? It’s the same Rect you use to draw that GUI.Box. It doesn’t matter if the camera called YourCamera is orthographic or perspective - the Texture2D will contain exactly the portion of the screen the user selected, as it would’ve been rendered to the screen by YourCamera.

PS: I am sorry the code has linebreaks in odd locations like that - it doesn't show like that when I'm in edit-mode. It has nice scroll bars that take care of the extra line width appropriately in the preview in edit mode.

y = Screen.Height - y should sort it out. i forget which is which but the gui's y is inverted to the screen

Right. Sorry, I think I had my LassoRect inverted to Screen coordinates already before the code I pasted. :) GUI coordinates, as @gheeler point out, are upside down. y = Screen.Height - y will do the inverting for you.

That moved it closer but still it's offset in the Y. I have read up that GUI Space and Screen coordinates are different and there is a utility function GUIToScreenPoint that I can try out. Thanks for the help though, i'm slowly inching forwards :)

Ah ok, will move the code around as I was still assuming I was working with a GUI point before reading your last reply. Cheers