Working at Pixel Level with BitmapData and Away3D

Welcome to this introduction to working at pixel level with ActionScript 3's BitmapData object. We'll take some 2D images, break them into their component pixels, then re-assemble them as 3D images which we can move and rotate.


Final Result Preview

Let's take a look at the final result we will be working towards:


Step 1: Set up

Just before jumping in let's take a moment to look at how the sample project is laid out. Opening the source zip for this tutorial you will have sources for each significant step, you can go right ahead and make a copy of the begin folder as this will serve as our starting point.

Inside this folder you'll find two other folders; src and bin. The src folder is where we will be saving all of our code and FLA files and the bin folder is where the Flash will save the SWF files. Inside the src folder there is the Main.FLA and the Main.AS document class.

If for any reason you find an error in your code, have an attempt to fix it (always good to learn from mistakes) but if you can't then don't worry! You can jump right back in and use one of the steps folders in the source zip that is closest to the step you were on.


Step 2: Download Away3D

If you've already had a peek of the Main.as file you'll already notice a few references to Away3D, a 3D framework for Flash. We're going to need to download this and add it to our project to continue.

You can grab their latest version from the Away3D site.

Once this download has completed, open the zip file and inside the away3d_3_6_0\src folder you will find three folders, away3d, nochump and wumedia. Copy these, as shown below to your src folder.

Copying Away3D to your src folder

Step 3: The Flash File

If you haven't already, open Main.fla and Main.as. Looking in the Flash Library you can see an image called 1.png and a MovieClip with an instance name of img1, which will serve as a basic container for the png.

We're going to perform a quick compile just to make sure we've added Away3D correctly. If all goes well we should see a blank Flash movie with a dark grey background and no error messages from Flash.


Step 4: The Main.as File

Examining the Main.as file we can see a few variables that are used in Away3D, there's already a host of tutorials on Away3D but we'll quickly recap on these:

  • Scene3D is a space we can use to add 3D objects like cubes and spheres.
  • TargetCamera3D is one of the many types of cameras available in Away3D, it's what we use to look at the Scene3D.
  • View3D is a viewport, often described as the "window" in which we see our scene.

Without going in to specifics you can also see a basic scene is setup ready for use with the initAway3d() method. Notice it adds an ENTER_FRAME EventListener, this simply tells Away3D to render() (or draw) any objects added to the Scene3D each frame.

That's pretty much it for the introduction to the Main.as class, we'll be building everything else as we go.


Step 5: Bitmaps and BitmapData

We're going to jump straight in and introduce these two classes, as we'll be working with these throughout the tutorial. If you're new to Bitmap and BitmapData you can think of them as a painters canvas and a collection of paint daubs. They're entirely different objects but are both connected, the BitmapData contains all pixel information or brush strokes and would be nothing without being painted on to a canvas or in this case, the Bitmap!

Let's test this out by adding an instance of the img1 MovieClip to the stage and making a copy of it using Bitmap/BitmapData.

Amend the Main.as to the following:

Looking at the drawExample() code, the first two lines simply add the img1 object to the stage, this is the image we will make of copy of.

Following that we create a BitmapData object with the following parameters:

  • width, the width to make the BitmapData
  • height, the height to make the BitmapData
  • transparent, whether the BitmapData should contain transparent pixels
  • color, the background color

As we know the width and height from img1 we have set them directly, as we're going to need transparency we set the next parameter to true and lastly we specify 0x000000 or black as the background color as it'll appear transparent until we fill it.


Step 6: Bitmaps and BitmapData Continued

