Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

How to use a Render Texture to take an "Off-Screen" screen shot?

Discussion in 'Scripting' started by d34thst4lker, Jul 13, 2015.

  1. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    I have been looking around and nothing I have tried is working.

    I would like to know if there is a way that I can setup a Canvas with some images/text that will get filled in during the game and then create a texture to file out of this Canvas without having to show it to the user.

    Googling around shows that creating a RenderTexture on a camera will show the Canvas on the RenderTexture but I have not been able to create a Texture2D out of it or save it out to a file.

    Any ideas??

    Thanks!
     
  2. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    Not a complete answer but here's what I found with quick research:

    Looks like Unity API itself doesn't allow to read texture buffer but you can call GetNativeTexturePtr to get direct access to texture. From there you need to use native code and whatever rendering library is being used in your case (probably DirectX or OpenGL) to get to pixel buffer.

    If you follow the documentation link to native code plugins you will find there an example of such plugin for writing to texture so it's almost what you need. Just need to read instead of writing. Also the texture reading and then exporting to file doesn't have to be integrated with Unity functionality in any way so when studying the example just drop all the "native code plugins" parts and just look at how they make use of the texture pointer returned by GetNativeTexturePtr to get to texture buffer.
     
  3. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
  4. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    Sweet!! That seemed to have created the texture but when I try to EncodeToPNG() I get
    Unable to retrieve image reference
    UnityEngine.Texture2D:EncodeToPNG()
     
    IgorAherneBusiness likes this.
  5. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    Did you check your texture pointer if it's not 0 for example? For me trying to create Texture2D from a texture pointer crashes the Unity.

    Here's another way.
    Texture2D.ReadPixels: "This will copy a rectangular pixel area from the currently active RenderTexture". So:
    Code (CSharp):
    1. Texture2D tex2d = new Texture2D(rendTex.width, rendTex.height, TextureFormat.ARGB32, rendTex.useMipMap);
    2. RenderTexture.active = rendTex;
    3. tex2d.ReadPixels(new Rect(0, 0, rendTex.width, rendTex.height), 0, 0);
    4. tex2d.Apply();
    5. byte[] bytes = tex2d.EncodeToPNG();
    6. File.WriteAllBytes(Application.dataPath + "/../SavedScreen.png", bytes);
    When creating Texture2D I hardcoded the texture format argument because TextureFormat and RenderTextureFormat are two different enums and it turns out that TextureFormat.ARGB32 is different value then RenderTextureFormat.ARGB32 so simply casting won't work. You need to write a RenderTextureFormatToTextureFormat method to do this properly. Since you have to find int value base on matching enum labels you probably will need Reflection for that.
     
  6. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    There you go:
    Code (CSharp):
    1. string formatName = Enum.GetName(typeof(RenderTextureFormat), rendTex.format);
    2. TextureFormat? texFormat = null;
    3. foreach (TextureFormat f in Enum.GetValues(typeof(TextureFormat)))
    4. {
    5.    string fName = Enum.GetName(typeof(TextureFormat), f);
    6.    if (fName.Equals(formatName, StringComparison.Ordinal))
    7.    {
    8.       texFormat = f;
    9.       break;
    10.    }
    11. }
    12.  
    13. if (texFormat != null)
    14. {
    15.    Texture2D tex2d = new Texture2D(rendTex.width, rendTex.height, (TextureFormat) texFormat, rendTex.useMipMap);
    16.    RenderTexture.active = rendTex;
    17.    tex2d.ReadPixels(new Rect(0, 0, rendTex.width, rendTex.height), 0, 0);
    18.    tex2d.Apply();
    19.    byte[] bytes = tex2d.EncodeToPNG();
    20.    File.WriteAllBytes(Application.dataPath + "/../SavedScreen.png", bytes);
    21. }
    22. else
    23.    Debug.Log("Failed to convert RenderTextureFormat to TextureFormat!");
     
    Last edited: Jul 14, 2015
  7. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    Hmm... I tried doing what you show above. I have the Texture2D as a public variable so I can see if it gets updated in the inspector and it does. The image I am expecting to see is now on that public variable. But the "EncodeToPNG()" is just coming up with a grey box.

    What could be wrong?
     
  8. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    Perhpas you have one piece of code that updates your public Texture2D that works fine and then another piece of code that updates Texture2D before saving to file and this one is not working? No idea really. Can you show some code, give relevant details on your setup?
     
  9. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    I literally used exactly what you posted above.
    I somehow messed it all up now. Not exactly sure what has happened but now its just putting a grey texture using the above code rather than the actual image as it was showing a moment ago... Very Weird

    But if I go ahead and use
    Code (CSharp):
    1. tex2d = Texture2D.CreateExternalTexture(rt.width,rt.height,TextureFormat.ARGB32,false,true,rt.GetNativeTexturePtr());
    rather than
    Code (CSharp):
    1. tex2d.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
    2. tex2d.Apply();
    The texture comes out properly again but when trying to get the bytes. It says;
    Unable to retrieve image reference
    UnityEngine.Texture2D:EncodeToPNG()
     
    Last edited: Jul 14, 2015
  10. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    I would stay far away from using the pointer now that we have a way of doing this without it. Sounds like you had it working in that the Texture2D got created correctly and then only the EncodeToPNG was causing some trouble but that was probably issue with setup. Code works, I tested it. Try reproducing the problem with minimalistic setup.
     
  11. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    Thanks for your help. I have no idea why your code is not working anymore. I didn't change anything so its really confusing. I would really like to get this done though. It's very much needed.
     
  12. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    Like I said, try with new scene and make as simple setup as possible to test the code. Find where exactly things go wrong and see where it leads you. Once you get the simple example working you can then compare it with your intended setup and see what's different there.
     
  13. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    There's nothing in my scene besides your code and a canvas with a few child objects on it with a "Screen Space - Camera" setup for the main camera along with the main camera having a render texture applied to it.
    So confused
     
  14. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    Ok! I got it to work by turning off anti-aliasing completely from the Render Texture. If I put it on then it wont work. What is the reason for that?
     
  15. Pirs01

    Pirs01

    Joined:
    Sep 30, 2012
    Posts:
    389
    No idea. You might want to make a new thread about this problem specifically cause I'm clueless.
     
  16. d34thst4lker

    d34thst4lker

    Joined:
    Aug 4, 2013
    Posts:
    124
    Thank you. I may