In case Flash no longer exists; a copy of this site is included in the Flashpoint archive's "ultimate" collection.

Dead Code Preservation :: Archived AS3 works from wonderfl.net

Stage3D 3D Particle Boundbox

vertexシェーダで頂点座標計算

今回はこれでもテクスチャ貼り付けてあります。
ボールに見えるように。
うちの環境だと68万パーティクルまではFPS60でした。

[↑↓ or MouseWhell]...move camera
[Mouse Drag]...rotate camera
/**
 * Copyright 9balls ( http://wonderfl.net/user/9balls )
 * MIT License ( http://www.opensource.org/licenses/mit-license.php )
 * Downloaded from: http://wonderfl.net/c/elYq
 */

package {
    import com.adobe.utils.AGALMiniAssembler;
    import com.bit101.components.NumericStepper;
    import com.bit101.components.PushButton;
    import flash.display.BitmapData;
    import flash.display.Sprite;
    import flash.display.Stage3D;
    import flash.display.StageAlign;
    import flash.display.StageScaleMode;
    import flash.display3D.Context3D;
    import flash.display3D.Context3DBlendFactor;
    import flash.display3D.Context3DCompareMode;
    import flash.display3D.Context3DProgramType;
    import flash.display3D.Context3DRenderMode;
    import flash.display3D.Context3DTextureFormat;
    import flash.display3D.Context3DTriangleFace;
    import flash.display3D.Context3DVertexBufferFormat;
    import flash.display3D.IndexBuffer3D;
    import flash.display3D.Program3D;
    import flash.display3D.textures.Texture;
    import flash.display3D.VertexBuffer3D;
    import flash.events.Event;
    import flash.events.MouseEvent;
    import flash.geom.Matrix3D;
    import flash.text.TextField;
    import flash.text.TextFieldAutoSize;
    import flash.utils.ByteArray;
    import frocessing.color.ColorHSV;
    import net.hires.debug.Stats;

    /**
     * ...
     * @author
     */
    public class Main extends Sprite {

        private const NUM_PARTICLES:uint = 16383;
        private const MAX_BUFFERS:uint = 500;
        private const P_SIZE:Number = 5;
        private const WIDTH:uint = 466;
        private const HEIGHT:uint = 466;
        private const RAD:Number = Math.PI / 180;
        private const BOX_SIZE:Number = 1000;
        //
        private var stage3D:Stage3D;
        private var context3D:Context3D;
        //
        private var camera:SimpleCamera3D;
        private var particleProgram:Program3D;
        private var vBufferVXYZ:VertexBuffer3D;
        private var vBufferUV:VertexBuffer3D;
        private var vBufferRGB:VertexBuffer3D;
        private var vBufferOffset:VertexBuffer3D;
        private var iBuffer:IndexBuffer3D;
        private var constInitials:Vector.<Vector.<Number>>;
        private var constGT:Vector.<Number> = Vector.<Number>([Math.PI, 0, BOX_SIZE, 0]);
        private var texture:Texture;
        private var boxMtx:Matrix3D;
        private var mtx:Matrix3D;
        private var t:uint = 0;
        //
        private var numBuffers:uint = 1;
        private var filter:TwinkleFilter3D;
        private var isFilter:Boolean = false;
        //
        private var tfP:TextField;

        public function Main():void {
            Wonderfl.disable_capture();
            stage.frameRate = 60;
            stage.align = StageAlign.TOP_LEFT;
            stage.scaleMode = StageScaleMode.NO_SCALE;
            addChild(new Stats());
            //
            //set ui
            var tfB:TextField = new TextField();
            tfB.autoSize = TextFieldAutoSize.LEFT;
            tfB.textColor = 0xffffff;
            tfB.text = "Buffer : ";
            tfB.x = 100;
            addChild(tfB);
            var stepper:NumericStepper = new NumericStepper(this, 150, 0, onStepper);
            stepper.minimum = 1;
            stepper.maximum = MAX_BUFFERS;
            stepper.width = 60;
            tfP = new TextField();
            tfP.autoSize = TextFieldAutoSize.LEFT;
            tfP.textColor = 0xffffff;
            tfP.text = "Particle : " + NUM_PARTICLES;
            tfP.x = 250;
            addChild(tfP);
            var pushButton:PushButton = new PushButton(this, 350, 0, "Twinkle", onPush);
            pushButton.toggle = true;
            new PushButton(this, 350, 20, "Reset", onReset);
            //
            stage3D = stage.stage3Ds[0];
            stage3D.addEventListener(Event.CONTEXT3D_CREATE, onContextCreate);
            stage3D.requestContext3D(Context3DRenderMode.AUTO);
        }

        private function onStepper(e:Event):void {
            numBuffers = (e.currentTarget as NumericStepper).value;
            tfP.text = "Particle : " + NUM_PARTICLES * numBuffers;
        }

        private function onPush(e:MouseEvent):void {
            isFilter = !isFilter;
        }
        
        private function onReset(e:MouseEvent):void {
            t = 0;
        }

        private function onContextCreate(e:Event):void {
            context3D = stage3D.context3D;
            //context3D.enableErrorChecking = true;
            context3D.configureBackBuffer(WIDTH, HEIGHT, 0, true);
            context3D.setCulling(Context3DTriangleFace.BACK);
            context3D.setBlendFactors(Context3DBlendFactor.SOURCE_ALPHA, Context3DBlendFactor.ONE_MINUS_SOURCE_ALPHA);
            context3D.setDepthTest(true, Context3DCompareMode.LESS);
            //
            filter = new TwinkleFilter3D(context3D, WIDTH, HEIGHT, 3);
            camera = new SimpleCamera3D(45 * RAD, 1, 0.1, 3000);
            new RoundCameraController(camera, stage);
            createBuffer();
            createShader();
            boxMtx = new Matrix3D();
            boxMtx.appendTranslation(-BOX_SIZE / 2, -BOX_SIZE / 2, -BOX_SIZE / 2);
            mtx = new Matrix3D();
            //
            texture = context3D.createTexture(64, 64, Context3DTextureFormat.BGRA, false);
            var sp:Sprite = new Sprite();
            sp.graphics.beginFill(0xffffff);
            sp.graphics.drawCircle(32, 32, 32);
            sp.graphics.endFill();
            var bd:BitmapData = new BitmapData(64, 64, true, 0x0);
            bd.draw(sp);
            texture.uploadFromBitmapData(bd);
            //
            context3D.setTextureAt(0, texture);
            constInitials = new Vector.<Vector.<Number>>(MAX_BUFFERS);
            constInitials[0] = Vector.<Number>([BOX_SIZE / 2, BOX_SIZE / 2, BOX_SIZE / 2, 1]);
            for (var i:int = 1; i < MAX_BUFFERS; i++){
                constInitials[i] = Vector.<Number>([BOX_SIZE * Math.random(), BOX_SIZE * Math.random(), BOX_SIZE * Math.random(), 1]);
            }
            //
            addEventListener(Event.ENTER_FRAME, onEnter);
        }

        private function onEnter(e:Event):void {
            t += 1;
            constGT[3] = t;
            mtx.copyFrom(boxMtx);
            mtx.append(camera.cameraMtx);
            if (isFilter){
                filter.setRenderToTexture();
            }
            context3D.clear();
            context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtx, true);
            for (var i:int = 0; i < numBuffers; i++){
                context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, constInitials[i]);
                context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 6, constGT);
                context3D.drawTriangles(iBuffer);
            }
            if (isFilter){
                context3D.setVertexBufferAt(0, null, 0, Context3DVertexBufferFormat.FLOAT_2);
                context3D.setVertexBufferAt(1, null, 0, Context3DVertexBufferFormat.FLOAT_3);
                context3D.setVertexBufferAt(2, null, 0, Context3DVertexBufferFormat.FLOAT_2);
                context3D.setVertexBufferAt(3, null, 0, Context3DVertexBufferFormat.FLOAT_3);
                filter.applyFilter();
                context3D.setProgram(particleProgram);
                context3D.setTextureAt(0, texture);
                context3D.setVertexBufferAt(0, vBufferVXYZ, 0, Context3DVertexBufferFormat.FLOAT_3);
                context3D.setVertexBufferAt(1, vBufferUV, 0, Context3DVertexBufferFormat.FLOAT_2);
                context3D.setVertexBufferAt(2, vBufferOffset, 0, Context3DVertexBufferFormat.FLOAT_2);
                context3D.setVertexBufferAt(3, vBufferRGB, 0, Context3DVertexBufferFormat.FLOAT_3);
            }
            context3D.present();
        }

        private function createShader():void {
            var agalAssembler:AGALMiniAssembler = new AGALMiniAssembler();
            ////Particle
            var code:String = "";
            code += "mov vt0, vc4\n"; //vt0=(0,0,0,1)
            code += "mul vt1.xyz, va0.xyz, vc6.w\n"; //vt1.xyz=vt
            code += "add vt0.xyz, vt0.xyz, vt1.xyz\n"; //vt0.xyz=0+vt
            code += "div vt0.xyz, vt0.xyz, vc6.z\n"; //vt0.xyz=x/D...(4.12 or 3.12)
            code += "frc vt1.xyz, vt0.xyz\n"; //vt1.xyz=frc(x/D)...(0.12)
            code += "sub vt0.xyz, vt0.xyz, vt1.xyz\n"; //vt0.xyz=int(x/D)...(4 or 3)
            code += "mul vt0.xyz, vt0.xyz, vc6.x\n"; //vt0.xyz*=pi...(4pi or 3pi)
            code += "cos vt0.xyz, vt0.xyz\n"; //vt0.xyz=cos(vt0.xyz)...(1 or -1)
            code += "sge vt2.xyz, vc6.y, vt0.xyz\n"; //vt2.xyz=(0>=vt0.xyz)?1:0...(0 or 1)
            code += "mul vt0.xyz, vt1.xyz, vt0.xyz\n"; //vt0.xyz=frc(x/D)*(1 or -1)...(0.12 or -0.12)
            code += "add vt0.xyz, vt0.xyz, vt2.xyz\n"; //vt0.xyz+=(0 or 1)...(0.12 or 0.78)
            code += "mul vt0.xyz, vt0.xyz, vc6.z\n"; //vt0.xyz*=D
            //
            code += "m44 vt0, vt0, vc0\n";
            code += "add vt0, vt0, va2\n";
            code += "mov op, vt0\n";
            code += "mov v0, va1\n";
            code += "mov v1, va3\n";
            var vertexShader:ByteArray = agalAssembler.assemble(Context3DProgramType.VERTEX, code);
            code = "";
            code += "tex ft0, v0, fs0 <2d,linear,nomip>\n";
            code += "mul ft0, ft0, v1\n";
            code += "mov oc, ft0\n";
            var fragmentShader:ByteArray = agalAssembler.assemble(Context3DProgramType.FRAGMENT, code);
            particleProgram = context3D.createProgram();
            particleProgram.upload(vertexShader, fragmentShader);
            context3D.setProgram(particleProgram);
        }

        private function createBuffer():void {
            ////Particle
            var numVertices:uint = NUM_PARTICLES * 4;
            var numIndices:uint = NUM_PARTICLES * 6;
            //hs,rgb
            var index:uint = 0;
            var index2:uint = 0;
            var index3:uint = 0;
            var pos:Vector.<Number> = new Vector.<Number>(numVertices * 3);
            var uv:Vector.<Number> = new Vector.<Number>(numVertices * 2);
            var rgb:Vector.<Number> = new Vector.<Number>(numVertices * 3);
            var color:ColorHSV = new ColorHSV();
            var h:Number;
            var s:Number;
            var v:Number;
            var hsv:ColorHSV = new ColorHSV();
            var r:Number;
            var g:Number;
            var b:Number;
            var vx:Number;
            var vy:Number;
            var vz:Number;
            var radius:Number;
            var theta:Number;
            var phi:Number;
            vBufferVXYZ = context3D.createVertexBuffer(numVertices, 3);
            vBufferUV = context3D.createVertexBuffer(numVertices, 2);
            vBufferRGB = context3D.createVertexBuffer(numVertices, 3);
            for (var i:int = 0; i < NUM_PARTICLES; i++){
                radius = (1 + 0.2 * Math.random()) * 5;
                theta = Math.random() * 360 * RAD;
                phi = Math.random() * 180 * RAD;
                vy = radius * Math.cos(phi);
                vx = radius * Math.sin(phi) * Math.cos(theta);
                vz = radius * Math.sin(phi) * Math.sin(theta);
                hsv.hsv(h, s, v);
                pos[index++] = vx;
                pos[index++] = vy;
                pos[index++] = vz;
                pos[index++] = vx;
                pos[index++] = vy;
                pos[index++] = vz;
                pos[index++] = vx;
                pos[index++] = vy;
                pos[index++] = vz;
                pos[index++] = vx;
                pos[index++] = vy;
                pos[index++] = vz;
                //
                uv[index2++] = 0;
                uv[index2++] = 1;
                uv[index2++] = 0;
                uv[index2++] = 0;
                uv[index2++] = 1;
                uv[index2++] = 1;
                uv[index2++] = 1;
                uv[index2++] = 0;
                //
                h = Math.random() * 360;
                v = Math.sqrt(Math.random());
                do {
                    s = Math.random();
                } while (v < s);
                hsv.hsv(h, s, v);
                r = hsv.r / 255.0;
                g = hsv.g / 255.0;
                b = hsv.b / 255.0;
                rgb[index3++] = r;
                rgb[index3++] = g;
                rgb[index3++] = b;
                rgb[index3++] = r;
                rgb[index3++] = g;
                rgb[index3++] = b;
                rgb[index3++] = r;
                rgb[index3++] = g;
                rgb[index3++] = b;
                rgb[index3++] = r;
                rgb[index3++] = g;
                rgb[index3++] = b;
            }
            vBufferVXYZ.uploadFromVector(pos, 0, numVertices);
            vBufferUV.uploadFromVector(uv, 0, numVertices);
            vBufferRGB.uploadFromVector(rgb, 0, numVertices);
            context3D.setVertexBufferAt(0, vBufferVXYZ, 0, Context3DVertexBufferFormat.FLOAT_3);
            context3D.setVertexBufferAt(1, vBufferUV, 0, Context3DVertexBufferFormat.FLOAT_2);
            context3D.setVertexBufferAt(3, vBufferRGB, 0, Context3DVertexBufferFormat.FLOAT_3);
            //offset
            vBufferOffset = context3D.createVertexBuffer(numVertices, 2);
            var offsetVec:Vector.<Number> = new Vector.<Number>(numVertices * 2);
            index = 0;
            for (i = 0; i < NUM_PARTICLES; i++){
                offsetVec[index++] = -P_SIZE;
                offsetVec[index++] = -P_SIZE;
                offsetVec[index++] = -P_SIZE;
                offsetVec[index++] = P_SIZE;
                offsetVec[index++] = P_SIZE;
                offsetVec[index++] = -P_SIZE;
                offsetVec[index++] = P_SIZE;
                offsetVec[index++] = P_SIZE;
            }
            vBufferOffset.uploadFromVector(offsetVec, 0, numVertices);
            context3D.setVertexBufferAt(2, vBufferOffset, 0, Context3DVertexBufferFormat.FLOAT_2);
            //index buffer
            iBuffer = context3D.createIndexBuffer(numIndices);
            var iVec:Vector.<uint> = new Vector.<uint>(numIndices);
            index = 0;
            var p:uint;
            for (i = 0; i < NUM_PARTICLES; i++){
                p = i << 2;
                iVec[index++] = p;
                iVec[index++] = p + 1;
                iVec[index++] = p + 2;
                iVec[index++] = p + 2;
                iVec[index++] = p + 1;
                iVec[index++] = p + 3;
            }
            iBuffer.uploadFromVector(iVec, 0, numIndices);
        }

    }
}

