Everyday 3D

Creative use of technology // A blog about 3D Flash and Actionscript by Bartek Drozdz

Loading and manipulating images in Unity3D

As I promised here's the first article exploring Unity as a 3D web application tool.

Every web application nowadays is data driven, therefore the ability to dynamically load assets is crucial. Unity3D can load plain text, images, video and sound. Since loading plain text is not so interesting, I thought I'd see how it works with an image.

Here's a little experiment I called "Image Decomposer". It loads an image passed as parameter in the URL and creates a wall of little cubes which colors are based on the pixels of the image. When you rollover the cubes they fall down slowly decomposing it, hence the name.

The code is pretty simple, you can get the source here. Below are a few things I found out while making it.

URL parameters

A very useful way to pass parameters to your swf files is to attach them as a query string in the embedding HTML, like this: flash.swf?paramA=valueA&paramB=valueB. It's a Flash classic!

The good news is that is also works in Unity. Take a look a the HTML source of the demo to see how the image URL is passed to the unity3d file. Inside Unity3D it's very easy to get access to it through a static property called Application.srcValue. It returns the whole path, including the file name and you need to parse the query string yourself. Here's a sample code in C#:

C#:
  1. string su = Application.srcValue;    
  2. string qs = su.Substring(su.IndexOf("?") + 1);
  3. char[] deli = "=".ToCharArray();
  4. string[] ps = qs.Split(deli);

The 'ps' array now contains pairs of key/values from the query string.

Loading freedom

Unity3D supports JPG and PNG format. Sadly GIFs are not supported, even animated... ;) The really nice thing is that there aren't any security restrictions when loading files from other domains. This is something that drove me crazy so many times with Flash! In Unity3D pass a valid URL and it will load the asset, it's so simple. If you don't believe me - just paste any link pointing to an image in the demo ("image" parameter in the URL) and see it for yourself.

I wouldn't expect things to be like that forever though, so use it while you can people!

Coroutines and C#

To deal with asynchronous events, like loading an external asset, Unity3D uses coroutines. They are a bit different from events in AS3, but not very much. If you are a fan of C# and want to use coroutines you may find the official documentation a bit unclear. So, first, read the official documentation here and here, and continue reading this post afterward.

What caught my attention is that to use the yield statement, the enclosing method must return IEnumerator. But all of the inherited methods in MonoBehavior, like Start(), Update() and the event handling ones return void.

Surprisingly, it turns out that you can just change the signature of any of those methods so that they return IEnumerator! If this isn't breaking OOP rules, I don't know what is. But, since it works, it's ok I guess. It's certainly not obvious. Otherwise it is possible to create a custom function that returns IEnumerator and use the StartCoroutine() method of MonoBehavior to run it.

Another thing is that with C# you need to write yield return... not just yield... The official docs being written in Javascript they tend to omit the new operator as well, and C# does not like that. So, if the docs say:

JAVASCRIPT:
  1. yield WaitForSeconds(2);

and you copy/paste this to a C# script, you need to edit this line and make it look like this:

C#:
  1. yield return new WaitForSeconds(2);

If you keep this in mind you will spare yourself some annoying compile errors.

