Pixel Bender Explorer

Pixel Bender Explorer is simple application that lets you dynamicly load .pbj files, apply it on image and edit shader values. Application uses two classes. ShaderLoader class loads external .pbj file and casts loaded binary data into Shader object. ShaderExplorer class inspects created Shader object and generates editable form based on Shader parameters. You can load your own external .pbj files (crossdomain.xml required near .pbj file) or feel free to download and use whole .swf application localy.

Application inspired by Enumerating Pixel Bender filter parameters and metadata dynamically in Flash and Introduction to Pixel Bender: Part 2 articles.

Pixel Bender Explorer application

sk.yoz.shader.ShaderLoader

package sk.yoz.shader
{
    import flash.display.Shader;
    import flash.events.Event;
    import flash.events.EventDispatcher;
    import flash.events.IOErrorEvent;
    import flash.filters.ShaderFilter;
    import flash.net.URLLoader;
    import flash.net.URLLoaderDataFormat;
    import flash.net.URLRequest;
    
    public class ShaderLoader extends EventDispatcher
    {
        protected var loader:URLLoader = new URLLoader();
        protected var _filters:Array = [];
        protected var _shader:Shader;
        protected var _shaderFilter:ShaderFilter;
        
        public function ShaderLoader()
        {
            super();
            
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.addEventListener(Event.COMPLETE, loadCompleteHandler);
            loader.addEventListener(IOErrorEvent.IO_ERROR, ioErrorHandler);
        }
        
        [Bindable(event="filtersChanged")]
        public function get filters():Array
        {
            return _filters;
        }
        
        protected function set filters(value:Array):void
        {
            _filters = value;
            dispatchEvent(new Event("filtersChanged"));
        }
        
        [Bindable(event="shaderChanged")]
        public function get shader():Shader
        {
            return _shader;
        }
        
        protected function set shader(value:Shader):void
        {
            _shader = value;
            dispatchEvent(new Event("shaderChanged"));
        }
        
        [Bindable(event="shaderFilterChanged")]
        public function get shaderFilter():ShaderFilter
        {
            return _shaderFilter;
        }
        
        protected function set shaderFilter(value:ShaderFilter):void
        {
            _shaderFilter = value;
            dispatchEvent(new Event("shaderFilterChanged"));
        }
        
        public function load(url:String):void
        {
            loader.load(new URLRequest(url));
        }
        
        protected function loadCompleteHandler(event:Event):void
        {
            try
            {
                protected::shader = new Shader(event.target.data);
                protected::shaderFilter = new ShaderFilter(public::shader);
                protected::filters = [public::shaderFilter];
            }
            catch(error:Error)
            {
                protected::shader = null;
                protected::shaderFilter = null;
                protected::filters = [];
            }
        }
        
        protected function ioErrorHandler(event:IOErrorEvent):void
        {
        }
    }
}

sk.yoz.shader.ShaderExplorer

