Search Unity

Question Creating atlas using CopyTexture

Discussion in 'General Graphics' started by carcasanchez, Sep 5, 2021.

  1. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    Hi,
    I have a bunch of individual textures that I want (automatically) to copy into a bigger one by order to create a spritesheet (I need them in order, so i cant use Textures.Pack).
    I initially wanted to use Graphics.CopyTexture, since it's the one that lets me say in which specific part of the destination texture I want to copy each sprite (perfect for constructing a spritesheet).
    However, I have found that CopyTexture only works on the GPU side, resulting in the sheet correctly showing on inspector: upload_2021-9-5_20-46-26.png

    But the image file is completely empty: upload_2021-9-5_20-46-53.png

    I think I can use Render Textures to then use Blit() after copying everything, but I'm not sure how, and I can't find any docs.

    Here is the code:
    Code (CSharp):
    1. void GenerateSpritesheet()
    2.     {
    3.         string[] fileEntries = Directory.GetFiles(Application.dataPath + "/" + originPath, "*.png");
    4.         List<Texture> allFrames = new List<Texture>();
    5.         foreach (string fileName in fileEntries)
    6.         {
    7.             int index = fileName.LastIndexOf("/");
    8.             string localPath = "Assets/" + originPath;
    9.  
    10.             if (index > 0)
    11.                 localPath += fileName.Substring(index);
    12.  
    13.             Texture t = (Texture)AssetDatabase.LoadAssetAtPath(localPath, typeof(Texture));
    14.  
    15.             if (t != null)
    16.                 allFrames.Add(t);
    17.         }
    18.  
    19.  
    20.         int size = Mathf.NextPowerOfTwo((int)Mathf.Sqrt((float)allFrames.Count)) * 512;
    21.  
    22.         //First, store the texture empty to have a texture importer
    23.         Texture2D spritesheet = new Texture2D(size, size, TextureFormat.RGBA32, false);
    24.  
    25.         Byte[] png = spritesheet.EncodeToPNG();
    26.         File.WriteAllBytes(Application.dataPath + "/" + spritesheetPath + animName + ".png", png);
    27.  
    28.         AssetDatabase.SaveAssets();
    29.         AssetDatabase.Refresh();
    30.         spritesheet = AssetDatabase.LoadAssetAtPath<Texture2D>("Assets/" + spritesheetPath + animName + ".png");
    31.  
    32.         TextureImporter textureImporter = AssetImporter.GetAtPath("Assets/" + spritesheetPath + animName + ".png") as TextureImporter;
    33.         textureImporter.textureType = TextureImporterType.Sprite;
    34.         textureImporter.alphaIsTransparency = true;
    35.         textureImporter.filterMode = FilterMode.Point;
    36.         textureImporter.spriteImportMode = SpriteImportMode.Multiple;
    37.         textureImporter.maxTextureSize = 8192;
    38.         textureImporter.isReadable = true;
    39.  
    40.         textureImporter.SaveAndReimport();
    41.  
    42.         int x = 0;
    43.         int y = spritesheet.width;
    44.         foreach (Texture t in allFrames)
    45.         {
    46.             Graphics.CopyTexture(t, 0, 0, 0, 0, t.width, t.height, spritesheet, 0, 0, x, y - t.height);
    47.  
    48.             x += t.width;
    49.  
    50.             if (x >= spritesheet.width)
    51.             {
    52.                 x = 0;
    53.                 y -= t.height;
    54.             }
    55.         }
    56.  
    57.         //spritesheet.Apply();
    58.         AssetDatabase.SaveAssets();
    59.         AssetDatabase.Refresh();
    60.     }
     
  2. Lo-renzo

    Lo-renzo

    Joined:
    Apr 8, 2018
    Posts:
    1,514
    Do you mean to instantiate a texture2D and use texture.SetPixels()? I've generated spritesheets using that approach. There are loads of similar methods in the Texture API, some more efficient than others. This requires your source textures be read/write enabled.

    You might also consider Unity's built-in Sprite Atlas. It doesn't work at runtime but works at editor time for most use cases.
     
  3. carcasanchez

    carcasanchez

    Joined:
    Jul 15, 2018
    Posts:
    177
    That was another option, but since ReadPixels works with the image as one dimensional array, I'm not sure how I could build the code to copy a smaller texture into a specific part of a bigger atlas.

    Didn't know about it, seems like it will be a good solution!

    Edit: SpriteAtlas work like a charm! Is better optimized than a handcrafted spritesheet, and you can work with the individual textures without doing any slicing. Perfect for what I need.
     
    Last edited: Sep 7, 2021