import com.adobe.utils.AGALMiniAssembler;
import com.adobe.utils.PerspectiveMatrix3D;
import flash.display.InteractiveObject;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DTextureFormat;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.textures.Texture;
import flash.display3D.VertexBuffer3D;
import flash.events.KeyboardEvent;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.ui.Keyboard;
import flash.utils.ByteArray;

//////////////////////////////////////////////////
// SimpleCamera3D
//////////////////////////////////////////////////
class SimpleCamera3D extends Object {

    private const RAD:Number = Math.random() / 180;
    private const DIRECTION:Vector3D = new Vector3D(0, 0, 1);
    //
    private var _cameraUP:Vector3D = new Vector3D(0, 1, 0);
    private var _direction:Vector3D = DIRECTION.clone();
    //
    private var _projectionMtx:PerspectiveMatrix3D = new PerspectiveMatrix3D();
    private var _cameraMtx:Matrix3D = new Matrix3D();
    private var _rotationMtx:Matrix3D = new Matrix3D();
    //
    public var x:Number = 0;
    public var y:Number = 0;
    public var z:Number = 0;

    public function SimpleCamera3D(fov:Number, aspect:Number, zNear:Number, zFar:Number):void {
        _projectionMtx.perspectiveFieldOfViewLH(fov, aspect, zNear, zFar);
    }

