Prevent Bitmapification !

Today Keith Peters posted about how vectors degrade in Flash Player 10 when they are given a non-zero z value. Apparently vectors are turned into bitmaps and then scaled when they are rendered in perspective. Bad Adobe! Well I thought about it a bit more and realized that a workaround for this would be to scale the points that make up the shape, and then redraw the shape using good old 2d drawingAPI. It proved to be fairly tedious, so you’re probably better off doing things the old fashioned way as Mr. Peters suggests. but I did get it to work so I figure I should post it anyways. I’m hoping that getting to grips with the new 3D API will pay off eventually…

flash 10 required

Try zooming in (right/ctrl-click + ‘zoom in’ ) and you’ll see the fishes stay sharp.

Here’s the code:

Fishes3D

Fish3D

6 Responses to “Prevent Bitmapification !”

  1. chris nuuja Says:

    Nice work. An alternate/easier way of doing it is to use localToGlobal (or local3DToGlobal for 3d points), which works across the 3d->2d projection. Make the following changes to Fish3D.as

    < private var vertices:Vector. = new Vector.();
    -> private var vertices:Vector. = new Vector.(10);

    // remove this from init()
    < //we must convert the array of points to a vertices vector
    < points.forEach(function(i:Point,n:int, a:Vector.):void {
    < vertices.push(i.x,i.y,0);
    < });

    And change render() to this:

    public function render(/*e:Event*/):void {
    points.forEach(function(i:Point,n:int, a:Vector.):void {
    var glob:Point = localToGlobal(i);
    vertices[n*2] = glob.x;
    vertices[n*2+1] = glob.y;
    });

    var g:Graphics = (root as Sprite).graphics;
    // or alternately use this.graphics and subtract (localToGlobal(new Point(0,0)) from each of the projected points
    g.beginFill(colour);
    g.moveTo(vertices[0],vertices[1]);
    g.curveTo(vertices[2], vertices[3],vertices[4], vertices[5]);
    g.curveTo(vertices[6], vertices[7],vertices[8], vertices[9]);
    }

    And change Fishes3D to this:
    var fish:Fish3D = new Fish3D(Math.random()*0xFFFFFF);
    fish.x= Math.random()*stage.stageWidth;
    fish.y= Math.random()*stage.stageHeight;
    fish.z= Math.random()*50 + -10;
    fish.rotationY = Math.random()*360;

    Also, not at all sure why you were keeping your own 3d properties on Fish3D and rebuilding a matrix3D each time instead of just using the matrix3D or 3d properties of the Sprite ?

  2. admin Says:

    Thanks for the tips, Chris, I wasn’t aware of localToGlobal() and friends… nice to see 3D versions of them in fp 10. I haven’t tried out your code yet , but one thing I notice is that you’re setting the .z property of the fish sprite in Fishes3D… this is what I wanted to avoid since it seems to cause the unsightly pixelated effect Keith posted about. This is also why I’m creating a new Matrix3D from the perspective object, because the transform.matrix3D is undefined until the z property is set, and I get a runtime error if I try to access it :(

  3. chris nuuja Says:

    I’m setting the .z property, but each Fish3D has no graphics. They are essentially empty containers used for positioning info. The render() method is using root’s graphics object to draw their shapes into. You could assign a special “canvas” sprite for this purpose instead of using root. The Fish3D does need to be on the displayList to get localToGlobal to work, but it doesn’t need to have anything that is drawn directly. But good point, my comment:

    // or alternately use this.graphics and subtract (localToGlobal(new Point(0,0)) from each of the projected points

    is bogus because if you did draw using the Fish3D’s own graphics, then things would be both pixelated *and* double projected (once from localToGlobal, and a second time when the Fish3D is drawn by Flash using its Matrix3D). I suppose you could set the .transform.matrix3D to a cached Matrix3D at the start of render() and then set .transform.matrix to an identity matrix at the end of render() if you really wanted each Fish to have its own graphics.

    This also brings up a problem with your original code: the fish are not all in the same 3d space. If you animated them, you might see the problem: Each one of them has their own perspectiveProjection centered on themselves. Imagine a hallway centered on stage center (or wherever the perspectiveProjection.projectionCenter is). Both left and right walls are rotated the same in Y, but you expect the left one’s edges to angle down and get smaller towards the stage center to its right and the right wall’s edges to do the mirror opposite. If each wall had its own perspectiveProjection, you wouldn’t see that. You’d see both left and right walls look identical instead of like mirror opposites. Or put another way, move all the fish backwards in Z. Instead of all disappearing towards a common point, each disappears towards its own regpoint. To be visually in the same 3d space, all objects must project against the same perspectiveProjection.

    You could solve it by using root.transform.perspectiveProjection (i.e. the default one) for all your Fish3Ds, but then your projected positions are going to be global: you would also need to set each Fish3D’s actual .x, .y to 0,0. Or use the simplier localToGlobal method I outline.

  4. Da Fish in Sea » Blog Archive » Swimming with the Fishes Says:

    [...] some insightful comments on my previous post about rendering vector graphics in 3D I’ve changed the way the fishes are drawn, using the localToGlobal() method instead of [...]

  5. admin Says:

    Hey Chris – I took your advice and revamped my code:
    http://www.dafishinsea.com/blog/2008/11/26/swimming-with-the-fishes/
    Cheers,
    Dave

  6. admin Says:

    I noticed that since updating to Flash Player 10,0,22,87 this example is not working as before .. I must have been relying on a bug which was fixed. *sigh* I will get around to fixing it at some point, but in the meantime you can check out the example linked to in my comment above this one, which is working.

Leave a Reply