Continuing on, now we have the BitmapData object set up we have several options available to us, we could for example loop through pixel by pixel and copy the image (we'll use something like this later on in the tutorial), or we could use the draw() method.

The draw() method takes a MovieClip or Sprite as a parameter and will copy all of the pixel information from the object to the BitmapData.

Following this, the next few lines create a Bitmap object with the BitmapData pixel information as a parameter, which is then moved below the original img MovieClip and added to the stage.

There's not a lot of setup involved in setting up the Bitmap aspect it simply displays a BitmapData, all the magic is with the BitmapData. Now when testing we should get the following:

BitmapData draw

Step 7: Reading Pixel Information

Now we have content inside the BitmapData object things begin to get interesting as we can start manipulating images using getPixel32() and setPixel32().

Starting with getPixel32() amend the drawExample() code from above to the following:

Examining the code we've created a regular uint variable and assigned it to the value of the pixel in the bmpData at 5 pixels horizontally and 0 pixels vertically. Remember the values begin at 0 as so:

Pixel coordinates

Knowing that we chose to get the pixel information for 5,0, that would make it black pixel on the top row and sure enough Flash outputs: 4278190080 ff000000

That might not seem right at first, but setPixel32 reads the alpha value of the pixel (where as setPixel just reads the color). We're generally used to working with hex values for colors such as FFFFFF or 000000 so we can tell Flash to toString(16) to get the hex value:

getPixel32 and hex values

Step 8: Drawing Pixels

Now we know how to read pixel information, drawing pixels to the BitmapData is very similar, only this time we use setPixel32() to draw pixels to the BitmapData, and we'll also throw in a for loop to draw some pixels.

First amend the code to the following:

The new code starts off creating a regular uint variable named color which we store 0xffff0000 which is: ff fully transparent, ff fully red, 00 no green, 00 no blue.

Then there are two counters made for rows and columns (rows are a line of horizontal pixels, columns are a line of vertical pixels). These counters are then put in to a for loop which increases the row and counter value each time, so when mixed with the setPixel32() method it will draw a diagonal line:

setPixel32 at work

Step 9: The PixelObject3D Class

In this step we're going to introduce the PixelObject3D.as class. To save a bit of time grab a copy of the class from the Step 8 folder in the source zip and drop it in to your src folder besides the Main.fla and Main.as.

Once you've done this, lets have a quick look at it before we begin adding the code to create 3D objects from pixels.

We have a few protected variables at the top of the class, one for a BitmapData and three Numbers for the width, height and a scale of the object.

Following them is an empty class constructor and the method we will be working with, createFromMovieClip(). You'll notice this method takes a parameter of MovieClip type, so as you can already guess we pass it a MovieClip and it'll give us back a 3D representation of it. When it's finished that is!


Step 10: An Instance of the PixelObject3D Class

While the PixelObject3D.as class doesn't actually do anything yet let's add an instance of it to the Main.as class so we can actually see the changes on screen as we go.

Starting with adding a private variable:

Following that add to the constructor a call to createPixelObect3D().

Lastly add the following function to the Main.as file. This will create an instance of the PixelObject3D class, invoke the createFromMovieClip() method and pass it a new MovieClip, the img1 we've used previously.

One last line to point out is that we add the PixelObject3D class as child of the scene as it's a 3D object, not the Stage.


Step 11: createFromMovieClip(mc:MovieClip)

Knowing we are passed the MovieClip we want to recreate from this method, the first thing on our agenda is to make a copy of it using BitmapData exactly as we did before. We can then use the pixel data to begin create 3D objects.

Just as before, we're going to create a BitmapData object and draw the mc MovieClip object:

We also set the _width and _height variables according to the mc width and height and multiply this by the _scaleFactor variable, this allows us to scale up or down the size of the 3D pixels if we wish. More on this later.


Step 12: createFromMovieClip(mc:MovieClip)

Remember the BitmapData is only the pixel information and without adding the BitmapData to a Bitmap we won't be able to see it, but we can still read and write to it. This is perfect for us as we're going to use this step to start looping through the pixels of the BitmapData and separating the red, green, blue and alpha values.

Amend your createFromMovieClip() method to match this:

Here we've set up a few variables for the color and alpha values then started a for loop based on the mc's width.

This for loop sets the pixelValue variable to the value of the current pixel using the getPixel32() method which we used earlier, but this time note we've used 0 for the second parameter which is y, so we're only going to process the first horizontal line of pixels.

Following this there is some pretty complex math known as bit masking and shifting, to save a little time you can assume each of the colors is extracted from the pixelValue variable and then output for us to see using trace(). If you do want to know more about bitwise operators, bit shifting and masking then you can find a great post at the Polygonal Labs website.

What you should see is the output of a whole bunch of 0 values but pay attention to the two alpha:255 lines, these are the two black pixels at the top of the hand.

seperating color values

Step 13: Creating 3D Objects from the Pixel Values

Phew there was quite a lot of logic in those last few steps! Now we've got the basics up and running, lets start using the pixel information we obtained earlier to create a 3D masterpiece.... almost.

If you've used Away3D or Papervision 3D before you'll be familiar with this step, we're going to start creating 3D cubes and applying materials to them. For every pixel that's alpha is 255 (opaque) we grab its color and create a material based on the color to apply to a 3D cube, below is the code to kick this off:

In the above code we've used the red, green and blue variables and created a regular hex color, which you can see output from the trace().

Then the hex color color variable is used to create a ColorMaterial with Away3D, which is just a regular material based on a color which can be applied to 3D objects.

Following that we create a Cube object and specify the material to be the material object we created the line before it. Also worth noting here is we've set the width, height and depth (remember we're working in three dimensions now!) to a value of twice the value of the _scaleValue variable, this allows us to make the cubes bigger or smaller by changing _scaleValue.