    public function get cameraMtx():Matrix3D {
        var forwardX:Number = _direction.x;
        var forwardY:Number = _direction.y;
        var forwardZ:Number = _direction.z;
        var rightX:Number = _cameraUP.y * forwardZ - _cameraUP.z * forwardY;
        var rightY:Number = _cameraUP.z * forwardX - _cameraUP.x * forwardZ;
        var rightZ:Number = _cameraUP.x * forwardY - _cameraUP.y * forwardX;
        var distance:Number = Math.sqrt(rightX * rightX + rightY * rightY + rightZ * rightZ);
        rightX /= distance;
        rightY /= distance;
        rightZ /= distance;
        var upX:Number = forwardY * rightZ - forwardZ * rightY;
        var upY:Number = forwardZ * rightX - forwardX * rightZ;
        var upZ:Number = forwardX * rightY - forwardY * rightX;
        var right:Number = rightX * x + rightY * y + rightZ * z;
        var up:Number = upX * x + upY * y + upZ * z;
        var forward:Number = forwardX * x + forwardY * y + forwardZ * z;
        _cameraMtx.rawData = Vector.<Number>([rightX, upX, forwardX, 0, rightY, upY, forwardY, 0, rightZ, upZ, forwardZ, 0, -right, -up, -forward, 1]);
        _cameraMtx.append(_projectionMtx);
        return _cameraMtx;
    }

