Mar
18
2010

Native 3D transformations issues: objects get blurry, slightly bigger and have wrong depth

Starting from Flash Player version 10 we have access to the new 3D capabilities. First of all Flash doesn't offer full 3d support, this is only 2.5d (or postcard in space). You can't create rotating boxes, but you can operate flat movie clips in the 3-dimensional space. Anyway, I decided to give it a try and create simple 3d carousel banner that will nicely fit to company's website. I was inspired by http://openscreenproject.org website, Adobe's initiative to support "our hard work" on all mobile devices possible (hello Mr Jobs...). This seemingly easy task turned out to be quite tricky and required few "workarounds" to achieve desired effect. As soon as I applied 3d transformation, movie clips got blurred and grown one pixel! Also objects that supposed to be in the front of other objects as per the z - order, were hidden in the back as per the Display List order.

First, let me show you the final project with all fixes and workarounds already applied:

The logic behind the whole project is very easy. Since we have access to 3d coordinates like "z" or rotationY from Action Script level, we can use them to operate in 3d space. I crated simple code to tween my 4 cards - movie clips (which include two layers: bitmap and static text) to different places on my "3d space" on the stage. Actually the only values to be changed are z and rotationY. The front movie clip has rotationY = 0 and z = 0, left and right MovieClips have rotationY = 45 and z = 300 and rotationY = -45 and z = 300 respectively. The fourth card, which is hidden in the back, has very large z value. Thinking that all of this is not a rocket science and should work at the first try... I pressed CTRL + ENTER and this is what I got (although it may look good initially, just rotate it and see the first issue):

 

1. Z-ordering problem

The z coordinate doesn't make any difference to Flash, as the order from the Display List is the only order when displaying objects on the stage. If my Card 4 has z=1000 and Card 1 has z = 0, but Card 1 was added to the Stage before Card 4 then according to the Display List, Card 4 will be on the front (but resized to be smaller as it's very deep in the 3d space).

To avoid that, I added one line of code that adds the Card that is going to be the front one to the stage. As you know addChild() always pushes objects to the front, in other words, to the top of the Display List. With short tweening time this actually gives an illusion that all cards are in the correct depth (z-order):

Having a feeling that this workaround is far from being satisfactory, I searched the web to find what others say. First answer was actually on Adobe Help site: http://help.adobe.com/en_US/ActionScript/3.0_ProgrammingAS3/WS18334A17-3F85-4d5a-ADB4-F5BF6196774C.html

They way how Adobe advises to reorder stage object is as follows:

1. Use the getRelativeMatrix3D() method of the Transform object to get the relative z-axes of the child 3D display objects.
2. Use the removeChild() method to remove the objects from the display list.
3. Sort the display objects based on their relative z-axis values.
4. Use the addChild() method to add the children back to the display list in reverse order.

Adobe also provides a self-explanatory example code on the same website.

So this sorted out the z-ordering problem. But then I realised that there is another problem...

2. Movie Clips with 3d transformations gets blurry

Just look at the smallest text field on each card... - yes, it's actually very hard to read. The text field at the bottom with my name has the same font, the same style and size and is much easier to read. You can right click on the last Flash banner above and select "zoom in" option to see this distortion. OK, it looks like our movie clip was converted to a bitmap. This would explain that "pixelisation" which is a result of conversion our vector objects to rasters.

After few minutes of experimenting I discovered that even if there is only one transformation applied to the movie clip: card1.z = 0, which actually shouldn't change anything as there is no rotation or anything like that, my card got blurred anyway. By just using a "z" property, I tell Flash to use the 3d transformation matrix and this automatically converts the movie clips to the bitmaps. Look on the example below, the same card, the left one with x = 0, y = 0, the right one with x = 260, y = 0, z = 0:

To see a difference better, just zoom in on the text field on a left image and compare it with a right image. Left is very crispy, right is blurred. Applying only z = 0 converted movie clip to a bitmap.

Fortunately there are many people complaining, unfortunaltely there is no perfect solution for that. This problem will occur when doing transformation with Papervision 3d and is also common for Silverlight. The most effective way to manipulate objects in 3d is to use transformation matrixes on bitmaps. This is much less CPU intensive than mathematic operations on the vector objects. Blurring is just side - effect of rounding operations when doing operations with transformation matrixes.

