A couple of years ago I said, in Prospects for Immersive Panoramas in Flash:
The displacement map in Flash provides only one byte of precision for each of x and y, which means it can map a pixel value from one of at most 128 discrete locations on one side, and only 127 on the other. So when you map pixels over larger distances (which you can do using the scaleX and scaleY parameters of the DisplacementMapFilter constructor), you lose precision.
And Psyark said this, in Psyark’s DisplacementMapFilter Tutorial:
Image quality can be improved by using as much as possible of the 0x00ï½ž0xFF range of each of the map image components. I wonâ€™t go into detail on the procedure, but these are the main points:
1. Multiply the mapâ€™s X component by N with 0x80 as a center
2. Divide the mapâ€™s scaleX by N
If N is so large that the range 0x00ï½ž0xFF is exceeded, adjust it appropriately.
So it has been standard practice to set the scaleX and scaleY parameters heuristically to minimize image degradation.
Well in Flash 10 that is all moot, thanks to PixelBender!
Click here to open BenderDemo in a new window (requires Flash Player 10 of course).
The program uses a slightly modified version of Ryan Taylor’s Pixel Bender DisplacementMapFilter. I’ve also written an ActionScript wrapper class, DisplacementMapShader, for it. Both images are generated using the DisplacementMapShader, the left one with a BitmapData displacement map just as in the DisplacementMapFilter, and the right one using a ByteArray of floats as the displacement map.
There are some interesting API differences between the shader and the built-in DisplacementMapFilter. Let’s compare the constructor calls:
_displacementMapFilter = new DisplacementMapFilter(_map as BitmapData, null, BitmapDataChannel.BLUE, BitmapDataChannel.GREEN, _outputSize, _outputSize, DisplacementMapFilterMode.COLOR, 0xc0c0c0); _displacementMapShader = new DisplacementMapShader(_map, new Point(_outputSize/2, _outputSize/2), BitmapDataChannel.BLUE, BitmapDataChannel.GREEN, 2, 2);
First we note the mapPoint parameter, which is defined as “A value that contains the offset of the upper-left corner of the target display object from the upper-left corner of the map image.” We pass null to the DisplacementMapFilter to signify that we want the map to be aligned with the input image. But for the DisplacementMapShader we pass new Point(_outputSize/2, _outputSize/2). Recall that a Shader has no concept of the image size or location; it operates over a potentially infinite coordinate system. I believe that this parameter indicates where the origin of the coordinate system is to be located.
Then we have the scaleX and scaleY parameters, “The multiplier to use to scale the x displacement result from the map calculation” and “The multiplier to use to scale the y displacement result from the map calculation.” What this means is that the range of the displacement channel data (0 to 255 for the DisplacementMapFilter; 0 to 1 for the DisplacementMapShader), which specifies the range from max negative displacement to max positive displacement, is mapped to the interval we pass in this parameter. For the DisplacementMapFilter we pass _outputSize, indicating a range of the image size, i.e. half that many pixels in the positive direction and half that many pixels in the negative direction. For the Shader, we pass the value 2, indicating that we want a max negative displacement of 1 and a max positive displacement of 1. We’re saying that we want the displacement map to cover a width of two units; this combined with the mapPoint indicates that the map covers a square of width 2 centered at the origin.
Download the source archive. Bender saves the day!