    public function lookAt(point:Vector3D):void {
        _direction.x = point.x - x;
        _direction.y = point.y - y;
        _direction.z = point.z - z;
        _direction.normalize();
    }

}

//////////////////////////////////////////////////
// RoundCameraController
//////////////////////////////////////////////////
class RoundCameraController extends Object {

    private var _camera:SimpleCamera3D;
    private var _stage:InteractiveObject;
    private var _target:Vector3D;
    public var radiusOffset:Number = 40;
    //
    private const RAD:Number = Math.PI / 180;
    //
    public var isMouseDown:Boolean;
    private var _radius:Number = 2000;
    private var _theta:Number = 0;
    private var _oldX:Number = 0;
    private var _phi:Number = 90;
    private var _oldY:Number = 0;

    public function RoundCameraController(camera:SimpleCamera3D, stage:InteractiveObject){
        _camera = camera;
        _stage = stage;
        _target = new Vector3D();
        enable();
        _upDateCamera();
    }

    public function setTarget(target:Vector3D):void {
        _target = target;
    }

    public function enable():void {
        _stage.stage.addEventListener(KeyboardEvent.KEY_DOWN, _keyHandler);
        _stage.stage.addEventListener(MouseEvent.MOUSE_UP, _upHandler);
        _stage.addEventListener(MouseEvent.MOUSE_DOWN, _downHandler);
        _stage.addEventListener(MouseEvent.MOUSE_MOVE, _moveHandler);
        _stage.addEventListener(MouseEvent.MOUSE_WHEEL, _wheelHandler);
    }

