x


Can a Texture2D be created at runtime from a snapshot of a RenderTexture?

Firstly, thanks Unity Answers community!

A synopsis of the situation: I'm trying to add content to a scene at runtime, send a secondary camera out to snap a single image of the content, and use that image as a texture (a thumbnail image rendered by the main camera).

I'm using currently using RenderTexture -->ReadPixels -->EncodeToPng --> load WWW (the PNG file) which works, but it creates a new file for each screen grab. Is there a way I can internally use the ReadPixels to create a snapshot Texture2D from the RenderTexture without writing an external PNG?

I apologize for this haggard pastiche of code (it's been pasted together from other Unity Answers):

function AddThumb(){

       nextThumbPosition = Vector3(thumbnailStart+(totalScenes*thumbnailSpacer)+(currentScene*sceneSpacer), thumbYValue, thumbZValue);

    var newThumbnail = Instantiate(thumbnail, nextThumbPosition, Quaternion.identity);
       newThumbnail.transform.parent = scrollPane.transform;
       newThumbnail.transform.rotation = Quaternion.Euler(0,0,180);
       newThumbnail.name = "Thumbnail_"+totalScenes.ToString();

    var rt = new RenderTexture(resWidth, resHeight, 24);

       thumbCamera.transform.position = Vector3(totalScenes*sceneSpacer,-19.55,-64.79);
       thumbCamera.targetTexture = rt; 
       screenShot = new Texture2D(resWidth, resHeight, TextureFormat.RGB24, false);     
       thumbCamera.Render(); 
       RenderTexture.active = rt;

       screenShot.ReadPixels(Rect(0, 0, resWidth, resHeight), 0, 0);

       RenderTexture.active = null; // JC: added to avoid errors 
        Destroy(rt);

    var bytes = screenShot.EncodeToPNG();

        System.IO.File.WriteAllBytes(Application.dataPath + "/Resources/screenshot"+totalScenes.ToString()+".png", bytes);

    var file : String =  Application.dataPath +  "/Resources/screenshot"+totalScenes.ToString()+".png";
       file = "file:"+file;

    var www : WWW = new WWW (file);
         yield www;
            newThumbnail.renderer.material.mainTexture = www.texture;
            thumbCamera.active = false;
            newThumbnail.SendMessage("SetSceneNumber",totalScenes);

}

I'm looking for a way to texture the newThumbnail with the "screenShot" Texture2D without having to EncodeToPNG(). Is this possible? Thank you for any feedback!

more ▼

asked Mar 14 '12 at 12:22 PM

meslin gravatar image

meslin
178 1 1 5

Incidentally Meslin - if you get this message, this is spectacular code and it was kind of you to post it here !!! The doco for ReadPixels, etc, is a shambles or nonexistent.

May 28 '12 at 07:57 PM Fattie
(comments are locked)
10|3000 characters needed characters left

3 answers: sort voted first

Why not just avoid it all together? Can't you just use the RT as the texture of the thumbnail? If not, you could use ReadPixels from the RT and SetPixels to another 2D texture which is your thumbnail, if you needed to do some processing in between or something. (Note ReadPixels32/SetPixels32 much faster).

more ▼

answered Mar 14 '12 at 11:37 PM

DaveA gravatar image

DaveA
26.8k 153 171 257

(comments are locked)
10|3000 characters needed characters left

Thanks Dave. The reason I can't use the RenderTexture directly as the mainTexture of the thumbnail plane is that the camera will continuously update the RenderTexture (unless I set it inactive), so I would need a dedicated camera for each thumbnail. I was looking for a way to create ten thumbnails without creating ten cameras, or ten PNG files, or ten of anything except what is absolutely necessary. Your response sent me to the GetPixels32/SetPixels32 documentation, of which there is very little! Below is my script reformulation. I still have a few unanswered questions related to this practice, though. The summary:

  1. The thumbnail camera assigns the targetTexture as the active RenderTexture
  2. A fresh Texture2D "thumbShot" runs ReadPixels
  3. GetPixels32 prints the pixels into the thumbPixels array
  4. SetPixels32 sets and applies the pixels into the thumbShot Texture2D
  5. Texture2D thumbShot is applied as the mainTexture to the thumbnailPlane.

That this works seems miraculous to me. How does the thumbShot.ReadPixels know that the reading refers to the thumbnailCamera? Is it because "rt" is the active RenderTexture?

Also, I'm wondering if anyone can point out any glaring problems regarding memory management with the script below, in light of the fact that there may be hundreds of these thumbnails created in a scene.

/*

    (1)Instantiates Thumbnail Plane as Scene_XXX_Thumbnail.
    (2)Sends ThumbnailCamera to Position.
    (3)Updates Texture on Thumbnail Plane with Thumbnail Camera Snapshot.

    as: 

    Start()
    ThumbnailSetup()
    ThumbnailSeek()
    Snapshot()


    Start()
       - Get static Vector3 'newThumbnailPosition' as 'thisThumbnailPosition'
       - Establish the thumbnailPlane as ThumbnailPlane prefab
       - ThumbnailSetup()

    ThumbnailSetup()
       - Instantiate Thumbnail Plane at thisThumbnailPosition
       - Make a new material if necessary
       - ThumbnailSeek();

    ThumbnailSeek()
       - MoveTo thisThumbnailPosition
       - iTween.MoveTo "oncomplete", Snapshot()

    Snapshot()
       - Set Up RenderTexture "rt" and a new blank Texture2D "thumbShot", ReadPixels(?)
       - GetPixels32 from the read pixels
       - SetPixels32 to Thumbnail Texture "thumbShot"

*/


var thumbnailResolutionX : int;
var thumbnailResolutionY : int;

var thumbPixels : Color32[];

var thisThumbnailPosition = Vector3();
var thisScenePosition = Vector3();
var thumbnailCameraPosition = Vector3();

var thumbnailPlanePrefab : GameObject;
var thumbnailPlane : GameObject;
var thumbnailCamera : Camera;


function Start(){

    thisThumbnailPositon = Vector3(-5.21,-2.98,0);

    thumbnailCameraPosition = Vector3(-.14,-10.05,-10);

    thumbnailCamera = GameObject.Find("ThumbnailCamera").camera;

    thumbnailResolutionX = 512;

    thumbnailResolutionY = 512;

    ThumbnailSetup();

}


function ThumbnailSetup(){

    thumbnailPlane = Instantiate(thumbnailPlanePrefab,thisThumbnailPosition,Quaternion.Euler(90,180,0));

    ThumbnailSeek();

}


function ThumbnailSeek(){

    iTween.MoveTo(thumbnailCamera.gameObject,{"position":thumbnailCameraPosition,"time":.5,"oncomplete":"Snapshot","oncompletetarget":gameObject});

}


function Snapshot(){

    var rt = new RenderTexture(thumbnailResolutionX, thumbnailResolutionY, 24);

    var thumbShot = new Texture2D(thumbnailResolutionX, thumbnailResolutionY, TextureFormat.RGB24, false);

       thumbnailCamera.targetTexture = rt;

       thumbnailCamera.Render();

       RenderTexture.active = rt;

       thumbShot.ReadPixels(Rect(0, 0, thumbnailResolutionX, thumbnailResolutionY), 0, 0);

       thumbPixels = thumbShot.GetPixels32();

       thumbShot.SetPixels32(thumbPixels);

       thumbShot.Apply();

       RenderTexture.active = null;   

        thumbnailPlane.renderer.material.mainTexture = thumbShot;

}
more ▼

answered Mar 16 '12 at 05:24 PM

meslin gravatar image

meslin
178 1 1 5

"How does the thumbShot.ReadPixels know that the reading refers to the thumbnailCamera? Is it because "rt" is the active RenderTexture?'

I've always wondered exactly the same thing, Meslin! It's infuriating. Did you ever get any closer on this puzzle???

May 28 '12 at 07:21 PM Fattie
(comments are locked)
10|3000 characters needed characters left

The magic bit you were missing was the thumbShot.Apply(); the get/set pixels calls are wasted cycles.

more ▼

answered Apr 24 '12 at 12:38 AM

jdonavan gravatar image

jdonavan
1

(comments are locked)
10|3000 characters needed characters left
Your answer
toggle preview:

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this question

By Email:

Once you sign in you will be able to subscribe for any updates here

By RSS:

Answers

Answers and Comments

Topics:

x3127
x370
x200
x39
x8

asked: Mar 14 '12 at 12:22 PM

Seen: 3282 times

Last Updated: May 28 '12 at 07:58 PM