Super fast method to create 1 texture from multiple textures

Discussion in 'Scripting' started by n0mad, Nov 5, 2010.

  1. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Hello,

    I don't know yet if RenderTextures work for iPhones, but it seems to work for iPad (and Android) since U3.

    So I found a way to create a texture from superposition of tons of other textures (special thanks to Dreamora for suggesting GUITextures/Readpixels).

    It can be super useful for clothes texture generation, for example.


    Update : I finally posted the script down the page : Link

    What you need to create :
    - a camera prefab that culls nothing else that could be rendered outside of this script. Only attach a Gui Layer to it, and set its depth mask to "don't clear".
    - a GUITexture prefab. Put it on the same culling layer than the Camera above. Set its Scale transforms to zero, as explained in the Unity docs.

    Now by code in a script, whenever you want to create your texture :
    - create a variable containing the targetted dimensions of your texture
    - create a new RenderTexture. Dimensions are the var above.
    - instantiate your camera GUITexture prefabs at zero position
    - set camera's targetTexture to the freshly created RenderTexture
    - Initialize your RTexture with _RenderTextureVar.Create() function (see the docs).
    - Now, each time you want to add a texture in your composition, just :
    a) set your GUITexture.texture to whatever Resources stored Texture2D (or anywhere you want to take it from, ingame, web, etc).
    b) launch a camera.Render() on your Cam instance.
    (or launch a WaitForEndOfFrame(), but it's slower obviously)

    When you've added all the textures to your composition :
    - Launch a RenderTexture.active = _yourRecentlyCreatedRTexture (important place, doesn't work if put before GUITexture.texture assign)
    - create your final texture Texture2D container(formatted as RGB24/ARGB, as explained in the docs).
    - perform a _finalTextureContainer.ReadPixels() (with your texture dimensions as parameter, see docs).
    - perform a RenderTexture.active = null
    - perform a _finalTextureContainer.Apply() to create mipmaps.

    Destroy your camera, GUITexture pefabs and your RenderTexture.

    Et voilà.

    Advantages over GetPixels methods :
    - it's 10+ times faster. With GetPixels/SetPixels/Alpha Lerping operations, I had a calculation time of 0.30 seconds on my home PC, with 11 x 512x512 alpha textures. With this method, I ended with times from 0.02 to 0.01. So for mobile devices, expect to multiply it by higher numbers.
    - Operations are GPU side, which now prevent any interference with CPU operations, like animation loading.
    - Biggest change for me : you don't need ARGB huge textures anymore. Yes, you can keep your compression settings (except for the computed final texture, indeed). You also don't need any more isReadable state for your source textures. Device memory says "oh god thank you".
    - You can perform prerendered visual effect operations on your final texture, by RenderTexture manipulation, like tinting, photo grain, decals, etc. Just add a prerendered effect image on top of your textures layers.
    - You can easily resize your final texture dimensions by changing the dimensions variable. No Resize operation needed.
    This can be useful for adjusting your texture size to the device memory, for example.

    Hints Tips :
    - There seems to be a bug when you cast camera.Render() outside of Unity basic loops (Start, Update, etc), an assertion saying something lime "screenViewCoords[0] < 0 || screenViewCoords[3] < 0" (Sorry I don't recall exactly).
    So your best use is to create the whole operation into a MonoBehaviour script that you attach to an object, and which self destroys at the the end of operations.
    - Placing your layered bitmaps by script can be hazardous, I suggest you place them directly into your source texture, putting all of them at fixed dimensions, with alpha zero pixels where there is no colors. Then you will only have to place layered bitmaps to zero position.

    Enjoy :)
     
    Last edited: Feb 26, 2013
  2. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Messages:
    11,531
    This could be adopted for resizing a texture as well couldn't it?
     
  3. windexglow

    windexglow

    New Member

    Joined:
    Jun 18, 2010
    Messages:
    378
  4. hippocoder

    hippocoder

    Digital Ape Moderator

    Joined:
    Apr 11, 2010
    Messages:
    11,531
    Yes only pro can render to texture.
     
  5. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Exactly ;)
    You can resize at will for your final texture to fit any device memory.

    Yep, Pro only. There are a lot of performance related tools and tricks that are pro only, it really worthes the money.
     
  6. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Oh I forgot, you can do it without RenderTextures (so with Free), by still using Readpixels on the screen instead of RT.

    But it's not very handy, as you won't be able to hide your texture works. Users will see big textures on screen for the calculation time.

    Not very classy, but doable without pro.
     
  7. vollnull

    vollnull

    New Member

    Joined:
    Aug 26, 2010
    Messages:
    70
    Is there a good reason, why I see only chaos on the screen instead of 1 single button in the lower left corner, which I added for testing the procedure?

    I have Unity Pro 3.1 by the way and Windows XP
     

    Attached Files:

  8. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Sorry for the late answer :

    Yes, the reason is that if your camera depth flag is set to "don't clear", your render texture will be added every non-transparent zone it sees at every rendering frame, without clearing what it already had written. Think of "don't clear" as an infinite additive method.
    Here you seem to have put your culling camera mask on layers that were already displaying something else than your button.

    In this procedure, "don't clear" is used to make additive layers stack on each other into a single bitmap without having to save this bitmap on every new stack added. Big performance gain.
    But if you're not setting your rendertexture's camera culling mask to a totally independant layer, you may witness this kind of mess up. Create a layer apart from all the others for this whole work, unless you want to use something that's been rendered before your script.
     
    Last edited: Dec 15, 2010
  9. vollnull

    vollnull

    New Member

    Joined:
    Aug 26, 2010
    Messages:
    70
    Thanks for your answer.
    I double checked the cameras culling mask and it is set to a special layer which I created only for GUI rendering. I get a weird result anyway. If I set the camera depth to solid color, and choose {0,0,0,0} as color, all is working as expected. But instead of one GUITexture I have several of them - one for each button - and I do a render call once at the end. Is that a good idea? It works well for a few buttons, but if I klick one of them, which causes some additional buttons to become visible, they disappear all.
     
  10. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Several GUITextures will lose the memory benefit of the method, as you will load as much memory as you have different bitmaps. This is doable, but beware of huge memory leaps on mobile platforms, which could cause big loading slowdowns as mem goes up.
    About your weird result, check out if you put the "RenderTexture.active = _yourRecentlyCreatedRTexture" at the right place, as mentionned in the first post, or it will never snapshot your GUITextures.

    Otherway, I admit I don't have enough time to provide full support to this procedure, but if done step by step it does work. After that, I suggest to use the good ol' trial-and-error technique to reach desired result ;-)

    Good luck
     
  11. nekoneko

    nekoneko

    New Member

    Joined:
    Mar 30, 2012
    Messages:
    1
    Hi
    Nomad i'm traing to use your method for baking blood on the ground in my topdown shooter, so far not good all i get is a crap. Could you post your script here? Please :confused:
     
  12. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Been a while since I created this topic ! But yeah, I realize I could have easily written down a more generalized version of the script in here ...
    So, after that long, here it is !


    Code (csharp):
    1. public static Texture2D CreateLayeredTexture() {
    2. //Just a camera with a GUILayer component, ortographic, size 100,
    3. //Clear Flags set to "Don't clear"
    4.         GameObject _TCam = (GameObject) Resources.Load("TextureCam");
    5.  
    6.  // your texture size
    7.         int _texSize = 512;
    8.         RenderTexture _targetTex = new RenderTexture(_texSize, _texSize, 24, RenderTextureFormat.ARGB32);
    9.         _targetTex.isPowerOfTwo = true;
    10.         _TCam.camera.targetTexture = _targetTex;
    11.         _targetTex.Create();
    12.  
    13. //Another object with only a GUITexture component, pixel inset width = 320, height = 320
    14.         GameObject _GUITexCombiner = (GameObject) Resources.Load("TextureCombiner");
    15.  
    16.          _GUITexCombiner.transform.localScale = new Vector3(0f, 0f, 0f);
    17.          _GUITexCombiner.transform.position = new Vector3(0f, 0f, 0f);
    18.          _GUITexCombiner.guiTexture.pixelInset = new Rect(0, 0, _texSize, _texSize);
    19.  
    20. //Tex 1 :
    21.          _GUITexCombiner.guiTexture.texture = (Texture2D)Resources.Load("Texture1");
    22.         _TCam.camera.Render();
    23.         Resources.UnloadAsset(_GUITexCombiner.guiTexture.texture);
    24.  
    25. //Tex2 :
    26.          _GUITexCombiner.guiTexture.texture = (Texture2D)Resources.Load("Texture2");
    27.         _TCam.camera.Render();
    28.         Resources.UnloadAsset(_GUITexCombiner.guiTexture.texture);
    29.  
    30.         //etc, etc .....
    31.  
    32.  
    33.         Texture2D _newTex = new Texture2D(_texSize, _texSize, TextureFormat.RGB24, false);
    34.         RenderTexture.active = _targetTex;
    35.         _newTex.ReadPixels(new Rect(0, 0, _texSize, _texSize), 0, 0);
    36.         _newTex.Apply(true);
    37.  
    38.         RenderTexture.active = null;
    39.         Object.Destroy(_GUITexCombiner);
    40.         Object.Destroy(_TCam);
    41.         _targetTex.Release();
    42.  
    43.         return _newTex;
    44.     }
    45.    
     
    Last edited: Mar 2, 2013
  13. FernandoRibeiro

    FernandoRibeiro

    Member

    Joined:
    Sep 23, 2009
    Messages:
    1,177
    Hi there =)
    Sent you a pm. I've been working on something like this for http://forum.unity3d.com/threads/153689-procedural-character-generation
    btw, I think there's a " ; " missing at 37

    See ya!
     
  14. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
  15. FernandoRibeiro

    FernandoRibeiro

    Member

    Joined:
    Sep 23, 2009
    Messages:
    1,177
    Great = D
    I really loved the "don't clear" depth flag concept, it's really very powerful in this solution!
    I'm working on the integration of this solution on UMA project, timing couldn't be better =D
     
  16. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    Ah crap, I forgot to specify this "Don't clear" property in the script ! Thanks for reminding, changing it now :)
    (was in the OP description but not in the script)

    I'm glad this will come to good use in your project !
    Cheers
     
  17. reset

    reset

    Member

    Joined:
    May 22, 2009
    Messages:
    365
    Hi there

    I am using this technique for my character clothing. I think it is great! Cheers.

    So you are creating a camera, and GUITexture as prefabs and locating them in the Assets/Resources folder? I can load them. And even my first texture - but when I hit "Resources.UnloadAsset(_GUITexCombiner.guiTexture.texture);" I CRASH! And then the whole process becomes unstable.

    What could be wrong?
     
  18. n0mad

    n0mad

    Member

    Joined:
    Jan 27, 2009
    Messages:
    3,731
    That's a very strange crash tbh ... sounds like a memory corruption or something ?