Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
2 captures
13 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
17
Question by Gunder · Jan 11, 2010 at 12:00 PM · texturegraphics

Convert a RenderTexture to a Texture2D.

It is possible to convert a RenderTexture to a Texture2D?. I need to create textures on the fly to be used later. I guess one will RenderTexture less performance than a Texture2D.

Thanks.

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

1 Reply

· Add your reply
  • Sort: 
avatar image
34
Best Answer

Answer by duck · Jan 11, 2010 at 01:18 PM

The trick here is to create a new Texture2D, and then use the ReadPixels method to read the pixels from the RenderTexture to the Texture2D, like this:

 RenderTexture.active = myRenderTexture;
 myTexture2D.ReadPixels(new Rect(0, 0, myRenderTexture.width, myRenderTexture.height), 0, 0);
 myTexture2D.Apply();

The above code assumes that you've created a new Texture2D object at the appropriate width and height to copy from the render texture.


Here's some really typical code - it may help someone, cheers

 public void MakeSquarePngFromOurVirtualThingy()
     {
     // capture the virtuCam and save it as a square PNG.
     
     int sqr = 512;
     
     virtuCamera.camera.aspect = 1.0f;
     // recall that the height is now the "actual" size from now on
     // the .aspect property is very tricky in Unity, and bizarrely is NOT shown in the editor
     // the editor will still incorrectly show the frustrum being screen-shaped
     
     RenderTexture tempRT = new RenderTexture(sqr,sqr, 24 );
     // the "24" can be 0,16,24 or formats like RenderTextureFormat.Default, ARGB32 etc.
     
     virtuCamera.camera.targetTexture = tempRT;
     virtuCamera.camera.Render();
     
     RenderTexture.active = tempRT;
     Texture2D virtualPhoto = new Texture2D(sqr,sqr, TextureFormat.RGB24, false);
     // false, meaning no need for mipmaps
     virtualPhoto.ReadPixels( new Rect(0, 0, sqr,sqr), 0, 0); // you get the center section
     
     RenderTexture.active = null; // "just in case" 
     virtuCamera.camera.targetTexture = null;
     //////Destroy(tempRT); - tricky on android and other platforms, take care
     
     byte[] bytes;
     bytes = virtualPhoto.EncodeToPNG();
     
     System.IO.File.WriteAllBytes( OurTempSquareImageLocation(), bytes );
     // virtualCam.SetActive(false); ... not necesssary but take care
     
     // now use the image somehow...
     YourOngoingRoutine( OurTempSquareImageLocation() );
     }
 private string OurTempSquareImageLocation()
     {
     string r = Application.persistentDataPath + "/p.png";
     return r;
     }


Next - or rather, before that.

Very often you have the nightmare of setting the plane and camera sizes correctly. This may help

it's a completely typical example when you are using Prime31's camera plugin. You can adapt it to your uses.

 public void PutCameraImageOnOurVirtualCanvas(string imagePath)
     {
     // one way or another, put the image on the virtual plane.
     // in this example it's from a device camera.  so, put the image on...
     
     // (NOTE - inevitably you will have rotated/twisted the plane (in the editor) since cameras suck
     
     virtuCanvas.renderer.material.mainTexture =
         EtceteraAndroid.textureFromFileAtPath( imagePath );
     
     //
     // make the canvas SHAPE correct, given the camera data shape
     //
     
     Vector2 ActualP31ImageSize = EtceteraAndroid.getImageSizeAtPath( imagePath );
     float heightIsBiggerBy = ActualP31ImageSize.y / ActualP31ImageSize.x;
     virtuCanvas.transform.localScale = new Vector3( 1f, heightIsBiggerBy, 1f );
     
     //
     // make the canvas fit to the same SIZE as the virtual camera
     // (so we'll be taking the middle chunk of the canvas)
     //
     
     float virtCamHeight = virtuCamera.camera.ScreenHeight();
     // and that's basically also the camera width as it concerns us,
     // since we set the camera to .aspect=1 below.
     // you seem to have to use Height as that measure (not width)
     // due to the way .aspect works.  .aspect is super-flakey in Unity, take care
     
     float imWidth = virtuCanvas.renderer.bounds.size.x;
     float imHeight = virtuCanvas.renderer.bounds.size.y;
     
     float shouldBeBiggerBy;
     if ( imWidth < imHeight )
         shouldBeBiggerBy = virtCamHeight / imWidth;
     else
         shouldBeBiggerBy =  virtCamHeight / imHeight;
     
     Vector3 imScale = virtuCanvas.transform.localScale;
     imScale.x = imScale.x * shouldBeBiggerBy;
     imScale.y = imScale.y * shouldBeBiggerBy;
     virtuCanvas.transform.localScale = imScale;
     }

 public static float ScreenHeight(this Camera someOrthoCamera)
     {
     // utility to get the dimension (real world meters) of any old ortho camera
     return
         someOrthoCamera.ViewportToWorldPoint(new Vector3(0,1,10)).y
         - someOrthoCamera.ViewportToWorldPoint(new Vector3(0,0,10)).y;
     }
 

And just the final piece of the puzzle that may help someone,

Don't forget when you do "GetPixels inside SetPixels" you want to take the correct shape.

