Introduction to Pixel Bender in Flash

A quite detailed tutorial on creating custom shaders in Pixel Bender and using them in your Flash projects.

This is quite a lengthy tutorial.  It includes a brief introduction to pixel shaders in general; a tour of the most important aspects of Pixel Bender for use creating shaders specifically for Flash, directions for integrating the custom shader you create into a Flash project, and example source code for two simple examples.

Some Introductions

Shaders, specifically pixel shaders.

These are programs that operate on an image on a per-pixel basis.  Per-pixel means the program operates on each pixel individually, without reference to operations carried out on other pixels.  Because shaders operate in this per-pixel manner they very well suited to parallel processing: Having many instances of the shader operating at the same time, each on their own pixel.  Operating per-pixel means that there are certain tasks that are well suited to being performed by a shader as well as many tasks that are completely unsuitable.

Filters, Effects, Blendmodes, Kernels.

There’s a lot of terminology floating around depending on where you read about stuff.  I’m going to call everything a Shader, because that’s the name of the class I end up using in Flash for everything (not the best method for naming things, but still.) To give a very rough idea to help you follow other documents: Filters and Effects are applied to just one image; Blendmodes are applied to two (or more, in theory?) images; a Kernel is the thing you make when you type code into Pixel Bender.

Pixel Bender.

The Pixel Bender Toolkit, to give it its full name, is a neat program made by Adobe to produce custom filters and effects for use in a variety of their applications.  You should be aware when reading other sources for information on Pixel Bender that many of its functions will just not function in Flash.  Pixel Bender is available for free from Adobe, look to the right of that page.  Pixel Bender was known as Hydra when it was earlier in development, so you may see that name crop up around the place too.

Here be monsters

Using Pixel Bender to make shaders for Flash is – to put it politely – prone to inconsistencies.  The Pixel Bender Toolkit lets you run your shader within it to test it.  What you see in the preview will quite often not match what comes out in Flash.  I’m attempting to build up a record of ways in which the two do not match but it’s far from complete.  Just be aware that you should test primarily in Flash and not rely on the inbuilt preview.  Since I first wrote this tutorial the Pixel Bender Toolkit now includes an option to simulate running a shader in Flash which is helpful, but I suspect still doesn’t catch everything.  Also be sure to regularly check for an updated version from Adobe – I’ve found myself stuck trying to work around some bug which has actually already been fixed if I’d just updated.

What’s the point

So Pixel Bender can make buggy programs that are limited to operating on images in a per-pixel basis.  Why am I bothering learning this?

Because it’s fast!

You might like to look at a normal mapping example I put together recently.

Flash programs are pretty slow generally, and are limited to executing in a single thread.  Consider if you want to tint an image red in Flash.  If you’re charmingly naïve you might write something like:

for (var x:int = 0; x < image.width; x++)
{
   for (var y:int = 0; y < image.height; y++)
   {
      var colour:uint = image.getPixel(x, y);
      colour = (colour | 0xff0000); //set the red channel to 255
      image.setPixel(x, y, colour);
   }
}

This will be slow.  It’ll be slow because of the BitmapData.getPixel and BitmapData.setPixel operations being called huge numbers of times, but on a more general level it’s slow because you’re visiting each pixel in turn when you could be performing that operation on all the pixels at once.  This is effectively what a custom shader from Pixel Bender does.

Your shader will not actually operate on all the pixels at once, as it’s limited by the number of threads it can execute on the machine its on.  But thinking of it as operating on every pixel at once is a useful mental model.

Pixel Bender was designed to run on GPUs, which are especially well suited to performing many parallel tasks.  When running in Flash it is actually limited to using the CPU.  This means it’s not nearly as fast as it could be, but it still has the potential to be many times faster than plain old actionscript.

To the Pixel Bender!

Let’s get right in and actually make a shader and get it working in Flash.

Download and install Pixel Bender.

Set a couple of options within Pixel Bender (Edit menu, Preferences…) to tell it you’ll be exporting to Flash:

Select New Kernel from the File menu.

You’ll now have the code for a shader that does nothing.  Excellent!

<languageVersion : 1.0;>
kernel NewFilter
<  namespace : "Your Namespace";
   vendor : "Your Vendor";
   version : 1;
   description : "your description";
>
{
   input image4 src;
   output pixel4 dst;
   void
   evaluatePixel()
   {
      dst = sampleNearest(src,outCoord());
   }
}

Oh no, this isn’t Actionscript!  Time to learn a new language.  Good news is it’s easy, and you can ignore most of it.

So what actually matters here?

input image4 src;

This declares a variable of the type image4 named src, and says that this variable will hold the input that our shader is given.  Later we’ll see how a shader can have multiple input images.

The type image4 just means that it’s an image with 4 colour channels: Red, green, blue and alpha.  There is also image3 if you want to ignore the alpha channel.

output pixel4 dst;

This declares a variable of the type pixel4 named dst, and says that whatever value we put in this variable will be the output of the shader.  pixel4 is a single pixel with 4 colour channels.

But wait!  We’re taking a whole image as input, and only giving a single pixel as output?  Shouldn’t this be producing a whole image?

I was just getting to that.

The evaulatePixel() function is the heart of our shader.  It is executed once for each pixel of the source image.  It produces just a single pixel – our output variable named dst – which is then built up to form the finished image.

void
evaluatePixel()
{
   dst = sampleNearest(src,outCoord());
}

Here’s the evaluatePixel function in full.  The only bit you ever need change is between the curly braces.  In this case it takes the result of the built-in function sampleNearest and assigns that to dst.  Note that unlike actionscript functions where you use ‘return’ to have a function produce a value, evaluatePixel just sets the value of the variable that we’ve previously defined as the shader’s output.

What does sampleNearest do?  Press F1 in Pixel Bender to get the documentation.  The majority of the documentation doesn’t apply to us (most sections end with “Flash player note: X is not supported in Flash Player.”)  But it has lists of built-in functions and data types, most of which are supported.  sampleNearest gives us the value of a single pixel from the image passed as the first parameter, which in this case is src.  Which pixel to return is determined by the second parameter, which in this case is the result of another built-in function: outCoord. This function returns a float2 value giving the position of the pixel that in this instance evaluatePixel was called on.

Vector data types

Unlike actionscript, Pixel Bender uses something called vector data types.  These store several values within one variable.  For instance a position in two-dimensional space can be specified by a float2 value, which is just two floating point values (basically the same as number in actionscript) joined together.  You can access the individual values, or operate on them as a whole.  Let’s take an example:

void
evaluatePixel()
{
   pixel4 myPixelVariable;
   myPixelVariable = sampleNearest(src, outCoord());
   dst = myPixelVariable * 2.0;
}

First new thing:

pixel4 myPixelVariable;

Simply declares a variable named myPixelVariable which is of type pixel4.

myPixelVariable = sampleNearest(src, outCoord());

As you’d expect we can assign a value to it.

dst = myPixelVariable * 2.0;

Then we can multiply the whole thing by 2, and assign that to our output variable.

This is exactly the same as performing:

dst.r = myPixelVariable.r * 2.0;
dst.g = myPixelVariable.g * 2.0;
dst.b = myPixelVariable.b * 2.0;
dst.a = myPixelVariable.a * 2.0;

Isn’t that neat.

myPixelVariable.r is accessing one of the four elements that make up the variable.  The elements can also be accessed by:

myPixelVariable[0], myPixelVariable[1], myPixelVariable[2], myPixelVariable[3]

myPixelVariable.x, myPixelVariable.y, myPixelVariable.z, myPixelVariable.w

myPixelVariable.s, myPixelVariable.t, myPixelVariable.u, myPixelVariable.v

The suffixes [0], .r, .x and .s are all equivalent.

You can access several elements of a vector-type variable at once.

myPixelVariable.rg *= 2.0;

Will multiply just the red and green channels by two.

You can also assign values between elements of a vector-type:

myPixelVariable.rgb = myPixelVariable.brg;

This assigns the blue value of the pixel to be the red value, the red to the green and the green to the blue.

You can specify each element of a vector-type like so:

myPixelVariable.rgb = float3(0.5, 0.2, 0.8);

Fussy about types

Notice that in the examples above we multiply by 2.0 rather than just 2.  This is because unlike actionscript, Pixel Bender doesn’t do implicit conversion.  What that means is that it will interpret 2 as an integer, and not know how to multiply that by the float values of a pixel4 variable.  Writing the value as 2.0 makes it clear that you want it treated as a float. You could also use float(2) to explicitly convert the integer into a float.

Slightly weirdly there seems to be little difference between pixel4 and float4.  I think there’s a difference in how accurately the values are stored, but they are largely interchangeable.

A working shader

Let’s pull that together into a simple working shader so we can move on to implementing it in Flash:

kernel NewFilter
<    namespace : "Your Namespace";
     vendor : "Your Vendor";
     version : 1;
     description : "your description";
>
{
    input image4 src;
    output pixel4 dst;

    void
    evaluatePixel()
    {
        dst = sampleNearest(src,outCoord());
        dst.r *= 2.0;
    }
}

This will just take each pixel from an image and double the red channel.

We can give this a quick test within Pixel Bender.  Just Load Image 1… from the File menu and select any old photo you have laying around. Then hit the Run button (or F5) and the photo will be displayed with a red tint.

Now choose Export Filter for Flash Player… from the File menu.  This will create a .pbj file containing Pixel Bender byte code which Flash will be able to understand.  Whenever you change a shader you’re working on, remember you’ll need to re-export it to have those changes show up in Flash.

And in to Flash

Once you know how, getting the shader in to your Flash program is actually quite simple.  The basic technique is very similar to embedding an image, font or sound:

[Embed(source = 'TestShader.pbj', mimeType = 'application/octet-stream')]
private var TestShader:Class;

And that’s it embedded! To create an instance of the shader:

var myShader:Shader = new Shader(new TestShader() as ByteArray);

The shader we’ve created requires an input image, so we need to point myShader at an image to use as its input.

myShader.data.src.input = myBitmapData;

This assumes we already have an image loaded and named myBitmapData. Note that the src corresponds to the name we gave the input variable in Pixel Bender.

To actually run the shader, we make use of a ShaderJob.

myJob:ShaderJob = new ShaderJob(myShader, outputBitmapData);
myJob.start(true);

Again this assumes we already have a bitmapData object named outputBitmapData which is ready to receive the finished image (it should be created as the same size as the input image.)  The true value passed when calling start on myJob tells Flash you want to wait until the job is complete before continuing.  It’s possible to pass false instead and have the shader execute asynchronously, but that brings up a whole lot of other issues and a pretty major bug.

Putting this all together into a program that’ll actually work:

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shader;
	import flash.display.ShaderJob;
	import flash.display.Sprite;
	import flash.utils.ByteArray;

	public class PrettyShader extends Sprite
	{
		//embed the shader
		[Embed(source = 'TestShader.pbj', mimeType = 'application/octet-stream')]
		private var TestShader:Class;

		//embed an image to test it on
		[Embed(source = 'SomeImage.jpg')]
		private var SomeImage:Class;

		public function PrettyShader()
		{
			//set up the input and output BitmapDatas
			var myBitmapData:BitmapData = (new SomeImage() as Bitmap).bitmapData;
			var outputBitmapData:BitmapData = new BitmapData(myBitmapData.width, myBitmapData.height);

			//set up the Shader
			var myShader:Shader = new Shader(new TestShader() as ByteArray);
			myShader.data.src.input = myBitmapData;

			//create the ShaderJob
			var myJob:ShaderJob = new ShaderJob(myShader, outputBitmapData);

			//and run it!
			myJob.start(true);

			//display the input and output.
			var before:Bitmap = new Bitmap(myBitmapData);
			addChild(before);
			var after:Bitmap = new Bitmap(outputBitmapData);
			after.x = before.width;
			addChild(after);
		}
	}
}

Remember to set your compiler to target Flash Player 10, and you should end up with something rather like this:

We’re actually almost done now!  There are two more things to cover:  multiple image inputs, and parameters.

Multiple images as inputs

The example above just takes one image and fiddles with the colour a little.  You can do much more powerful things if you work with more than one image.

In Pixel Bender all we need to do is add an extra input statement for each image we want to sample from.  For example:

kernel NewFilter
<    namespace : "Your Namespace";
     vendor : "Your Vendor";
     version : 1;
     description : "your description";
>
{
    input image4 srcOne;
    input image4 srcTwo;
    output pixel4 dst;

    void
    evaluatePixel()
    {
        float2 positionHere = outCoord();
        pixel4 fromOne = sampleNearest(srcOne, positionHere);
        pixel4 fromTwo = sampleNearest(srcTwo, positionHere);
        float crossfade = positionHere.x / 400.0;
        dst = fromOne * crossfade + fromTwo * (1.0 - crossfade);
    }
}

This will sample from both images, and mix them together depending on the horizontal position of the pixel in question. You might notice that I create a float2 variable named positionHere to store the result of outCoord(), this is simply to save calling the function multiple times when I know it’ll give the same result.  Unnecessary premature optimisation or just being tidy?  You decide!

The changes in Flash are just as simple.  Simply assign another BitmapData as the shader’s second input:

myShader.data.srcOne.input = myBitmapData;
myShader.data.srcTwo.input = myOtherBitmapData;

Parameters
Parameters are simply variables that are used within the shader and can be set from within Flash.  Like input images and the output pixel, parameters are defined outside of the evaluatePixel function.  As well as specifying the data type that the parameter should be you give minimum, maximum and default values.

Here is the above shader amended with two parameters:

kernel NewFilter
<    namespace : "Your Namespace";
     vendor : "Your Vendor";
     version : 1;
     description : "your description";
>
{
    input image4 srcOne;
    input image4 srcTwo;
    output pixel4 dst;

    parameter float middleOfFade
    <
         minValue: -1.0;
         maxValue: 1.0;
         defaultValue: 0.0;
    >;

    parameter int widthOfImage
    <
         minValue: 0;
         maxValue: 5000;
         defaultValue: 400;
    >;

    void
    evaluatePixel()
    {
        float2 positionHere = outCoord();
        pixel4 fromOne = sampleNearest(srcOne, positionHere);
        pixel4 fromTwo = sampleNearest(srcTwo, positionHere);
        float crossfade = middleOfFade + (positionHere.x / float(widthOfImage));
        if (crossfade > 1.0) crossfade = 1.0;
        if (crossfade < 0.0) crossfade = 0.0;
        dst = fromOne * crossfade + fromTwo * (1.0 - crossfade);
    }
}

In this case we use two parameters, for two quite different purposes. You might have noticed that the previous versions relied on a constant value of 400.0 in the calculation of crossfade. This was the width of the image in use, but of course our shader should be ready to accept other sized images too so the parameter widthOfImage was added.  Secondly the parameter middleOfFade is added to allow interactive changes to how the two source images are mixed.

As a side note, Pixel Bender actually includes some built-in functions for determining the size of input images, but they are not available when exporting for the Flash player.

If you test this shader in Pixel Bender it will display some sliders that let you alter your parameters on the fly and see what effect they have.

Passing values to your shader from Flash is again fairly simple, although slightly obscure.

myShader.data.middleOfFade.value = [0.2];

Notice that you use .value when setting a parameter, but .input when setting an input image.  You also have to put your value inside an array first.  It makes more sense if you need to pass values into a float3 variable:

myShader.data.someFloat3Variable.value = [0.1, 0.6, 0.3];

All together now
A working Flash program that uses a shader with two image inputs and parameters.

package
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Shader;
	import flash.display.ShaderJob;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.utils.ByteArray;

	public class PrettyShader extends Sprite
	{
		//embed the shader
		[Embed(source = 'TestShader.pbj', mimeType = 'application/octet-stream')]
		private var TestShader:Class;

		//embed the images to test it with
		[Embed(source = 'SomeImage.jpg')]
		private var SomeImage:Class;
		[Embed(source = 'SecondImage.jpg')]
		private var OtherImage:Class;

		private var myShader:Shader;
		private var outputImage:BitmapData;

		private var count:Number = 0;
		private const twoPi:Number = Math.PI * 2;

		public function PrettyShader()
		{
			//set up the input and output BitmapDatas
			var firstImage:BitmapData = (new SomeImage() as Bitmap).bitmapData;
			var secondImage:BitmapData = (new OtherImage() as Bitmap).bitmapData;
			outputImage = new BitmapData(firstImage.width, firstImage.height);

			//set up the Shader
			myShader = new Shader(new TestShader() as ByteArray);
			myShader.data.srcOne.input = firstImage;
			myShader.data.srcTwo.input = secondImage;

			//set parameters
			myShader.data.widthOfImage.value = [firstImage.width];
			myShader.data.middleOfFade.value = [0.0];

			//display the output.
			addChild(new Bitmap(outputImage));

                        //update every frame
			addEventListener(Event.ENTER_FRAME, perFrame);
		}

		private function perFrame(e:Event = null):void
		{
			//update realtime parameter
			count += 0.05;
			if (count > twoPi) count -= twoPi;
			myShader.data.middleOfFade.value = [Math.sin(count)];

			//create the ShaderJob
			var myJob:ShaderJob = new ShaderJob(myShader, outputImage);

			//and run it!
			myJob.start(true);
		}
	}
}

Here it is running:

I think that is about all for now.  There is plenty more to learn about both Pixel Bender and using shaders in Flash, but this should give you everything you need to get some cool stuff working.

Of course making those awesome shaders is another topic altogether.

As a final note, keep in mind that it is possible to use custom shaders as BlendModes or Filters directly in Flash.  I have found that doing so ended up being more complex (or at least confusing), didn’t allow for the creation of anything that couldn’t be achieved through the use of Shader and ShaderJob, and may even run slightly slower.

12 thoughts on “Introduction to Pixel Bender in Flash

  1. Great article! But there’s one concept that doesn’t make sense to me. The value of the separate thread, to me, is that you can render an effect even if the flash player’s frame rate is choking. But all of the examples I’ve seen rely on the player’s frame rate to invoke the Shader. Won’t the shader run poorly when the player is under some load that is slowing the actionscript/frame thread? I’m probably missing some concept, hope you can clarify…

    Thanks!

    • Hi!
      You’re quite right, all of my examples are running the shader synchronously – by passing ‘true’ to the shaderJob.start() call – which locks up the actionscript execution until the shader is finished.
      The threaded advantage you get even from running shaders synchronously is that the execution of the shader itself is spread over several threads and so benefits from however many CPU cores the user has. The advantage is simply that the shader itself can get executed faster, and you can get back to and unpause the actionscript thread sooner.
      It is possible to run a shader asynchronously which will free it completely from the actionscript thread, and allow the actionscript to keep executing at the same time. I’ve kind of skipped over it in this article as there’s a couple of odd behaviours (the unfaithful may call them bugs) which you have to deal with. I will try to put together at least a short piece on using asynchronous shaders soon.

  2. Thanks for the reply!!

    “It is possible to run a shader asynchronously which will free it completely from the actionscript thread, and allow the actionscript to keep executing at the same time.”

    That makes sense to me, and I’ve messed around with it. But I’m starting to think that what I want to do isn’t what shaders are designed to do. Basically, I want to make a spinning progress indicator that won’t stick when I ask flash to parse, for example, a giant xml file.

    But it seems that once ‘evaluatePixel’ has evaluated all of the pixels in its target, it’s done and you can only re-invoke it by changing a parameter, which is usually done on an enter frame basis. So, you can’t really start the pixel bender, let it run without touching it for a while and then stop it. As I see it, that’s what you’d need to do to make a Shader-based preloader that didn’t rely on the framerate of your swf and would never stick no matter what the actionscript thread was doing.

    Does that make sense? Can you think of a way to achieve it?

    • Ah yes, I think you’re right and your task isn’t going to match up with what shaders can do. I don’t think it’s possible to start a shader going again except through actionscript, and if your actionscript thread is busy crunching through XML then it’s not going to be able to make that call.

      Coincidentally, I’ve been working on a very similar problem. I have a game prototype that procedurally generates its own (quite large) texture and normal maps. Even on modern machines that can take a few seconds, during which time the Flash player becomes completely unresponsive. That’s a little inconvenient, but in doing some recent public testing it seems that some browser and OS combinations will crash during that unresponsive period, which makes it a serious problem. edit I’ve since discovered that it wasn’t the Flash player locking up that caused the crash, but my trying to run shaders on overly large images. Still good practice to not lock up the Flash player though!

      To avoid the Flash player becoming unresponsive, I have split the heavy processing into many smaller chunks with their execution split between frames (not using any shader stuff for that, just plain old actionscript.) I found a quite detailed article about this approach which probably explains it better than I can.

      That said, I’m still getting some reported crashes. I’ve chopped up the processing into even finer chunks for a future version so hopefully that’ll finally fix it.

      To actually answer your question: It should be possible to keep Flash being responsive whilst also doing heavy parsing, but you’ll need to rearrange your code a little so that the parsing operation can be split between multiple frames. Shaders will probably not be much use in this case (if you were doing just heavy number crunching it would be possible to do that crunching on an asynchronous shader which would be neat.)

  3. Pingback: Tweets that mention Pixel Bender Tutorial -- Topsy.com

  4. Pingback: 픽셀벤더(Pixel Bender)에 대한 나름 간단 정리 | Freedom Developers

  5. Pingback: Paint mixing with PixelBender | Purple Squirrels | An interactive and motion design blog

  6. I have been looking for a pixel bender based cross-fade effect precisely like the one from your second example. Thanks a bunch.

Leave a Reply

Your email address will not be published.

* Copy this password:

* Type or paste password here:

16,849 Spam Comments Blocked so far by Spam Free Wordpress