Lastly we position the Cube to zero minus half the width of the mc multiplied by the for loops counter i, this makes the registration or transform point of the finished 3D object in the center. It's then added as a child and when you test you will see two small black 3D Cube objects.


Step 14: Rows and Columns

Now two 3D cubes is great and all but we really want to get the whole hand shape in to 3D cubes. We're already using a for loop to loop through all the pixels in the first row, but how do we get it to loop through the remaining rows of pixels?

You guessed it, another for loop!

This time we've only really changed three things, a new for loop that this time has j for its counter. The getPixel32() now has the j variable added as the y parameter and lastly the Cube is positioned vertically using the j counter.

This pretty much completes the main logic, now it will loop through horizontally, read the pixel values, create a ColorMaterial and a Cube and position them accordingly. Once it reaches the end of the horizontal line, because of the new for loop it will move on to the next pixel down and loop through horizontally again until the image is complete. Have a look for yourself by testing the movie:

The for loops working perfectly

Step 15: Into the 3rd Dimension

We now have all of these 3D objects but they're looking very 2D, so we're going to add a bit of movement and get the whole object rotating.

To do this we'll have to back track to the Main.as file and locate the renderLoop() method. Remember Away3D will need to render (or paint) the 3D image every frame, so we can add some simple rotations to our PixelObject3D to see all the child Cubes rotate:

Feel free to experiment with rotationX, rotationY and rotationZ here just remember to reset it back to the code above before continuing. You can also add to the create3DObject() to better center and align the Cubes to the camera.


Step 16: Exploding the PixelObject3D

Now this is more like it, we can finally see the 3D pixel object rotating. We can begin tweaking this and add an exploded view by simply editing the z value of the Cubes when we create them.

Jump back in to the PixelObject3d.as class and find the lines where we position the Cube's x and y and add the following:

This will move each Cube to a random depth from -25 to positive 25 and create a nice exploded effect:


Step 17: Scaling

As the PixelObject3D is a bit small on the screen, we're going to adjust the scale slightly. We can do this quickly by adjusting the _scaleValue variable in the PixelObject3D.as class and increasing it to 1.5.


Step 18: Different Images

Using the PixelObject3D class to create other images is easy, simply import the image you want to process in to Flash. Then convert it to a MovieClip as per usual, this time give it a Class Name of img2 like this:

Different images

Now you can alter Main.as to use the new img2 object with one tiny alteration:


Step 19: Multiple Objects

You can use as many of these as you like, just make sure you add them to the Away3D scene and you could have several. In this example I've removed the z property that we used in Step 16 for the explosion effect.

Main.as with another PixelObject3D added:

Then create another instance:

And lastly rotate it in the Away3D render loop:


Step 20: Fin.

All that's remaining is for you to test your movie and bask in the wonderfulness of 2D pixels transformed in to 3D objects. Now, what can you do with BitmapData in your next application or game?


Conclusion

Through this tutorial we've looked at a mixture of items but we've primarily focused on BitmapData usage such as drawing MovieClips in to BitmapData, using setPixel32() to draw individual pixels, displaying the BitmapData by using a Bitmap and reading pixel values using getPixel32().

We've also covered some color math, getting hex colors and even individual alpha, red, green and blue values using toString(16). Lastly we wrote a small loop to create 3D Cubes using the pixel values we read, phew!

There's so many possibilities when working at pixel level and with a little imagination and experimentation you can create some really cool applications and games! Thanks for your time, I hope you enjoyed this tutorial.

Tags:

Comments

Related Articles