Pixel Bender Disco

While playing with pixel bender, I created some simple sahders that when combined, reminds me of winamp visualizations effects. Feel free to use any in your own projects.

Click and drag mouse to make some disco effects. (wait while page header flash finishes loading so it runs smoothly)

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"
    enterFrame="enterFrame()" applicationComplete="init()"
    backgroundColor="#ffffff" frameRate="50"
    mouseMove="mouseMoveHandler(event)" 
    mouseDown="mouseDownHandler(event)" 
    mouseUp="mouseUpHandler(event)">
<mx:Script>
<![CDATA[
    private var drawing:Boolean = false;
    private var color:uint = 0x0;
    private var bitmap:Bitmap = new Bitmap();
    private var lineX:Number = 0;
    
    [Bindable]
    private var fps:Number = 0;
    private var fpsDate:Date = new Date();
    private var fpsFrames:uint = 0;
    
    [Bindable]
    private var rotationTime:uint = 0;
    
    [Bindable]
    private var blurTime:uint = 0;
    
    [Bindable]
    private var colorizeTime:uint = 0;
    
    [Bindable]
    private var twirlTime:uint = 0;
    
    [Embed(source="../pbj/rotation.pbj", mimeType="application/octet-stream")] 
    private static const ROTATION_CLASS:Class;
    private static const ROTATION_SHADER:Shader = new Shader(new ROTATION_CLASS());
    
    [Embed(source="../pbj/blur.pbj", mimeType="application/octet-stream")] 
    private static const BLUR_CLASS:Class;
    private static const BLUR_SHADER:Shader = new Shader(new BLUR_CLASS());
    
    [Embed(source="../pbj/colorize.pbj", mimeType="application/octet-stream")] 
    private static const COLORIZE_CLASS:Class;
    private static const COLORIZE_SHADER:Shader = new Shader(new COLORIZE_CLASS());
    
    [Embed(source="../pbj/twirl.pbj", mimeType="application/octet-stream")] 
    private static const TWIRL_CLASS:Class;
    private static const TWIRL_SHADER:Shader = new Shader(new TWIRL_CLASS());
    
    private function init():void
    {
        placeholder.addChild(bitmap);
    }
    
    private function mouseDownHandler(event:MouseEvent):void
    {
        drawing = true;
        color = Math.random() * 0xffffff;
    }
    
    private function initGraphics():void
    {
        container.graphics.clear();
        container.graphics.moveTo(mouseX, mouseY);
        container.graphics.lineStyle(10, color, 1);
    }
    
    private function mouseUpHandler(event:MouseEvent):void
    {
        drawing = false;
    }
    
    private function mouseMoveHandler(event:MouseEvent):void
    {
        if(!drawing)
            return;
        container.graphics.lineTo(event.localX, event.localY);
    }
    
    private function countFps():void
    {
        if(++fpsFrames < 10)
            return;
        fps = Math.round(1 / (new Date().time - fpsDate.time) * 10000);
        fpsDate = new Date();
        fpsFrames = 0;
    }
    
    private function get w():Number
    {
        return container.width;
    }
    
    private function get h():Number
    {
        return container.height;
    }
    
    private function enterFrame():void
    {
        countFps();
        
        if(!container || !container.width)
            return;
            
        var bitmapData:BitmapData = new BitmapData(w, h, true, 0x000000);
        bitmapData.draw(bitmap);
        if(rotationCheckBox.selected)
            addRotation(bitmapData, 0.1);
        if(blurCheckBox.selected)
            addBlur(bitmapData);
        if(colorizeCheckBox.selected)
            addColorize(bitmapData, color);
        if(twirlCheckBox.selected)
        {
            var radius:Number = Math.min(w / 2, h / 2);
            addTwirl(bitmapData, 10, new Point(w / 4, h / 2), radius);
            addTwirl(bitmapData, -10, new Point(w / 4 * 3, h / 2), radius);
        }
        
        addAutoFill();
        bitmapData.draw(container);
        
        bitmap.bitmapData = bitmapData;
        initGraphics();
    }
    
    private function addAutoFill():void
    {
        lineX = (lineX > w) ? 0 : (lineX + 15);
        container.graphics.moveTo(lineX - 15, h / 3 * 2);
        container.graphics.lineTo(lineX, h / 3 * 2);
    }
    
    private function addRotation(bitmapData:BitmapData, rotation:Number):void
    {
        rotationTime = new Date().time;
        ROTATION_SHADER.data.src.input = bitmapData;
        ROTATION_SHADER.data.src.width = w;
        ROTATION_SHADER.data.src.height = h;
        ROTATION_SHADER.data.rotation.value = [rotation];
        ROTATION_SHADER.data.tx.value = [w / 2];
        ROTATION_SHADER.data.ty.value = [h / 2];
        
        var job:ShaderJob = new ShaderJob(ROTATION_SHADER, bitmapData);
        job.start(true);
        rotationTime = new Date().time - rotationTime;
    }
    
    private function addBlur(bitmapData:BitmapData):void
    {
        blurTime = new Date().time;
        BLUR_SHADER.data.src.input = bitmapData;
        BLUR_SHADER.data.src.width = w;
        BLUR_SHADER.data.src.height = h;
        
        var job:ShaderJob = new ShaderJob(BLUR_SHADER, bitmapData);
        job.start(true);
        blurTime = new Date().time - blurTime;
    }
    
    private function addColorize(bitmapData:BitmapData, color:uint):void
    {
        colorizeTime = new Date().time;
        var r:Number = ((color >> 16) & 0xFF) / 0xFF - 0.5;
        var g:Number = ((color >> 8) & 0xFF) / 0xFF - 0.5;
        var b:Number = (color & 0xFF) / 0xFF - 0.5;
        
        COLORIZE_SHADER.data.src.input = bitmapData;
        COLORIZE_SHADER.data.src.width = w;
        COLORIZE_SHADER.data.src.height = h;
        COLORIZE_SHADER.data.color.value = [r / 40, g / 40, b / 40];
        
        var job:ShaderJob = new ShaderJob(COLORIZE_SHADER, bitmapData);
        job.start(true);
        colorizeTime = new Date().time - colorizeTime;
    }
    
    private function addTwirl(bitmapData:BitmapData, angle:Number,
        center:Point, radius:Number):void
    {
        twirlTime = new Date().time;
        TWIRL_SHADER.data.oImage.input = bitmapData;
        TWIRL_SHADER.data.oImage.width = w;
        TWIRL_SHADER.data.oImage.height = h;
        TWIRL_SHADER.data.radius.value = [radius];
        TWIRL_SHADER.data.center.value = [center.x, center.y];
        TWIRL_SHADER.data.twirlAngle.value = [angle];
        
        var job:ShaderJob = new ShaderJob(TWIRL_SHADER, bitmapData);
        job.start(true);
        twirlTime = new Date().time - twirlTime;
    }
]]>
</mx:Script>
<mx:UIComponent width="100%" height="100%" id="placeholder" />
<mx:Container id="container" width="100%" height="100%" backgroundAlpha="0"/>
<mx:VBox paddingLeft="10" paddingTop="10">
    <mx:Text text="fps: {fps} / 50" />
    <mx:HBox>
        <mx:CheckBox id="rotationCheckBox" selected="true"/>
        <mx:Label text="rotation {rotationTime} ms" />
    </mx:HBox>
    <mx:HBox>
        <mx:CheckBox id="blurCheckBox" selected="true"/>
        <mx:Label text="blur {blurTime} ms" />
    </mx:HBox>
    <mx:HBox>
        <mx:CheckBox id="colorizeCheckBox" selected="true"/>
        <mx:Label text="colorize {colorizeTime} ms" />
    </mx:HBox>
    <mx:HBox>
        <mx:CheckBox id="twirlCheckBox" selected="false"/>
        <mx:Label text="twirl {twirlTime} ms" />
    </mx:HBox>