    //
    private function _keyHandler(e:KeyboardEvent):void {
        switch (e.keyCode){
            case Keyboard.UP:
                _radius -= radiusOffset;
                if (_radius < 10){
                    _radius = 10;
                }
                _upDateCamera();
                break;
            case Keyboard.DOWN:
                _radius += radiusOffset;
                _upDateCamera();
                break;
            default:
                break;
        }
    }

    private function _upHandler(e:MouseEvent):void {
        isMouseDown = false;
    }

    private function _downHandler(e:MouseEvent):void {
        isMouseDown = true;
        _oldX = _stage.mouseX;
        _oldY = _stage.mouseY;
    }

    private function _wheelHandler(e:MouseEvent):void {
        if (e.delta > 0){
            _radius -= radiusOffset;
            if (_radius < 10){
                _radius = 10;
            }
        } else {
            _radius += radiusOffset;
        }
        _upDateCamera();
    }

    private function _moveHandler(e:MouseEvent):void {
        if (isMouseDown){
            _theta += (e.stageX - _oldX) >> 2;
            _oldX = e.stageX;
            _phi -= (e.stageY - _oldY) >> 2;
            _oldY = e.stageY;
            //
            if (_theta < 0){
                _theta += 360;
            } else if (_theta > 360){
                _theta -= 360;
            }
            if (_phi < 20){
                _phi = 20;
            } else if (_phi > 160){
                _phi = 160;
            }
            _upDateCamera();
        }
    }

    private function _upDateCamera():void {
        var t:Number = _theta * RAD;
        var p:Number = _phi * RAD;
        var rsin:Number = _radius * Math.sin(p);
        _camera.x = rsin * Math.sin(t) + _target.x;
        _camera.z = rsin * Math.cos(t) + _target.z;
        _camera.y = _radius * Math.cos(p) + _target.y;
        _camera.lookAt(_target)
    }

}

