3x3 Convolution Matrices in AGAL / Stage3D
It is what it is...
/**
* Copyright devon_o ( http://wonderfl.net/user/devon_o )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/uBTI
*/
package
{
/**
* A look at performing 3x3 matrix convolutions with AGAL / Stage3D
*
*
* @author Devon O.
*/
import com.adobe.utils.*;
import com.bit101.components.ComboBox;
import com.bit101.components.HUISlider;
import flash.display.*;
import flash.display3D.*;
import flash.display3D.textures.Texture;
import flash.events.*;
import flash.geom.Matrix3D;
import flash.net.URLRequest;
import flash.system.LoaderContext;
[SWF(width="512", height="512", frameRate="60", backgroundColor="#000000")]
public class Convolution extends Sprite
{
private var mContext3d:Context3D;
private var mVertBuffer:VertexBuffer3D;
private var mIndexBuffer:IndexBuffer3D;
private var mProgram:Program3D;
private var mTexture:Texture;
private var mTextureData:BitmapData;
private var mMatrix:Matrix3D = new Matrix3D();
private var mIdentity:Array =
[
new <Number>[ 0, 0, 0, 1],
new <Number>[ 0, 1, 0, 1],
new <Number>[ 0, 0, 0, 1]
];
private var mGaussianBlur1:Array =
[
new <Number>[ 0.045, 0.122, 0.045, 1],
new <Number>[ 0.122, 0.332, 0.122, 1],
new <Number>[ 0.045, 0.122, 0.045, 1]
];
private var mGaussianBlur2:Array =
[
new <Number>[ 0, 1, 0, 1],
new <Number>[ 1, 1, 1, 1],
new <Number>[ 0, 1, 0, 1]
];
private var mSharpen:Array =
[
new <Number>[ -1, -1, -1, 1],
new <Number>[ -1, 16, -1, 1],
new <Number>[ -1, -1, -1, 1]
];
private var mSharpen2:Array =
[
new <Number>[ 1, 1, 1, 1],
new <Number>[ 1, -7, 1, 1],
new <Number>[ 1, 1, 1, 1]
];
private var mEdgeDetect1:Array =
[
new <Number>[ -0.125, -0.125, -0.125, 1],
new <Number>[ -0.125, 1, -0.125, 1],
new <Number>[ -0.125, -0.125, -0.125, 1]
];
private var mEdgeDetect2:Array =
[
new <Number>[ 0, 1, 0, 1],
new <Number>[ 1, -4, 1, 1],
new <Number>[ 0, 1, 0, 1]
];
private var mSobelVert:Array =
[
new <Number>[ -1, 0, 1, 1],
new <Number>[ -2, 0, 2, 1],
new <Number>[ -1, 0, 1, 1]
];
private var mSobelHorz:Array =
[
new <Number>[ -1, -2, -1, 1],
new <Number>[ 0, 0, 0, 1],
new <Number>[ 1, 2, 1, 1]
];
private var mBoxBlur:Array =
[
new <Number>[ 0.111, 0.111, 0.111, 1],
new <Number>[ 0.111, 0.111, 0.111, 1],
new <Number>[ 0.111, 0.111, 0.111, 1]
];
private var mTriangleBlur:Array =
[
new <Number>[ 0.0625, 0.125, 0.0625, 1],
new <Number>[ 0.125, 0.25, 0.125, 1],
new <Number>[ 0.0625, 0.125, 0.0625, 1]
];
private var mEmboss:Array =
[
new <Number>[ -2, -1, 0, 1],
new <Number>[ -1, 1, 1, 1],
new <Number>[ 0, 1, 2, 1]
];
private var mEmboss2:Array =
[
new <Number>[ -1, -1, 0, 1],
new <Number>[ -1, .25, 1, 1],
new <Number>[ 0, 1, 1, 1]
];
public function Convolution()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(event:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
initStage();
initCombo();
loadImage();
addEventListener(Event.ENTER_FRAME, onTick);
}
private function loadImage():void {
var l:Loader = new Loader();
l.contentLoaderInfo.addEventListener(Event.COMPLETE, onImageLoad);
l.load(new URLRequest("http://assets.wonderfl.net/images/related_images/6/68/68ef/68efcb70438fb102c11265efe3fd1a737160999c"), new LoaderContext(true));
}
private function onImageLoad(event:Event = null):void
{
event.currentTarget.removeEventListener(Event.COMPLETE, onImageLoad);
var l:Loader = (event.currentTarget as LoaderInfo).loader;
mTextureData = (l.content as Bitmap).bitmapData;
stage.stage3Ds[0].addEventListener( Event.CONTEXT3D_CREATE, initStage3d );
stage.stage3Ds[0].requestContext3D();
}
private function initStage():void
{
stage.scaleMode = StageScaleMode.NO_SCALE;
stage.align = StageAlign.TOP_LEFT;
}
private function initCombo():void
{
var back:Shape = new Shape();
back.graphics.beginFill(0x000000, .90);
back.graphics.drawRect(0, 0, 125, 40);
back.graphics.endFill();
addChild(back);
var data:Array =
[
{ label:'Identity', data:mIdentity },
{ label:'Gaussian Blur 1', data:mGaussianBlur1 },
{ label:'Gaussian Blur 2', data:mGaussianBlur2 },
{ label:'Sharpen', data:mSharpen },
{ label:'Sharpen (Edges)', data:mSharpen2 },
{ label:'Edge Detect 1', data:mEdgeDetect1 },
{ label:'Edge Detect 2', data:mEdgeDetect2 },
{ label:'Sobel (Vert)', data:mSobelVert },
{ label:'Sobel (Horz)', data:mSobelHorz },
{ label:'Box Blur', data:mBoxBlur },
{ label:'Triangle Blur', data:mTriangleBlur },
{ label:'Emboss 1', data:mEmboss },
{ label:'Emboss 2', data:mEmboss2 }
]
var combo:ComboBox = new ComboBox(this, 10, 10, "Identity", data);
combo.numVisibleItems = 13;
combo.addEventListener(Event.SELECT, onMatrixSelect);
}
private function onMatrixSelect(event:Event):void
{
var combo:ComboBox = event.currentTarget as ComboBox;
var mat:Array = combo.selectedItem.data as Array;
m1 = mat[0];
m2 = mat[1];
m3 = mat[2];
}
private function initStage3d(event:Event):void
{
mContext3d = stage.stage3Ds[0].context3D;
mContext3d.configureBackBuffer(stage.stageWidth, stage.stageHeight, 1, true);
var vertices:Vector.<Number> = Vector.<Number>([
// x y z u v
-1.0, -1.0, 0, 0, 0,
-1.0, 1.0, 0, 0, 1,
1.0, 1.0, 0, 1, 1,
1.0, -1.0, 0, 1, 0 ]);
mVertBuffer = mContext3d.createVertexBuffer(4, 5);
mVertBuffer.uploadFromVector(vertices, 0, 4);
mIndexBuffer = mContext3d.createIndexBuffer(6);
mIndexBuffer.uploadFromVector (Vector.<uint>([0, 1, 2, 2, 3, 0]), 0, 6);
mTexture = mContext3d.createTexture(mTextureData.width, mTextureData.height, Context3DTextureFormat.BGRA, true);
mTexture.uploadFromBitmapData(mTextureData);
// va0 holds xyz
mContext3d.setVertexBufferAt(0, mVertBuffer, 0, Context3DVertexBufferFormat.FLOAT_3);
// va1 holds uv
mContext3d.setVertexBufferAt(1, mVertBuffer, 3, Context3DVertexBufferFormat.FLOAT_2);
generateMicroProg();
mContext3d.setTextureAt(0, mTexture);
mContext3d.setProgram(mProgram);
}
private function generateMicroProg():void
{
var vertexShaderAssembler : AGALMiniAssembler = new AGALMiniAssembler();
vertexShaderAssembler.assemble( Context3DProgramType.VERTEX,
"m44 op, va0, vc0 \n" +
"mov v0, va1 "
);
var fragmentShaderAssembler : AGALMiniAssembler= new AGALMiniAssembler();
fragmentShaderAssembler.assemble( Context3DProgramType.FRAGMENT,
"mov ft2.xy, fc0.yy \n" +
"mul ft3.xy, fc0.x, ft2.xy \n" +
"add ft3.xy, ft3.xy, v0.xy \n" +
"tex ft3, ft3.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft3, ft3, fc1.x \n" +
"mov ft2.xy, fc0.zw \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc1.y \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.wy \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc1.z \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.yz \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc2.x \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.zz \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc2.y \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.wz \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc2.z \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.yw \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc3.x \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.zw \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc3.y \n" +
"add ft3, ft3, ft4 \n" +
"mov ft2.xy, fc0.ww \n" +
"mul ft4.xy, fc0.x, ft2.xy \n" +
"add ft4.xy, ft4.xy, v0.xy \n" +
"tex ft4, ft4.xy, fs0<2d, clamp, linear, nomip> \n" +
"mul ft4, ft4, fc3.z \n" +
"add ft3, ft3, ft4 \n" +
"div ft3.xyz, ft3.xyz, fc4.x \n" +
"mov ft3.w, fc0.w \n" +
"mov oc, ft3"
);
mProgram = mContext3d.createProgram();
mProgram.upload( vertexShaderAssembler.agalcode, fragmentShaderAssembler.agalcode);
}
private var m1:Vector.<Number> = mIdentity[0];
private var m2:Vector.<Number> = mIdentity[1];
private var m3:Vector.<Number> = mIdentity[2];
private const PIXEL:Number = 1 / 512;
private function onTick(event:Event):void
{
if ( !mContext3d )
return;
mContext3d.clear ( 0, 0, 0, 1 );
// set vertex data from blank Matrix3D
mContext3d.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mMatrix, true);
// FC0 = CONSTANTS = [onePixel, -1, 0, 1]
mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 0, Vector.<Number>( [ PIXEL, -1, 0, 1 ]) );
// FC1 - FC3 = 3x3 MATRIX
mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 1, m1 );
mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 2, m2 );
mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 3, m3 );
// add it up
var sum:Number = 0.0;
for (var i:int = 0; i < 3; i++)
{
sum += m1[i];
sum += m2[i];
sum += m3[i];
}
if (sum <= 0.0) sum = 1.0;
// FC4 = SUM
mContext3d.setProgramConstantsFromVector(Context3DProgramType.FRAGMENT, 4, Vector.<Number>( [ sum, 1, 1, 1 ]) );
mContext3d.drawTriangles(mIndexBuffer);
mContext3d.present();
}
}
}