Author Topic: build size optimization - jpg + separate alpha in png format possible?  (Read 11479 times)

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Hello,

I have a game with hundreds of full screen character sprites (resulting in hundreds of MB to download). It's really important for me to reduce this without breaking the art. The art doesn't look good with DXT compression. I believe the ideal way to go is to have the color image in a jpg and the alpha information in a separate 8-bit png. Is this possible in nGUI, or in Unity in general?

I am aware, this is not how Unity works (you import the texture into a format, Unity compresses the asset package at build time etc.)
Still, I found out I can load jpg and png trough Texture2D.LoadImage, but I have no idea how to put them together. I guess a shader could do that, but I have no clue how to set them up in Unity and even less how to use them in code to call something like Finale = Merge(jpg, png).

I think I found slightly related answer here a 10 lines of code that should do the trick, but I miss the context, I don't know how to use it: http://forum.unity3d.com/threads/1309-combining-textures-and-alpha

I am open to any ideas, and hope someone solved this already :)
Thank you for your time.
« Last Edit: April 28, 2013, 04:39:40 PM by andrejvojtas »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Unity uses LZMA compression for textures, which already gives you the best possible texture size (especially targeting the web). I know because I was trying to go down the same route in Gameloft when we were trying to fit into the 20 mb limit. I achieved some success by using my own custom LZMA-based image format that packed texture info in either interleaved (RGBARGBA), or consecutive (RRGGBBAA) format, choosing the smallest of the two. However the gain was negligible (~5%). It's easier to achieve smaller sizes by being smart instead. Instead of storing fully textured RGBA data, store masks instead (red channel = pants, green channel = shirt, etc), and color them in a shader instead.

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Thank you for your answer. Your idea is really good, and I imagine very easy to set up with nGUI sprites. However it's not compatible with my art, there is lot's of different colors, etc.

I have run extensive test on my art samples and LZMA reduces the size to ~20% (very similar to png compression, naturally).
jpg quality 9 (still no visual difference) and 8-bit png for alpha gets me down to 6%. This is not something I invented, this method was used for years in old 2D frameworks.

I appreciate any advice how to go about this.

Nicki

  • Global Moderator
  • Hero Member
  • *****
  • Thank You
  • -Given: 33
  • -Receive: 141
  • Posts: 1,768
    • View Profile
Hmm, well, I suppose you could load jpgs and alphas from Resources and put them together run time with http://docs.unity3d.com/Documentation/ScriptReference/Texture2D.html 's SetPixel.

I'm not sure if Unity will re-pack the textures from jpeg though and I also don't know how fast it would be.

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Unity uses LZMA compression for textures

EDIT: I was confused as for the standalone build it did not compress the textures and the asset file grow into a couple GBs. Then I understood it uses LZMA for webplayer and for standalone I can compress it on my own before distributing on the web.
« Last Edit: May 01, 2013, 12:41:04 PM by andrejvojtas »

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Ok,

so I figured out a shader that does what I need, see below. If there are some errors or unnecessary stuff, please let me know. It takes one RGB 24 bit Unity texture and one Alpha 8 Unity Texture (with Alpha from Greyscale ON). I tested it in the editor with a simple scene. I created a material, changed the shader to the custom one, added the textures. Then I added the material onto a plane and it works.

I also figured out how to avoid Unity importing my .jpg and .png as textures once I add them in the Resource folder, as I want them added to the build in their compressed form. I change the extension to .bytes and then use Resource.Load("name") as TextAsset and then create a texture and add the image data from the TextAsset.bytes with Texture2D.LoadImage.

So I have a shader and the two textures loaded from the image files in code. I can create a plane, but I am not sure how to scale and position it so that the image is displayed pixel perfect. Is there an easy way how to do it? Can I somehow use the nGUI to do this? I mean can I use nGUI from code to create, scale and positions the mesh and then add the material using the custom shader? Or is there a way how to use the shader without a plane/mesh?

  1. Shader "RGBplusA" {
  2.         Properties {
  3.                 _MainTex ("Main Texture", 2D) = "white" {}
  4.                 _Mask ("Mask Texture", 2D) = "white" {}
  5.         }
  6.         SubShader {
  7.                 Blend SrcAlpha OneMinusSrcAlpha
  8.                
  9.                 Pass
  10.                 {
  11.                         SetTexture [_Mask] {combine texture}
  12.                         SetTexture [_MainTex] {combine texture, previous}
  13.                 }
  14.         }
  15. }
  16.  

EDIT: I corrected the info above, the renamed extensions must be .bytes, not txt
« Last Edit: April 30, 2013, 12:10:13 PM by andrejvojtas »

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
Use UITexture to draw this. Don't create your own planes.

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Thank you, that's a life saver. But how do I create an UITexture from code, please?