</mx:VBox>
</mx:Application>

Rotation pixel bender

<languageVersion : 1.0;>

kernel RotationFilter
<
	namespace : "sk.yoz";
	vendor : "Yoz";
	version : 1;
	description : "Image rotation over transform point";
>
{
	input image4 src;
	output pixel4 dst;
	
	parameter float tx
	<
		minValue: float(0.0);
		maxValue: float(4096.0);
		defaultValue: float(0.0);
		description: "The amount of movement along the x axis to the right, in pixels.";
	>;
	
	parameter float ty
	<
		minValue: float(0.0);
		maxValue: float(4096.0);
		defaultValue: float(0.0);
		description: "The amount of movement down along the y axis, in pixels.";
	>;
	
	parameter float rotation
	<
		minValue: float(0.0);
		maxValue: float(4096.0);
		defaultValue: float(0.0);
		description: "Rotation in radians";
	>;
	
	void evaluatePixel()
	{
		float2 pos = outCoord();
		float rc = cos(rotation);
		float rs = sin(rotation);
		float dx = pos.x - tx;
		float dy = pos.y - ty;
		dst = sampleNearest(src, float2(
			tx + rc * dx + rs * dy, 
			ty + rc * dy - rs * dx));
	}
}

Blur pixel bender

<languageVersion : 1.0;>

