FP10: Pixel Bender DisplacementMapFilter
The Flash API already includes a filter for displacement maps; however I created my own Pixel Bender equivalent for the sake of testing and experimentation. Below is the source, followed by a few examples of how it can be implemented in Flash.
-
// *****************************************************************************************
-
// DisplacementMapFilter.pbk
-
//
-
// Copyright (c) 2008 Ryan Taylor | http://www.boostworthy.com
-
//
-
// Permission is hereby granted, free of charge, to any person
-
// obtaining a copy of this software and associated documentation
-
// files (the "Software"), to deal in the Software without
-
// restriction, including without limitation the rights to use,
-
// copy, modify, merge, publish, distribute, sublicense, and/or sell
-
// copies of the Software, and to permit persons to whom the
-
// Software is furnished to do so, subject to the following
-
// conditions:
-
//
-
// The above copyright notice and this permission notice shall be
-
// included in all copies or substantial portions of the Software.
-
//
-
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
-
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
-
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
-
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
-
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
-
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-
// OTHER DEALINGS IN THE SOFTWARE.
-
// *****************************************************************************************
-
//
-
// + + + + + + + + +
-
//
-
// *****************************************************************************************
-
-
<languageVersion : 1.0;>
-
-
kernel DisplacementMapFilter
-
<
-
namespace : "Boostworthy::Filters";
-
vendor : "Ryan Taylor";
-
version : 1;
-
description : "Standard filter for displacing source pixels based on map color values.";
-
>
-
{
-
parameter int2 component
-
<
-
minValue : int2(0, 0);
-
maxValue : int2(3, 3);
-
defaultValue : int2(1, 2);
-
>;
-
-
parameter float2 offset
-
<
-
minValue : float2(-256.0, -256.0);
-
maxValue : float2(256.0, 256.0);
-
defaultValue : float2(0.0, 0.0);
-
>;
-
-
parameter float scale
-
<
-
minValue : -1.0;
-
maxValue : 1.0;
-
defaultValue : 1.0;
-
>;
-
-
input image4 source;
-
input image4 map;
-
output pixel4 result;
-
-
void evaluatePixel()
-
{
-
float2 coord = outCoord();
-
pixel4 mapPixel = sampleLinear(map, coord);
-
-
// Note: The following statements are used for the sake of
-
// Flash compatibility. mapPixel[component[0]], for instance,
-
// is not allowed because the index must be a constant.
-
-
float componentX;
-
-
if(component[0] == 0)
-
componentX = mapPixel[0];
-
else if(component[0] == 1)
-
componentX = mapPixel[1];
-
else if(component[0] == 2)
-
componentX = mapPixel[2];
-
else if(component[0] == 3)
-
componentX = mapPixel[3];
-
-
float componentY;
-
-
if(component[1] == 0)
-
componentY = mapPixel[0];
-
else if(component[1] == 1)
-
componentY = mapPixel[1];
-
else if(component[1] == 2)
-
componentY = mapPixel[2];
-
else if(component[1] == 3)
-
componentY = mapPixel[3];
-
-
float x = coord.x + (componentX - 0.5) * offset[0] * scale;
-
float y = coord.y + (componentY - 0.5) * offset[1] * scale;
-
-
result = sampleLinear(source, float2(x, y));
-
}
-
}
NOTE: It is important to make sure that the main source image is the first input. Flash's ShaderFilter class makes this assumption and automatically passes it's input to the input at index 0.
In Flash, you can embed the 'DisplacementMapFilter.pbj' file at compiletime or keep it external and load it at runtime using a Loader instance. Regardless of the approach, you then feed the filter's byte code to a Shader object as seen below.
-
var sourceImage:Bitmap = Bitmap(new SourceImage());
-
var mapImage:Bitmap = Bitmap(new MapImage());
-
-
var shader:Shader = new Shader(new DisplacementMapFilter());
-
shader.data.source.input = sourceImage.bitmapData;
-
shader.data.map.input = mapImage.bitmapData;
-
shader.data.component.value = [1, 2];
-
shader.data.offset.value = [50, 190];
-
shader.data.scale.value = [1];
Once you have set the inputs and tweaked any parameters, one way to render the shader is to draw it as a fill using the Flash drawing API.
-
var container:Sprite = new Sprite();
-
container.graphics.beginShaderFill(shader);
-
container.graphics.drawRect(0, 0, sourceImage.width, sourceImage.height);
-
container.graphics.endFill();
-
addChild(container);
Alternatively, you can also create a ShaderFilter and apply it to a BitmapData object.
-
var containerData:BitmapData = new BitmapData(sourceImage.width, sourceImage.height);
-
var container:Bitmap = new Bitmap(containerData);
-
-
var displacementMapFilter:ShaderFilter = new ShaderFilter(shader);
-
var filterRect:Rectangle = new Rectangle(0, 0, sourceImage.width, sourceImage.height);
-
var filterPoint:Point = new Point(0, 0);
-
-
containerData.applyFilter(sourceImage.bitmapData, filterRect, filterPoint, displacementMapFilter);
-
-
addChild(container);
Unlike the other filter classes, the ShaderFilter class is not final - you can create subclasses for interfacing with your custom Pixel Bender filters. Performance-wise, the fill approach seems to beat out the use of a filter slightly, though that may change by the final release of Flash Player 10 as they continue to optimize things.
In terms of usage, the filter accepts two image inputs; one for a source image and one for the displacement map. The map's colors dictate the displacement of the pixels. By default, the component used for X is the green channel and for Y the blue channel. A channel value of 128 translates to 0 movement along the corresponding component's axis, where as 0 is -1, 255 is 1, and everything in between is smooth steps. This value is then multiplied against an offset and scale value before being applied as the final displacement. Here is an example:
Source image:

Map image:

Source image with displacement applied {component:[1, 2], offset:[50, 190], scale:[1]}:

Map image overlaid 50% over displaced source image:

Also available on Adobe Labs.
6 Comments so far
Leave a reply

very nice
I need an image to be applied on a spherical object. the spherical object is in an image but need another image to create effect like the second image is applied only to the spherical object in the first image
Please guide me how can I achieve it
Thanks in advance
Satya
Hi your work is amazing, I was wondering if you could make something like this,
http://img374.imageshack.us/my.php?image=distortdr6.jpg
it's done with a Photoshop filter called "Lens Correction"
Please contact me
Cheers.
Hey, just wanted to let you know that this posting was fantastic - if nothing else, the displacement code makes it a lot easier to develop my displacement bitmaps, cutting down on the hours of trial and error it usually takes me to get it 'just right'. Thanks for the examples!
to start of with im gonna title my self as displacementMapFilter expert - i have been spending alot of time the last years putting my mind into how the maps should be perfectly designed to work perfect
i did some labs:
http://www.thonbo.com/tempShowCase/lensLab.html
http://www.thonbo.com/filtersOnTheRun/
http://thonbo.com/tempShowCase/loader6_mc.html
http://www.thonbo.com/tempShowCase/push2Pinch.html
http://thonbo.com/tempShowCase/displaceTest4.html (dynamic map)
http://thonbo.com/tempShowCase/sliceisnice.html
http://www.thonbo.com/tempShowCase/sphear3.html
This is amazing news - when i was at max at milan where i think kevin goldsmith talked about pixelbender i told him that after his session i planned to do a displacementmap shader with 2 input images a map and an image - and then take the values from the map and so on but the kernal language was abit tricky so i quickly abandondoned the project because of time issues and such - but to see its finally came to life is amazing - i hope i can write you later on if any special requests come to my mind - but for now i will test some of my filters on your shader and post some links when im done - GREAT
i believe an important parameter thats missing is the point value and maybe the mode - if the map is smaller than the image the pixels outside should not be displaced so the color value outside the map should be read as 0.5 (neutral) by default - and the scale should have a larger magin but that easy to fix
how does the performance of your shader displacement filter compare with the built in player filter?
im thinking it shouldnt be any faster, but is it??
sorry... i know this is an old post.... nice work by the way...
=)