Senocular on kirupa.com (http://www.kirupa.com/forum/showthread.php?t=313219) advises to "scale up content in the 2D space and then scale it back in 3D". Scaled up in 2D will give a finer quality bitmap which when scaled down will look better and less blurry at "full" size. Surely it's not perfect...

Most of the developers just disable the 3d transformation matrix when 3d animation is complete (like Actionscript Girl Laughing writes on her blog: http://actionscriptgirl.com/?p=12). Note that when resetting transformation matrix, you also reset x, y, scaling, rotating... all these things that you applied to your Movie Clip. So to avoid that you can actually copy 2d matrix and apply it later on before performing any 3d operations:

1
2
3
4
5
6
7
// save transformation matrix before doing 3d 
transformation

var matrix2d:Matrix = card1.transform.matrix;

//do your 3d transformations on card1

//apply 2d transformation matrix
card1.transform.matrix = matrix2d:Matrix;


In my code, I reset 3d transformations by applying copy of 2d matrix just after the tweening of the card (that is going to be the front) is done. I do that in the Tweener's onComplete function. This is fine because as soon as the card is in front, there is no need for any 3d effects, the z is 0, no rotations etc. You can see the effect below.

Did you notice in the end of the tweening process, that the blurred image transformes into the crispy one? This is great but.... you probably also noticed that kind of small jump, like image would move a little... Just look at the bottom of the image when tweening is finishing. This actually looks like the blurred image (with 3d transformation) is few pixels higher than the crispy one (without 3d). Right... we run into another problem...

3. Movie Clips with 3d transformations gets bigger

Yes, I know, this is pretty weird. Applying 3d transformation makes the movie clips growing... This is described with examples by Matteo Sisti Sette on FlashAndMath (http://www.flashandmath.com/flashcs4/blursol/index.html). In his own words:

"The object converted to 3D doesn't just 'become blurry'. It becomes bigger. It seems that the object is being rescaled to exactly 1 pixel wider and 1 pixel higher than its original size. No matter what's the width and the height of the object, it grows one pixel. The scale factor is not constant, it depends on the sizes: horizontal_scale=(width+1)/width, vertical_scale=(height+1)/height."

Matteo actually uses his method to get rid of the blurring issue (described before in this article) but his method is not perfect. I tested it and text still gets slightly blurry. His method is good to get rid of our cards - resizing problem.

So as the workaround in my code, I created two arrays to store these x and y factors of the Cards. This is done before doing any 3d transformations:

fix3dFactorX.push(card1.width/(card1.width+1));
fix3dFactorY.push(card1.height/(card1.height+1));


Next I resize my cards before doing 3d transformation.

card1.scaleX = fix3dFactorX[i];
card1.scaleY = fix3dFactorY[i];


To recap, if 3d transformation makes my movie clip one pixel bigger, I will rescale it one pixel down (before performing 3d operation) to compensate that difference.

And this fixes my last problem with developing the 3d carousel banner. The final effect is at the beginning of this article. Hope you can't notice any of this tricks when looking on it:-)

* thanks to Martycud for the nice images used in the example banner

Comments 

 
0 #3 DQvsRA 2010-09-01 06:40
Hello! Nice example, now i have the same problem with image gallery. And that's why i ask you to publish a source code to view what is happend. Thanks to you any way.
Quote
 
 
0 #2 sigman 2010-04-12 08:44
Regarding the z-ordering problem, there is also a class SimpleZSorter available to overcome this problem. Here is a post on Lee's blog: http://theflashblog.com/?p=470 and class is available here: http://code.google.com/p/leebrimelow/source/browse/trunk/as3/com/theflashblog/fp10/SimpleZSorter.as?r=13
Implementation: SimpleZSorter.sortClips(mcCon tainer); wehre mcContainer is a movie clip containing clips to sort.
Quote
 
 
0 #1 radek 2010-03-23 20:11
Also faced these problems but I wasn't aware of resizing issue though. Thanks for sharing!
Quote
 

AS3 Tips

I'm with Adobe - Facebook group