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:
November 25th, 2008 at 12:46 pm
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 ?
November 25th, 2008 at 7:03 pm
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
November 26th, 2008 at 10:15 am
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.
November 26th, 2008 at 9:27 pm
[...] 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 [...]
November 26th, 2008 at 9:33 pm
Hey Chris – I took your advice and revamped my code:
http://www.dafishinsea.com/blog/2008/11/26/swimming-with-the-fishes/
Cheers,
Dave
August 17th, 2009 at 7:41 pm
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.