kernel BlurFilter
<
	namespace : "sk.yoz";
	vendor : "Yoz";
	version : 1;
	description : "Simple blur";
>
{
	input image4 src;
	output pixel4 dst;
	
	void evaluatePixel()
	{
		float2 pos = outCoord();
		
		dst = 1.0 / 9.0 * (
			sampleNearest(src, pos - float2(-1.0, -1.0))
			+ sampleNearest(src, pos - float2(0.0, -1.0))
			+ sampleNearest(src, pos - float2(1.0, -1.0))
			+ sampleNearest(src, pos - float2(-1.0, 0.0))
			+ sampleNearest(src, pos - float2(0.0, 0.0))
			+ sampleNearest(src, pos - float2(1.0, 0.0))
			+ sampleNearest(src, pos - float2(-1.0, 1.0))
			+ sampleNearest(src, pos - float2(0.0, 1.0))
			+ sampleNearest(src, pos - float2(1.0, 1.0)));
	}
}

Colorize pixel bender

<languageVersion : 1.0;>

kernel ColorizeFilter
<
	namespace : "sk.yoz";
	vendor : "Yoz";
	version : 1;
	description : "Simple colorize";
>
{
	input image4 src;
	output pixel4 dst;
	
	parameter float3 color
	<
		minValue: float3(-1.0, -1.0, -1.0);
		maxValue: float3(1.0, 1.0, 1.0);
		defaultValue: float3(0.0, 0.0, 0.0);
		description: "Color";
	>;
	
	void evaluatePixel()
	{
		dst = sampleNearest(src, outCoord());
		dst.r += color.r;
		dst.g += color.g;
		dst.b += color.b;
	}
}

Twirl pixel bender effect is available for download here

3 comments so far

  1. Klas Lundberg April 2, 2010 16:48

    Nice filter, love it! I have made a pixel bender filter that changes the individual RGB color channels to any given color. This way you can set a picture to match a given set of colors. You can try it out and download it at http://last.instinct.se/graphics-and-effects/channel-colorizer-demo-application/554

    Keep up the good work!

    /Klas

  2. Jozef Chúťka April 2, 2010 17:38

    Hi Klas, thnx and nice work on channel colorizer

  3. […] Pixel Bender Disco by Jozef ChúťkaReally trippy real-time visuals created with multiple Pixel Bender filters. […]

Leave a comment

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