Pixel precision in Papervision3D

I used to work for a company that made of the concept of pixel perfection one of the pillars of their success. Today most of the top clients and agencies demand high quality, and pixel perfection became one of the important components of a great website. Obviously, it concerns 3D as well.

To achieve it, sooner or later in your 3D adventures you will come across one of these problems:
1. A material should be rendered as in 1:1 scale of the original texture
2. A precise, pixel level match between 2d and 3d content is necessary

In order to check how to solve these problems, I created a very simple demo. First, I had to make sure my textures are not scaled. I started to browse the Papervision3D forum and it wasn't long before I found the magic formula:

Actionscript:
  1. 3dobj.z = ((camera.zoom - 1) * camera.focus) - Math.abs(camera.z)

Many thanks to unnormaJH and kjangsa3 for this tip. I would add that it works just as it is only with planes. In my example I worked with a cube, and a cubes Z position is defined by the point in the center of it - which means that the face facing the camera will be closer half the cubes width, so remember that this needs to be adjusted.

UPDATE. With the new Camera classes (GW rev. 639 an higher) the formula changes a bit. The "-1" in "zoom-1" is no longer needed. So it looks like this:

Actionscript:
  1. 3dobj.z = (camera.zoom * camera.focus) - Math.abs(camera.z)

The second problem - a pixel perfect match between 2D and 3D - is mainly about being able to figure out the screen X and Y coordinates of the 3D object. They can be obtained that way:

Actionscript:
  1. screenPosX = 3dobj.screen.x + viewport.viewportWidth / 2;
  2. screenPosY = 3dobj.screen.y + viewport.viewportHeight / 2

That, I found in an article on PV3D.org which shows how to do this. There is also an example of how to get the X and Y coordinates of a single vertex.

In the demo above, knowing the screen coordinates of the cube, I use the BitmapData.draw method to take a snapshot of the 2D background at this position for use as the texture. If you scale the browser window you can see how the texture on the cube is being updated to exactly match the background tile.

In the real world, what you'll need much more often is to do the opposite, that is drawing 2d content exactly on top of a 3d object. An example would be replacing a texture with a Sprite containing some interactive elements. By knowing the screen coordinates of your 3d object, and by making sure it is scaled 1:1 it is a quite easy task to make that look seamless & perfect!

Source code - get it here.

Credits The tile graphics come from Squidfingers.

19 Responses to “Pixel precision in Papervision3D”

  1. Playfool® - Darren Richardson » Blog Archive » Pixel precision in Papervision3D Says:

    […] Everyday Flash / ยป Pixel precision in Papervision3D / by Bartek Drozdz […]

  2. hebchop Says:

    very helpful. Thanks!

  3. garrymatteson Says:

    You should add onStageResize listener to change pixel-background position when changing window size :)

  4. Piergiorgio Niero Says:

    Very cool!:)

  5. bartek drozdz Says:

    @garrymatteson Actually I did, but there was a bug. Now it’s fixed. Thanks for finding that one!

  6. Mr.doob Says:

    That’s a clever demo! :D

  7. Mark Mun Says:

    nice, I always wondered what the 2d pixel perfect equivalent was.

  8. lukasz Says:

    hi bartek,

    this is a great example and i can see the binary you supplied in the deloy folder working, but when i compile i get a slightly different result.

    when compiling, the cube material bitmap seem larger then the background.
    im using Papervision3D Public Alpha 2.0 - Great White (24.03.08) and i know theres work being done in the camera classes so just wondering if this could be causing the problem.

    which means that the equation,
    3dobj.z = ((camera.zoom - 1) * camera.focus) - Math.abs(camera.z);
    no longer holds true, for some reason…

    anyhoo, if you have any information on this i would be interested to hear it.

    thx
    Lukasz

  9. daddfristy Says:

    how do you reverse the equation to solve for camera.z? i came up with

    camera.z = ((camera.zoom - 1) * camera.focus) - 3dobj.z or
    camera.z = ((camera.zoom - 1) * camera.focus) - Math.abs(3dobj.z) and best but still did not work right
    camera.z = ((camera.zoom - 1) * camera.focus) + 3dobj.z;

    but it did not look right. I’m guessing I’m doing something wrong with the Math.abs

    algebraically challenged

    -df

  10. bartek drozdz Says:

    @lukasz you are right. the all camera classes have been changed after I did that demo. I was able to make it work again with the lates revision of GW. It seems like the ‘zoom - 1′ is not necesarry anymore… just use ‘zoom’ in the equation. I’ll post an updated ZIP with the sources later.

    Anyway, since the camera classes have been reworked I will need to check the formula again. Hopefuly I will post something on that soon. I will also see the reversing of the equation that daddfristy is mentionning.

  11. Pedro Taranto Says:

    thanks bartek
    thats do the trick

  12. daddyfristy Says:

    hello, i was able to get the results I wanted with:
    distance between camera and object = camera.focus * camera.zoom

    thanks for a real helpful tutorial

    -df

  13. TK Says:

    Wait, daddyfristy, how did you calculate the camera movement?

    - TK

  14. TK Says:

    Bartek,
    I’m trying to solve something similar to this. I have a camera and a plane and I need to make sure that the top edge of the plane is always at the top edge of the viewport/camera’s viewing area. I need to do this with the camera, not the plane, for display purposes. How would I do the math to figure this out?

    - TK

  15. Frameworks | Papervision 3D Web Roundup - Tutorials and Sources « Flash Enabled Blog Says:

    […] Pixel precision in Papervision3D […]

  16. lauritz Says:

    great! thanks.

  17. Max Says:

    You forgot to mention on thing, if you are using a cube, that cube is centered. This equation will work fine with planes but for anything with depth you must use the following equation:

    cube.z = (camera.zoom * camera.focus) - (Math.abs(camera.z) - cubeDepth/2)

  18. Piotr Says:

    Wonderful! Saved me a couple of hours :)

  19. Peter Briers Says:

    I use this one in your demo to get it working right:
    cube.z = -((camera.zoom * camera.focus) - (Math.abs(camera.z) - baseSize / 2));

    Basicly the same as Max’s solution, but with the minus in front of the whole equation.

Leave a Reply