Since the release of Flash 10, I’ve seen a few blog posts about creating .swc libraries that can be used in Flash. Probably the most in depth was Mike Chambers article “Creating Re-distributable Actionscript Libraries of Pixel Bender Filters”. Needless to say, I had to give it a go, myself. So, I fired up Pixel Bender and created the DirectionalBlurFilter seen below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
// Directional Blur // 01NOV08 // // TODO: // Blur from image center - not image origin <languageVersion : 1.0;> kernel DirectionalBlur < namespace : "com.onebyonedesign"; vendor : "Devon O. Wolfgang"; version : 1; description : "Directional Blur"; > { parameter float angle < minValue: 0.0; maxValue: 360.0; defaultValue: 0.0; >; parameter float blurAmount < minValue: 0.0; maxValue: 1.0; defaultValue: 0.0; >; input image4 src; output pixel4 dst; void evaluatePixel() { float2 offset; float rads = radians(angle); offset.y = sin(rads); offset.x = cos(rads); // add a little more blur float amount = blurAmount * 5.0; offset *= amount; // number of sampled images int count = 30; // very annoying pseudo loop dst += sampleNearest( src, outCoord() + offset * float(0) ); dst += sampleNearest( src, outCoord() + offset * float(1) ); dst += sampleNearest( src, outCoord() + offset * float(2) ); dst += sampleNearest( src, outCoord() + offset * float(3) ); dst += sampleNearest( src, outCoord() + offset * float(4) ); dst += sampleNearest( src, outCoord() + offset * float(5) ); dst += sampleNearest( src, outCoord() + offset * float(6) ); dst += sampleNearest( src, outCoord() + offset * float(7) ); dst += sampleNearest( src, outCoord() + offset * float(8) ); dst += sampleNearest( src, outCoord() + offset * float(9) ); dst += sampleNearest( src, outCoord() + offset * float(10) ); dst += sampleNearest( src, outCoord() + offset * float(11) ); dst += sampleNearest( src, outCoord() + offset * float(12) ); dst += sampleNearest( src, outCoord() + offset * float(13) ); dst += sampleNearest( src, outCoord() + offset * float(14) ); dst += sampleNearest( src, outCoord() + offset * float(15) ); dst += sampleNearest( src, outCoord() + offset * float(16) ); dst += sampleNearest( src, outCoord() + offset * float(17) ); dst += sampleNearest( src, outCoord() + offset * float(18) ); dst += sampleNearest( src, outCoord() + offset * float(19) ); dst += sampleNearest( src, outCoord() + offset * float(20) ); dst += sampleNearest( src, outCoord() + offset * float(21) ); dst += sampleNearest( src, outCoord() + offset * float(22) ); dst += sampleNearest( src, outCoord() + offset * float(23) ); dst += sampleNearest( src, outCoord() + offset * float(24) ); dst += sampleNearest( src, outCoord() + offset * float(25) ); dst += sampleNearest( src, outCoord() + offset * float(26) ); dst += sampleNearest( src, outCoord() + offset * float(27) ); dst += sampleNearest( src, outCoord() + offset * float(28) ); dst += sampleNearest( src, outCoord() + offset * float(29) ); // no loops allowed /* for (int i = 0; i < count; i++) { dst += sampleNearest( src, outCoord() + offset * float(i) ); } */ // normalize image dst /= float(count); } } |
What I dislike most about it is that the object blurs from the edge rather than the center, cutting the blur off rather abruptly. If anyone knows how to fix that or knows of a better Directional or Motion Blur filter, don’t hesitate to post. Lord knows when it comes to Pixel Shader coding, I’ll be the first to admit, I’m not exactly your “go to guy”.
In any case, after creating the “peanut butter and jelly file” (that’s a .pbj for those wondering), compiling it into a .swc (according to Mike Chambers’ instructions) and compiling that .swc file within a Flash .swf I got the below example.
The actionscript was simple enough - and I love the ability to treat my own filters just like a native Flash filter (just wish I was better at writing them).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 |
package { import flash.display.Sprite; import flash.events.Event; import flash.events.MouseEvent; import caurina.transitions.Tweener; import flash.text.TextField; import flash.text.TextFieldAutoSize; import flash.text.TextFormat; import src.filters.DirectionalBlurFilter; /** * ... * @author Devon O. */ public class Main extends Sprite { // Euro is item in .fla library private var _euro:Euro; private var _dirFilter:DirectionalBlurFilter; public function Main():void { if (stage) init(); else addEventListener(Event.ADDED_TO_STAGE, init); } private function init(e:Event = null):void { removeEventListener(Event.ADDED_TO_STAGE, init); var instructions:TextField = new TextField; instructions.mouseEnabled = false; instructions.selectable = false; instructions.defaultTextFormat = new TextFormat("_sans", 12); instructions.autoSize = TextFieldAutoSize.LEFT; instructions.text = "Click around the stage."; instructions.y = 5; instructions.x = int(stage.stageWidth * .5 - instructions.width * .5); addChild(instructions); _dirFilter = new DirectionalBlurFilter(); _euro = new Euro(); _euro.x = 40; _euro.y = 40; addChild(_euro); stage.addEventListener(MouseEvent.CLICK, moveCoin); } private function moveCoin(event:MouseEvent):void { var tx:Number = event.stageX; var ty:Number = event.stageY; var rad:Number = Math.atan2(_euro.y - ty, _euro.x - tx); var angle:Number = rad * 180 / Math.PI; _dirFilter.angle = angle; _dirFilter.blurAmount = 1; var dataObj:Object = { blurAmount:1 }; Tweener.addTween(_euro, { x:tx, y:ty, time:.5, transition:"easeOutQuad" } ); Tweener.addTween(dataObj, { blurAmount:0, time:.5, transition:"easeOutQuad", onUpdate:applyBlur, onUpdateParams:[dataObj] } ); } private function applyBlur(o:Object):void { _dirFilter.blurAmount = o.blurAmount; _euro.filters = [_dirFilter]; } } } |
In other news, yes it's been awhile, but I've been extremely busy lately with the new job and just settling into a new country in general. The company I'm working for now, just picked up an IIA NetVisionary award the other day and is now shortlisted for two Golden Spider awards. Wish I could say I helped out, but the awards are for projects created before my arrival. Still, it's a great atmosphere to be coming into.
Ireland is a wonderful country - the people here have been terrific about welcoming this foreigner into their midst. And, of course, finally living together with my wife for the first time in nearly 3 years of marriage is the best thing ever. Who knew life could be so good?
Very cool.
I noticed when you move at 45 degrees, the front edge gets clipped pretty badly but the trailing edge looks fine.
Hey, John.
Thanks for the comment. That clipped thing is the whole “blur from the edge” problem I was talking about. I got no idea how to solve it.. short of learning more pixel shader technique, that is..
To my surprise, I saw a strip of pink lines on the coin, defect produce after a few times of click on stage (near the text), look like FP10 have defective graphics bug? I was shock to think my nVidia graphics card is spolit.
Nice job.
It works well when you click on a position far away from the coin, but when you click on a location right beside the coin the effect looks too weird.
Congrats on the living part of the past. As for the coding, outside of the blur from edge debacle, you should be using TweenLite/Max instead of tweener for your tweening :P
I’ve seen the pink stripe ugliness myself. It too has something to do with filter code. The focus of the post was supposed to be more on the fact that you can now write your own filters and easily use them in Flash just as you would a native filter class – not so much on my filter writing abilities, which leave a lot to be desired.
Matt, my first tweening choice was actually TweenFilterLite, but when I discovered I couldn’t (at least not easily) tween custom filter properties, I reverted back to Tweener. Brand loyalty, I suspect… Thank you for the congrats.. :)
Hey Devon. Just wanted to chime in and make sure you (and others) knew that TweenLite/FilterLite/Max can easily do exactly what you’re doing with Tweener. I assume the “custom filter properties” you mentioned had to do with these lines:
var dataObj:Object = { blurAmount:1 };
Tweener.addTween(dataObj, { blurAmount:0, time:.5, transition:”easeOutQuad”, onUpdate:applyBlur, onUpdateParams:[dataObj] } );
Which would look like this with TweenLite:
var dataObj:Object = { blurAmount:1 };
TweenLite.to(dataObj, .5, { blurAmount:0, ease:Quad.easeOut, onUpdate:applyBlur, onUpdateParams:[dataObj] } );
Not that Tweener is a bad choice – I just wanted to correct what seemed like a misunderstanding as far as tweening custom properties with TweenLite/FilterLite/Max. Or did I miss something that you were trying to do with Tweener that TweenLite/FilterLite/Max couldn’t do?
Hey Jack,
Thank you for the comment and useful info. Actually what I was hoping to do though was something like:
TweenFilterLite.to(_euro, .5, {blurFilter:{blurX:0}});
except I wanted to replace the blurFilter with my own custom (DirectionalBlurFilter) filter. To be honest, I didn’t poke around the code too much. Is that possible?
No, sorry, but blurFilter:{} is specifically for BlurFilters, not any old custom filter. But I believe Tweener (and any popular tweening engine) has that requirement as well. It’s pretty easy to accomplish what you’re after with an onUpdate, though (as your code demonstrated). TweenLite/FilterLite/Max can tween any numeric property of ANY object, and if you need to do something special on every frame with the new value (like apply it to the “filters” property of an object), just use onUpdate to call a function that does that job for you.