Scaling images (or why you don't need to do this)

Once the image is loaded, it can be assigned to a Texture2D object, which is the type to store bitmaps in Unity3D. From now on it can be used as a texture for any material. For example, you can create a plane and assign it as the main texture of the plane's material or use GUI.Label to display the picture in 2D. But let's do something more interesting.

Texture2D has a few methods to extract information from the image, the most basic being GetPixel() and SetPixel(). They work exactly the same way as their equivalents in AS3 BitmapData class. The first allows to get the color of a pixel at a given coordinate, the other does the opposite.

When you start to play with these and you use images embedded in the project, make sure to select the "Is Readable" checkbox in the inspector. Images loaded dynamically are readable by default.

In the demo, no matter how big the loaded image is, the amount of cubes generated remains the same (it will adapt for different aspect ratios though). My first idea was to scale the image to something like 50x50 pixels, and then using getPixel() to fetch the color for each cube. But I found out that there isn't any function to resize an image in the API. Sure, I could write my own function, but this can get quite complex as you can see here. Fortunately there is a better way.

Texture2D has a method called GetPixelBilinear(). It uses bilinear filtering to evaluate the color of the image at a specific point defined by normalized coordinates (i.e. in 0-1 range). If the word "bilinear" sounds familiar to you it's because Photoshop also uses this algorithm to scale images. With this method the same code can be used to get pixel colors from images regardless of their size and aspect ratio:

C#:
  1. Color c1 = image.GetPixelBilinear(0,0); // always top-left color
  2. Color c2 = image.GetPixelBilinear(1,1); // always bottom-right

It's much more elegant (and faster) than scaling images!

As a side note: it would be also very easy to implement an actual resizing function using this method, in case you really need that. I'm sure that by now you can figure out how to do this :)

What's next?

The above example is simple but I hope it shows you how to get started with dynamic content. As far as images are concerned, much more is possible. By using custom shaders and render textures you can apply filter effects to images (and video). However, these features are available only in Unity3D Pro and besides, explaining it would make this post way too long. I'll do that in a separate article instead.

Categories: CSharp, Unity3d

comments RSS

14 Comments

  1. Great post! Thanks for sharing!

  2. This is a cool blog post!
    It’s nice to see a creative mind using C#, maybe it will help change peoples minds that it is not some dull boring microsoft language.
    Looking forward to the next post.

  3. Thanks!

    @Dominic C# is very elegant indeed and, I dare to say, is easy to learn even for people who only did JS and ActionScript coding so far (at least in the context of Unity3D).

    The only thing one must get used to is a lot of casting (especially between int and float :)

  4. True there is a lot of casting, but then on the other hand, it does make use of generics, and to try generic programming is to love it.
    Looking forward to the next Unity3D posts.

  5. Really cool post! I didn’t realize you could call “new” when doing yields with C#. I have been doing it the long way and declaring a new function putting the content in there. I will have to check out the code more later. thanks!

  6. [...] site Everydayflash met en ligne une petite expérience visuelle. Il s’agit de reconstituer une image quelconque [...]

  7. …Unity3D can load plain text, images, video and sound. Since loading plain text is not so interesting, I thought I’d see how it works with an image…

    Actually it would be interesting to see some proper ‘standard’ website content that we would do in flash with unity 3d. To not only treat unity as a game thing, (there is not only games in life) but a solid alternative to flash. I wonder how a RIA might look with unity or a typical commercial microsite!

  8. wicked demo by the way! But i must admit it was rather slow on my machine (macbook pro, core2 2.6ghz)

  9. John Crawford

    Actually, your URL parsing code will create an array with three values:
    ps[0] = “paramA”
    ps[1] = “valueA&paramB”
    ps[2] = “valueB”

    This code will properly parse it:

    String para = “flash.swf?paramA=valueA&paramB=valueB”;
    Debug.Log(“para = ” + para);
    String[] pList = para.Substring(para.IndexOf(“?”) + 1).Split(‘&’);
    for (int x = 0; x < pList.Length; x++) {
    String[] foo = pList[x].Split('=');
    Debug.Log("pList[" + x + "] = " + foo[0] + ", " + foo[1]);
    }

  10. @John Thanks! You are absolutely right. I had only one parameter so I didn’t think what happens if there is more.

  11. [...] Loading and Manipulating Images in Unity3D [...]

  12. I have been following your blog since EverdayFlash. Recently I also begin to learn unity3d and your posts have taught me a lot. Inspired by this post, I made this:
    http://spotlightor.com/lab/unity3d/imagewave?url=http://unity3d.com/images/top-menu/mm_unity_icon.png

    In this app, I use the ExternalCall to inform javascript and return the URL to unity(using SendMessage) since I found no attributes in Application could tell me the URL that webplayer is running.

  13. [...] http://www.everyday3d.com/blog/index.php/2010/03/09/loading-and-manipulating-images-in-unity3d/ [...]

  14. [...] already seen that any bitmap you import into a Unity3D project is represented by the class Texture2D. Of course, [...]

Leave a comment



  • FITC10


  • FITC10


  • FITC10