11 Aralık 2012 Salı

Pack It Up !

Draw Calls! We all hate it,we all have problems with it and it makes our games slow!

As you know there are lot of variables which might affect your games performance which i have explained in my earlier post.But even if you apply all those fixes,tricks whatever you call them,sometimes it's still not enough.So here it is another great way of optimizing performance.

First things first,What will you learn from that arcticle ?

I will explain Texture Atlases,Material sharing and a little bit about UV mapping.I will not go into detail of eveything,but you can ask in comments if you have any questions.

Texture Atlases

They are basicly a bunch of textures put in to a single big texture.But why do it ?First reason is preventing little hickups caused by loading textures.The catch is,when you put some textures together into one big image,vram only loads 1 texture and uses that for multiple objects in your game.This saves you from loading and unloading multiple textures all the time and with Material Sharing which i will explain later you will even save a lot of Draw Calls.
But how to do it ? I need to talk about a few more things before i can explain that.





Material Sharing

Imagine you have 5 objects,lets call them planets.They all need a different texture and a different material to hold this texture.And lets say all those planets will use the same shader.Well normally you would create 5 materials and assign 5 different textures to those materials.But that will cause 5 draw calls to draw all those planets.This is where material sharing becomes usefull,It reduces Draw Calls to 1 and also reduces the texture loading hickups with the help of Texture Atlases.

After explaining UV maps i can finally explain how to do it step by step.Because understanding those parts are quite important.

UV Mapping

Well,what is that UV thing ?Pretty straight forward,It maps your texture into your model.In the exmaple you can see a monkey texture mapped onto a head model.



But,How to do it ?

We will be using our planets again as an example.
  1. We have 5 planets and 5 textures in our hands.
  2. First we have to convert them into a Texture Atlas.
  3. You can use Texture Packer for that job.It works quite good and has a lot of options for different needs.
  4. To create a basic Texture Atlas.

    1. Run the program.
    2. Drag your textures to the right pane.
    3. Choose a place to save your file by filling Texture file box.
    4. After that you should play with the settings and choose what is best for your file.Sometimes the best atlas is when there is minimal empty spaces left or sometimes its best when all the textures are at the same size.Depends on your needs.
    5. Then you have to assign your new texture into a single material in Unity.
    6. Untill here we have created our Texture Atlas and material which will be shared across multiple objects.
      After this part we need to make a little bit coding.
      Read the code below carefully.It divides a single texture into defined rows and columns,then maps the UV to the desired part of that Texture.
      This script only works for planes which has 4 UV points.But it can be extended very easily.(As an example the one i'm using in my project can work with any model and also can animate the textures on them).
      I just didn't wanted to give you everything for free(No i'm not talking about money:)).Read the code,understand and make some simple changes to make it work with any model.Trust me just a few lines will make it work with any model !
      using UnityEngine;
      using System.Collections;
      public class TextureAtlasReader : MonoBehaviour
          public int totalColumns = 3;
          public int totalRows = 3;
          public int row;
          public int column;
          Mesh _mesh;
          Vector2[] _uvs;
          Vector2 _dimensions;
          void Awake()
              _mesh = gameObject.GetComponent<meshfilter>().mesh;
              Texture _texture = renderer.sharedMaterial.mainTexture;
              _dimensions = new Vector2((_texture.width / totalColumns) / (float)_texture.width,
                                       -((_texture.height / totalRows) / (float)_texture.height));
              Vector2[] _uvs = new Vector2[_mesh.uv.Length];
              Vector2 offset = new Vector2(column * _dimensions.x, 1 - (-_dimensions.y * row));
              _uvs[0] = offset + new Vector2(_dimensions.x * 0.0f, _dimensions.y * 1.0f);
              _uvs[1] = offset + new Vector2(_dimensions.x * 1.0f, _dimensions.y * 1.0f);
              _uvs[2] = offset + new Vector2(_dimensions.x * 0.0f, _dimensions.y * 0.0f);
              _uvs[3] = offset + new Vector2(_dimensions.x * 1.0f, _dimensions.y * 0.0f);
              _mesh.uv = _uvs;
    7. The last step ! 
      1. Just attach this script to the "Plane" which you want to use Texture Atlasing.
      2. From Editor GUI;Enter total row and column count.
      3. From Editor GUI;Enter the row and column  which you want to use for each object.
    8. And thats all ! Now you are sharing Material between multiple objects and drawing them in just a single Draw Call.

 Things to be aware of:

  • Sometimes it can be better not to use Texture Atlases.
  • You can use this code wherever you want.But if you use in your game it would be good drop by and leave me a message.So i can check you game :)
  • If you have made a better version,i would be glad to see it.
  • I'm not saying this is the best method.
  • My english is not the best. :)
  • For further Performance Optimization read Draw Call Batching from Unity website.
It took great amount of time to learn it when i was just beginning game development.So i hope it will save you a little bit of time.