package sk.yoz.shader
{
    import flash.display.Shader;
    import flash.display.ShaderInput;
    import flash.display.ShaderParameter;
    import flash.events.Event;
    import flash.filters.ShaderFilter;
    
    import mx.containers.VBox;
    import mx.controls.HRule;
    import mx.controls.HSlider;
    import mx.events.SliderEvent;
    import mx.controls.Spacer;
    import mx.controls.Text;
    
    public class ShaderExplorer extends VBox
    {
        protected var _shader:Shader;
        
        public function ShaderExplorer()
        {
            super();
        }
        
        public function get shader():Shader
        {
            return _shader;
        }
        
        public function set shader(value:Shader):void
        {
            _shader = value;
            createControls();
            dispatchEvent(new Event("shaderFiltersChanged"));
        }
        
        [Bindable(event="shaderFiltersChanged")]
        public function get shaderFilters():Array
        {
            if(!shader)
                return [];
            return [new ShaderFilter(shader)];
        }
        
        protected function createControls():void
        {
            var spacer:Spacer;
            
            removeAllChildren();
            createDescriptors();
            spacer = new Spacer();
            spacer.height = 20;
            addChild(spacer);
            createInputs();
            spacer = new Spacer();
            spacer.height = 20;
            addChild(spacer);
            createParameters();
        }
        
        protected function createDescriptors():void
        {
            var param:String;
            for(param in shader.data)
                if(shader.data[param] is String)
                    add_text(param + ": " + shader.data[param]);
        }
        
        protected function createInputs():void
        {
            var param:String;
            for(param in shader.data)
                if(shader.data[param] is ShaderInput)
                    add_text(param + ": " + shader.data[param]);
        }
        
        protected function sliderChangeHandler(event:SliderEvent):void
        {
            if(!event.target.name)
                return;
            var name:String = String(event.target.name).replace(/_[0-9]+$/, '');
            var shaderParameter:ShaderParameter = shader.data[name];
            var values:Array = [];
            var i:uint = 0;
            while(getChildByName(name + "_" + i))
                values.push(HSlider(getChildByName(name + "_" + i++)).value);
            shaderParameter.value = values;
            dispatchEvent(new Event("shaderFiltersChanged"));
        }
        
        protected function createParameters():void
        {
            var param:String;
            var shaderParameter:ShaderParameter;
            for(param in shader.data)
            {
                if(!(shader.data[param] is ShaderParameter))
                    continue;
                
                shaderParameter = shader.data[param] as ShaderParameter;
                try
                {
                    this["add_" + shaderParameter.type].apply(null, [shaderParameter]);
                }
                catch(error:Error)
                {
                    add_text("unknown type (" + shaderParameter.type + ")");
                }
            }
        }
        
        protected function add_text(text:String):void
        {
            var txt:Text = new Text();
            txt.text = text;
            txt.percentWidth = 100;
            addChild(txt);
        }
        
        protected function add_slider(name:String, value:Number, minimum:Number,
            maximum:Number, snapInterval:Number = 0):void
        {
            var slider:HSlider = new HSlider();
            slider.name = name;
            slider.minimum = minimum;
            slider.maximum = maximum;
            slider.value = value;
            slider.snapInterval = snapInterval;
            slider.percentWidth = 100;
            slider.addEventListener(SliderEvent.CHANGE, sliderChangeHandler);
            addChild(slider);
        }
        
        protected function add_float(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0]);
        }
        
        protected function add_float2(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0]);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1]);
        }
        
        protected function add_float3(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0]);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1]);
            add_slider(p.name + "_2", p.defaultValue[2], p.minValue[2], p.maxValue[2]);
        }
        
        protected function add_float4(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0]);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1]);
            add_slider(p.name + "_2", p.defaultValue[2], p.minValue[2], p.maxValue[2]);
            add_slider(p.name + "_3", p.defaultValue[3], p.minValue[3], p.maxValue[3]);
        }
        
        protected function add_int(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0] ,1);
        }
        
        protected function add_int2(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0] ,1);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1] ,1);
        }
        
        protected function add_int3(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0] ,1);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1] ,1);
            add_slider(p.name + "_2", p.defaultValue[2], p.minValue[2], p.maxValue[2] ,1);
        }
        
        protected function add_int4(p:ShaderParameter):void
        {
            add_text(p.name + " (" + p.type + ")");
            add_slider(p.name + "_0", p.defaultValue[0], p.minValue[0], p.maxValue[0] ,1);
            add_slider(p.name + "_1", p.defaultValue[1], p.minValue[1], p.maxValue[1] ,1);
            add_slider(p.name + "_2", p.defaultValue[2], p.minValue[2], p.maxValue[2] ,1);
            add_slider(p.name + "_3", p.defaultValue[3], p.minValue[3], p.maxValue[3], 1);
        }
        
        // TODO, matrix, matrix4x4...
    }
}

Pixel Bender Explorer application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml"
    layout="vertical" xmlns:shader="sk.yoz.shader.*"
    creationComplete="shadersService.send();" width="620" height="650"
    paddingTop="10" paddingRight="10" paddingBottom="10" paddingLeft="10"
    backgroundGradientColors="{[0xffffff, 0xffffff]}">
<mx:Script>
<![CDATA[
    import sk.yoz.shader.ShaderLoader;
    
    [Embed(source="../assets/img.jpg")]
    public static const IMAGE_CLASS:Class;
    
    [Bindable]
    private var loader:ShaderLoader = new ShaderLoader();
]]>
</mx:Script>
<mx:HTTPService id="shadersService" url="shaders.xml" />
<mx:HBox width="100%">
    <mx:CheckBox id="useFilter" selected="true"/>
    <mx:ComboBox id="combo" editable="true" 
        close="{loader.load(String(combo.value))}"
        enter="{loader.load(String(combo.value))}"
        dataProvider="{shadersService.lastResult.data.shader}" width="510"/>
    <mx:Button label="Load" click="{loader.load(String(combo.value))}"/>
</mx:HBox>
<mx:HBox width="100%" height="100%">
    <mx:Image source="{IMAGE_CLASS}" 
        filters="{useFilter.selected ? explorer.shaderFilters : []}"/>
    <mx:Canvas width="100%" height="100%" verticalScrollPolicy="auto">
        <shader:ShaderExplorer id="explorer" shader="{loader.shader}"
            height="100%" />
    </mx:Canvas>
</mx:HBox>
</mx:Application>

7 comments so far

  1. […] Pixel Bender Explorer at Jozef Chúťka's blog Share and […]

  2. Oeyvind February 18, 2010 02:27

    Where is shaders.xml ?

  3. Jozef Chúťka February 18, 2010 10:57

    good point Oeyvind, you can find it here:
    http://blog.yoz.sk/examples/pixelBenderExplorer/shaders.xml

  4. […] source: http://blog.yoz.sk/ […]

  5. as November 9, 2011 10:28

    Hi Jozef! Thank You for all PixelBender stuff! Can I / We use it for commercial project your PixelBender filters?

  6. Jozef Chúťka November 9, 2011 11:06

    Hi as, yes feel free to use it for commercial projects. All my posts are under MIT (if not specified else) http://blog.yoz.sk/2010/06/licensing-all-my-software/

  7. kaka November 11, 2011 16:23

    WOW! Thank You very much!

Leave a comment

Please be polite and on topic. Your e-mail will never be published.