In the example the size of the camera would almost certainly be 4:3, say, but we want only a square shape. Don't forget that even thoug we "rescale the plane" as above to present a square shape, as you'll see with the first log statements the "whole shape" is still actually there. So, just use the trick of the arguments inside the GetPixels to get the shape you want -- there's no need to tediously traverse the arrays manually, or anything like that.

 public string CreateAndSaveImageNow()
         {
         Debug.Log("making image -- now using camTexture  ");
         
         Debug.Log("camTexture width height is " +camTexture.width +" " +camTexture.height);
         Debug.Log("renderer.material.mainTexture width height is " +renderer.material.mainTexture.width +" " +renderer.material.mainTexture.height);
         
         // it's very likely the camera texture is say 640.480, but we want ONLY a square.
         
         Texture2D virtualPhoto =
             new Texture2D(480,480,
             TextureFormat.RGB24, false);
         
         // so be sure to use the size arguments, inside the "GetPixels", to get only the square you want.
         
         virtualPhoto.SetPixels( camTexture.GetPixels(0,0,480,480) );
         virtualPhoto.Apply();
         
         byte[] bytes;
         bytes = virtualPhoto.EncodeToPNG();
         System.IO.File.WriteAllBytes( OurTempSquareImageLocation(), bytes );
         
         return OurTempSquareImageLocation();
         }

And finally! In that last example you'd typically have to rotate the image. Here's the same routine with some simple, easy to understand kode which will do that.

 int desiredSize;
 
 public string CreateAndSaveImageNow()
     {
     Debug.Log("needsToBeRotatedDegrees was here ... " +needsToBeRotatedDegrees);
     
     if ( camTexture.width < camTexture.height )
         desiredSize = camTexture.width;
     else
         desiredSize = camTexture.height;
     
     Debug.Log("DeviceCamera, CreateAndSaveImageNow, size " +desiredSize);
     
     Texture2D virtualPhoto =
         new Texture2D(desiredSize, desiredSize, TextureFormat.RGB24, false);
     
     Color[] origPixels = camTexture.GetPixels(0,0,desiredSize,desiredSize);
     
     // GetPixels has the handy 'four argument' form, GetPixels32 does not have that
     // here for simplicity, using GetPixels. so first take a square block.
     // now rotate as needed either -90, 0, 90
     
     Color[] rotPixels = new Color[ origPixels.Length ];
     
     for (var x = 0; x < desiredSize; x++)
         for (var y = 0; y < desiredSize; y++)
             {
             if ( needsToBeRotatedDegrees == 0 )
                 rotPixels[ XY(x, y) ] = origPixels[ XY(x, y) ];
             
             if ( needsToBeRotatedDegrees == 270 || needsToBeRotatedDegrees == -90 )
                 rotPixels[ XY(x, y) ] = origPixels[ XY(y, (desiredSize-1-x)) ];
             
             if ( needsToBeRotatedDegrees == 90 )
                 rotPixels[ XY(x, y) ] = origPixels[ XY((desiredSize-1-y), x) ];
             }
     
     virtualPhoto.SetPixels( rotPixels );
     virtualPhoto.Apply();
     
     byte[] bytes;
     bytes = virtualPhoto.EncodeToPNG();
     System.IO.File.WriteAllBytes( OurTempSquareImageLocation(), bytes );
     
     return OurTempSquareImageLocation();
     }
 
 private int XY( int x, int y )
     {
     // this trivial routine just returns the "1d array" version of x,y
     // using the global width (==height) of the arrays in question
     return ( x + y * desiredSize );
     }




Again hope it helps someone save some time, cheers...

Comment
Add comment · Show 7 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image duck ♦♦ · Jan 11, 2010 at 01:52 PM 3
Share

You don't call ReadPixels on the RenderTexture itself, you call it on the destination Texture2D, while the RenderTexture is active. ReadPixels automatically reads from the currently active render texture.

avatar image kor · Jun 17, 2010 at 02:02 PM 0
Share

Hey, this is exactly what I want for a little test, just failing to get it working. Hopefully just something simple I'm doing wrong, if you could take a quick look?

var myRenderTexture : Texture; function Start () { var photo = new Texture2D (256, 256); photo.ReadPixels(new Rect(0, 0, myRenderTexture.width, myRenderTexture.height), 0, 0); photo.Apply(); renderer.material.mainTexture = photo; }

avatar image Codster · May 19, 2011 at 12:26 AM 1
Share

You also have to set the active render texture before you read pixels. So ReadPixels() knows what it's reading from. So the full code would be like;

RenderTexture.active = myRenderTexture; myTexture2D.ReadPixels(new Rect(0, 0, myRenderTexture.width, myRenderTexture.height), 0, 0); myTexture2D.Apply();

avatar image ina · Nov 02, 2011 at 06:05 PM 0
Share

i get cannot cast from source type to render type for RenderTexture.active = myRenderTexture; ... can you assign a renderer.material.mainTexture to the .active?

avatar image Xefier · Jan 22, 2015 at 04:24 AM 1
Share

I would like to add that ins$$anonymous$$d of using a Camera you can simply use Graphics.Blit to draw the material to the RenderTexture.

Show more comments

Your answer

Hint: You can notify a user about this post by typing @username

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

Answers Answers and Comments

4 People are following this question.

avatar image avatar image avatar image avatar image

Related Questions

Copying Textures and transparency. 0 Answers

Tracing in Unity3D 2 Answers

Problem with graphics after restarting a unity application 0 Answers

Is it possible to make an image sequence into an animated normal map? 0 Answers

How to load a large texture at runtime without blocking the pipeline? 1 Answer


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges