RenderTexture to Texture2D

In the following piece of code that is supposed to take a screenshot, I don’t understand why we need to call screenShot.ReadPixels to get Texture2D. A RenderTexture is already a texture, do we really need to use a time consuming operation like ReadPixel to turn it into a Texture2D? Can’t we directly render a camera into a Texture2D? (In Cocos2d, there is a class called CCRenderTexture that extends CCTexture and that can be used to render scenes into. So there must be a way to do so, at least with OpenGL.)

	public static void ScreenShot() {
		RenderTexture rt = new RenderTexture((int)Futile.screen.pixelWidth, (int)Futile.screen.pixelHeight, 24);
        Futile.instance.camera.targetTexture = rt;
        Texture2D screenShot = new Texture2D((int)Futile.screen.pixelWidth, (int)Futile.screen.pixelHeight, TextureFormat.RGB24, false);
        Futile.instance.camera.Render();
        RenderTexture.active = rt;
        screenShot.ReadPixels(new Rect(0, 0, (int)Futile.screen.pixelWidth, (int)Futile.screen.pixelHeight), 0, 0);
        Futile.instance.camera.targetTexture = null;
        RenderTexture.active = null;
        //DestroyImmediate(rt);
		var bytes = screenShot.EncodeToPNG();
		System.IO.File.WriteAllBytes(Application.dataPath + "/screenshots/screen" + System.DateTime.Now.ToString("dd-MM-yyyy_HH-mm-ss") + ".png", bytes);
	}

You can’t render a camera into a Texture2D; you can only render it into a RenderTexture. A RenderTexture is a Texture, but it’s not a Texture2D. Both RenderTexture and Texture2D inherit from Texture. The RenderTexture is not accessible from main memory, so yes you do need to convert it.

I find the EncodeToPNG very slow. What you can do is get the color32 and use System.Graphics to render it for you.

I’ve just done a little research (coz i needed a similar thing)
ReadPixels() does a GPU->CPU readback, which is very slow. Now depending on what you need, you can either

  • Use the RenderTexture directly in your further rendering. This is NOT suitable if you want to hold some game state data in there, as the renderTexture content can be lost by GPU/Driver on some events (screen resize, etc)

  • Use Graphics.CopyTexture(). This happens entirely on GPU, but you will not be able to read pixels to the CPU from this texture, nor call Apply() after other modifications - the GPU memory will be overwritten by outdated content. If you really need to read back the pixels at some time (eg. I do it when I do the savegame) You can do:

  • This:

                 bool[] data = new bool[ra.width * ra.height];
    
                 // Copying the m_revealedArea to itself using temporary RT
                 // m_revealedArea is filled by Graphics.CopyTexture() which only affects GPU memory. GetPixels32() would not return the actual content.
                 // Blitting the m_revealedArea to the tempRT allows us to do ReadPixels() in the other direction and finally alling the GetPixels32()
                 var tempRT = RenderTexture.GetTemporary(m_revealedArea.width, m_revealedArea.height, 0, RenderTextureFormat.ARGB32);
                 try
                 {
                     Graphics.Blit(m_revealedArea, tempRT);
                     RenderTexture.active = tempRT;
                     m_revealedArea.ReadPixels(new Rect(0, 0, tempRT.width, tempRT.height), 0, 0);
                     m_revealedArea.Apply();
                 }
                 finally
                 {
                     RenderTexture.ReleaseTemporary(tempRT);
                 }
    
                 Color32[] color = m_revealedArea.GetPixels32();
    

Simply use Application.CaptureScreenshot.