Also there is one last step I miss with the shader. It only works if the Texture has the Greyscale from Alpha checked. This is not possible to do when I load the png on the fly, via TextAsset. But it should be possible to use the greyscale RGB value in a shader in a blending mode and affect the alpha channel with it only.

EDIT: I figured a way that works with the shader I have and meets the size optimization requirements, see post below.
« Last Edit: April 30, 2013, 07:01:43 PM by andrejvojtas »

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Ok, just in case someone googles this, I figured it out, I don't need another shader, I am fine with the first one. I ended up using a 32 bit for the alpha, with RGB all turned to 0,0,0. This way the png compresses to almost the same size as a greyscale png. I used the original png32 art and reduced the Luminance in a Image editing program.

ArenMook

  • Administrator
  • Hero Member
  • *****
  • Thank You
  • -Given: 337
  • -Receive: 1171
  • Posts: 22,128
  • Toronto, Canada
    • View Profile
NGUITools.AddWidget<UITexture>()

Nicki

  • Global Moderator
  • Hero Member
  • *****
  • Thank You
  • -Given: 33
  • -Receive: 141
  • Posts: 1,768
    • View Profile
Andrej, if you're using png's directly, consider using a program like ImageOptim ( http://imageoptim.com/ ) to crush the last kb's out of them. :)

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
@Nicki: yes, thank you that's a good tip

@ArenMook: thank you, it's a place to start. Is there perhaps a place in one of the NGUI examples I imported into my project a place where this is used so I can understand the scene  setup better? What is the best practice for communicating between game object in Unity?

andrejvojtas

  • Newbie
  • *
  • Thank You
  • -Given: 0
  • -Receive: 0
  • Posts: 19
    • View Profile
Ok, I figured out the last bits. For the sake another newbie comes this way I will post the solution here.

Btw. the step by step tutorials are a great help how to get a grasp on how things are setup in nGUI. Too bad I was like 50 times on the nGUI page and never noticed them. I just though they were another unity web player examples. It would save me a lot of pain and make a much better start impression if I knew from the start that they exist.

- So I used the NGUI > Open the UI Wizard this time, it creates the UI Root > Camera etc. hierarchy and voila no need to set up camera culling masks and layers etc.
- Also the jpg and png needs to be power of two to work with the 'Make Pixel Perfect' correctly. Otherwise they were still blurry as they were stretched. Maybe it's enough to specify the exact size when creating the Texture2D? Does anybody know a different solution then preparing the images in power of two dimensions?
- To avoid white edges on the result, the jpgs must be prepared correctly. One way how to do it is to use the free Flaming Pear plugins, specifically Solidify. As a side effect they compress much better than with the white background.

Then I use this code to load and display the art pixel perfect, in best quality, with smallest install and download size. The only cost of this method is vRAM cost, but it my game I display only a couple of these at once.

  1.         void Start () {                
  2.                                
  3.                 //load the prepared image data
  4.                 TextAsset colortxt;
  5.                 TextAsset alphatxt;
  6.                                
  7.                 Texture2D colorjpg = new Texture2D(4, 4);
  8.                 Texture2D alphapng = new Texture2D(4, 4);
  9.                                
  10.                 colortxt = Resources.Load("1024cloak01") as TextAsset;
  11.                 alphatxt = Resources.Load ("1024cloak01A") as TextAsset;
  12.                
  13.                 colorjpg.LoadImage(colortxt.bytes);            
  14.                 alphapng.LoadImage(alphatxt.bytes);                                            
  15.                
  16.                 //load the prepared Shader asset
  17.                 Shader rgbplusa;       
  18.                 rgbplusa = Shader.Find("RGBplusA");
  19.                
  20.                 //use NGUI to display the image pixel-perfect, make use of the camera setup created with NGUI > Open the UI Wizard
  21.                 UITexture perfect = NGUITools.AddWidget<UITexture>(GameObject.Find ("Panel"));                         
  22.                 perfect.material =  new Material (rgbplusa);
  23.                 perfect.material.SetTexture("_MainTex", colorjpg);
  24.                 perfect.material.SetTexture("_Mask", alphapng);
  25.                 perfect.MakePixelPerfect();
  26.                                        
  27.         }
  28.  
« Last Edit: May 01, 2013, 01:04:08 PM by andrejvojtas »

aikitect

  • Newbie
  • *
  • Thank You
  • -Given: 1
  • -Receive: 0
  • Posts: 27
    • View Profile
Andrej, if you're using png's directly, consider using a program like ImageOptim ( http://imageoptim.com/ ) to crush the last kb's out of them. :)

Will this work for UIAtlases?