//////////////////////////////////////////////////
// TwinkleFilter3D
//////////////////////////////////////////////////
class TwinkleFilter3D extends Object {
    private var _context3D:Context3D;
    private var _width:Number;
    private var _height:Number;
    //twinkle
    private var _canvasTexture:Texture;
    private var _shrinkTexture:Texture;
    private var _vertexBuffer:VertexBuffer3D;
    private var _indexBuffer:IndexBuffer3D;
    private var _shrinkProgram:Program3D;
    private var _compositeProgram:Program3D;

    public function TwinkleFilter3D(context3D:Context3D, width:uint, height:uint, twinkleStrength:uint = 4){
        _context3D = context3D;
        _width = _toPowerOfTwo(width);
        _height = _toPowerOfTwo(height);
        twinkleStrength = twinkleStrength < 2 ? 2 : twinkleStrength;
        var sW:uint = _width >> twinkleStrength;
        var sH:uint = _height >> twinkleStrength;
        sW = sW < 1 ? 1 : sW;
        sH = sH < 1 ? 1 : sH;
        ////texture
        _canvasTexture = _context3D.createTexture(_width, _height, Context3DTextureFormat.BGRA, true);
        _shrinkTexture = _context3D.createTexture(sW, sH, Context3DTextureFormat.BGRA, true);
        ////buffer
        //vertex buffer
        _vertexBuffer = _context3D.createVertexBuffer(4, 4);
        _vertexBuffer.uploadFromVector(Vector.<Number>([-1, -1, 0, 1, -1, 1, 0, 0, 1, -1, 1, 1, 1, 1, 1, 0]), 0, 4);
        //index buffer
        _indexBuffer = _context3D.createIndexBuffer(6);
        _indexBuffer.uploadFromVector(Vector.<uint>([0, 1, 2, 1, 3, 2]), 0, 6);
        ////shader
        //shrink
        var agal:AGALMiniAssembler = new AGALMiniAssembler();
        var vertexShader:ByteArray = agal.assemble(Context3DProgramType.VERTEX, "mov op, va0 \n" + "mov v0, va1\n");
        var code:String = "";
        code += "mov ft0 v0\n";
        code += "tex ft0, ft0, fs0<2d,repeat,linear>\n";
        code += "mov oc, ft0\n";
        var fragmentShader:ByteArray = agal.assemble(Context3DProgramType.FRAGMENT, code);
        _shrinkProgram = _context3D.createProgram();
        _shrinkProgram.upload(vertexShader, fragmentShader);
        //composite
        code = "";
        code += "mov ft0 v0\n";
        code += "tex ft1, ft0, fs0<2d,repeat,linear>\n";
        code += "tex ft0, ft0, fs1<2d,repeat,linear>\n";
        code += "add ft0, ft0, ft1\n";
        code += "mov oc, ft0\n";
        fragmentShader = agal.assemble(Context3DProgramType.FRAGMENT, code);
        _compositeProgram = _context3D.createProgram();
        _compositeProgram.upload(vertexShader, fragmentShader);
    }

    public function setRenderToTexture():void {
        _context3D.setRenderToTexture(_canvasTexture, true);
    }

    public function applyFilter():void {
        _context3D.setVertexBufferAt(0, _vertexBuffer, 0, Context3DVertexBufferFormat.FLOAT_2);
        _context3D.setVertexBufferAt(1, _vertexBuffer, 2, Context3DVertexBufferFormat.FLOAT_2);
        //shrink
        _context3D.setProgram(_shrinkProgram);
        _context3D.setRenderToTexture(_shrinkTexture);
        _context3D.clear();
        _context3D.setTextureAt(0, _canvasTexture);
        _context3D.drawTriangles(_indexBuffer);
        //composite
        _context3D.setRenderToBackBuffer();
        _context3D.setProgram(_compositeProgram);
        _context3D.setTextureAt(0, _canvasTexture);
        _context3D.setTextureAt(1, _shrinkTexture);
        _context3D.clear();
        _context3D.drawTriangles(_indexBuffer);
        //
        _context3D.setTextureAt(0, null);
        _context3D.setTextureAt(1, null);
        _context3D.setVertexBufferAt(0, null, 0, Context3DVertexBufferFormat.FLOAT_2);
        _context3D.setVertexBufferAt(1, null, 2, Context3DVertexBufferFormat.FLOAT_2);
    }

    //
    private function _toPowerOfTwo(x:uint):uint {
        if ((x & (x - 1))){
            var i:uint = 1;
            while (i < x){
                i <<= 1;
            }
            x = i;
        }
        return x;
    }

}