Getting the displacement you need in ActionScript 2
In my previous post I explained the basics of displacement mapping, and I promised to tell something about getting the filter to do what you want it to do. Well, in order to do so, let’s look at the picture I showed you again, written a little differently:
where ? is an unknown scale factor
It says that given a source image, if we apply a displacement map multiplied by some scaling factor, we will end up with a result image. If we look at the visual formula written like this:
ImgSource + DispMap*ScaleFactor = ImgResult,
we can see that with some basic math we get:
ImgResult – ImgSource = DispMap*ScaleFactor, in other words:
This tells us that if we have the source image and the result, that we can in theory calculate the displacement map, but since the displacement map is related to the scalefactor, there is not a single result.
Would this simple idea gives us the one possible displacement map that caused the transformation of ImgSource into ImgResult?
No, unfortunately not. Why not?
Well, in displacement mapping, pixels are being displaced (Orly?), but pixels are being lost and duplicated as well. All in all, just by looking at the original image and the displacement, you cannot usually tell which pixel came from where. This is especially true of course if multiple pixels in the source image have the same value.
But then again, we only need one displacement map that matches the formula and we are good to go. In order to find a good displacementmap we need to start with a good image. So what is a good image to start with? One that has unique pixels in it AND relates the position of a pixel to its color. Here is an example of such an image:
The x,y coordinate of a pixel is reflected in the pixel value. This allows us to relate pixel values to these x,y coordinates.
In order to transform this image to get our displaced image (the result), we can use photoshop for example. Imagine we transform this image by performing a few sphererizes on it. Now, the magic step is to simply substract the pixel values in the source image from the result image and add the ‘no displacement’ value of 0x80 to each pixelchannel, and tadaa: we have our displacement map. In order to understand how this works, we have to realize that since pixel values were actually pixel locations, the distance in value between two pixels, is actually the distance in pixels. To explain this further, let’s look at a single pixel. For simplicities sake we are leaving the scale factor out of this discussion.
Example pixel displacement
- a pixel at (16,128) has color value 0x108000
- if after applying a transform this pixel has been moved to (32,96) the distance in pixels is (-16,32).
- the pixel at (32,96) had the value 0x206000 before applying the transform.
- the difference between 0x108000 and 0x206000 is -0x10 << 16 | 0x20 << 8, which is (-16,32)
- adding 0x80 for each channel gives us: 0x70<<16|0xa0<<8 = 0x70a000
- a value of 0x70a0 in the displacement map at position (32,96) will cause a lookup at (-16,32) which is (16,128)
- the story applies to a pixel at (x,y) in general
Note that you cannot display the difference without adding the grey no-displacement map, since we cannot show negative values for a pixel channel. Select some different images below to play with this principle. You can use the code it contains to calculate your own displacement maps.
I haven’t experimented a lot with maps larger than 256×256, I will blog about that when I get to it. Ofcourse, again a picture says more than a thousand words, so here is something that shows this principle in practice:
Select an effect, and play with the slider! Note that this example has not been optimized for performance.
Here are the sources: Displacement Reference Map Example (446)
Well, I don't know about you but I can just feel the air tingle with Harry Potter magic, Pixellatio!
- for the actual process please refer to the source code. In fact: the formula is that half the distance between two pixels is added, since the maximum distance is +/- 256 while we need it to be +/- 128