Stage3D 2D Particle Test Optimized
http://wonderfl.net/c/eTBH
の座標計算をvertexシェーダに任せたバージョン
速度はダンチ
汎用性はゼロ
/**
* Copyright 9balls ( http://wonderfl.net/user/9balls )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/by41
*/
package {
import com.adobe.utils.AGALMiniAssembler;
import com.bit101.components.NumericStepper;
import com.bit101.components.PushButton;
import flash.display.Sprite;
import flash.display.Stage3D;
import flash.display.StageAlign;
import flash.display.StageScaleMode;
import flash.display3D.Context3D;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DRenderMode;
import flash.display3D.Context3DTriangleFace;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.Program3D;
import flash.display3D.VertexBuffer3D;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.text.TextField;
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 NUM_BUFFERS_MAX:uint = 300;
private const WIDTH:uint = 466;
private const HEIGHT:uint = 466;
private const SIZE:Number = 0.5;
//
private var stage3D:Stage3D;
private var context3D:Context3D;
//Particle
private var programP:Program3D;
private var vBufferVXY:VertexBuffer3D;
private var vBufferRGB:VertexBuffer3D;
private var vBufferOffset:VertexBuffer3D;
private var iBuffer:IndexBuffer3D;
private var t:int = 0;
private var initPosConsts:Vector.<Vector.<Number>>;
private var radConst:Vector.<Number>;
//
private var numBuffers:uint = 1;
private var tfR:TextField;
private var tfP:TextField;
public function Main():void {
Wonderfl.disable_capture();
stage.frameRate = 60;
stage.align = StageAlign.TOP_LEFT;
stage.scaleMode = StageScaleMode.NO_SCALE;
//set ui
addChild(new Stats());
var tfB:TextField = new TextField();
tfB.textColor = 0xffffff;
tfB.text = "Buffers : ";
tfB.x = 100;
addChild(tfB);
var stepper:NumericStepper = new NumericStepper(this, 150, 0, onStepper);
stepper.minimum = 1;
stepper.maximum = NUM_BUFFERS_MAX;
stepper.width = 60;
tfP = new TextField();
tfP.textColor = 0xffffff;
tfP.text = "Particle : " + NUM_PARTICLES;
tfP.x = 250;
addChild(tfP);
tfR = new TextField();
tfR.textColor = 0xffffff;
tfR.x = 400;
addChild(tfR);
new PushButton(this, 350, 0, "Reset", onReset);
//
stage3D = stage.stage3Ds[0];
stage3D.x = 0;
stage3D.y = 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 onReset(e:MouseEvent):void {
t = -100;
for (var i:int = 0; i < NUM_BUFFERS_MAX; i++){
initPosConsts[i] = Vector.<Number>([Math.random() * WIDTH, Math.random() * HEIGHT, 0, 1]);
}
}
private function onContextCreate(e:Event):void {
context3D = stage3D.context3D;
//context3D.enableErrorChecking = true;
//
createShaders();
setConstant();
setBuffer();
//
context3D.configureBackBuffer(WIDTH, HEIGHT, 0, false);
context3D.setCulling(Context3DTriangleFace.BACK);
context3D.setVertexBufferAt(0, vBufferVXY, 0, Context3DVertexBufferFormat.FLOAT_2);
context3D.setVertexBufferAt(1, vBufferRGB, 0, Context3DVertexBufferFormat.FLOAT_3);
context3D.setVertexBufferAt(2, vBufferOffset, 0, Context3DVertexBufferFormat.FLOAT_2);
context3D.setProgram(programP);
//
stage.addEventListener(Event.ENTER_FRAME, onEnterFrame);
}
private function onEnterFrame(e:Event):void {
context3D.clear(0, 0, 0, 1);
radConst[3] = ++t;
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, radConst);
for (var i:int = 0; i < numBuffers; i++){
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, initPosConsts[i]);
context3D.drawTriangles(iBuffer);
}
context3D.present();
}
private function createShaders():void {
var agalAssembler:AGALMiniAssembler = new AGALMiniAssembler();
//Particle
var code:String = "";
code += "mov vt0, vc4\n";
code += "mul vt1.xy, va0.xy, vc5.w\n";
code += "add vt0.xy, vt0.xy, vt1.xy\n";
code += "div vt0.xy, vt0.xy, vc5.z\n"; //vt0.xyz=x/D...(4.12 or 3.12)
code += "frc vt1.xy, vt0.xy\n"; //vt1.xyz=frc(x/D)...(0.12)
code += "sub vt0.xy, vt0.xy, vt1.xy\n"; //vt0.xyz=int(x/D)...(4 or 3)
code += "mul vt0.xy, vt0.xy, vc5.x\n"; //vt0.xyz*=pi...(4pi or 3pi)
code += "cos vt0.xy, vt0.xy\n"; //vt0.xyz=cos(vt0.xyz)...(1 or -1)
code += "sge vt2.xy, vc5.y, vt0.xy\n"; //vt2.xyz=(0>=vt0.xyz)?1:0...(0 or 1)
code += "mul vt0.xy, vt1.xy, vt0.xy\n"; //vt0.xyz=frc(x/D)*(1 or -1)...(0.12 or -0.12)
code += "add vt0.xy, vt0.xy, vt2.xy\n"; //vt0.xyz+=(0 or 1)...(0.12 or 0.78)
code += "mul vt0.xy, vt0.xy, vc5.z\n"; //vt0.xyz*=D
//
code += "add vt0, vt0, va2\n";
code += "m44 vt0, vt0, vc0\n";
code += "mov op, vt0\n";
code += "mov v0, va1\n";
var vertexShader:ByteArray = agalAssembler.assemble(Context3DProgramType.VERTEX, code);
var fragmentShader:ByteArray = agalAssembler.assemble(Context3DProgramType.FRAGMENT, "mov oc, v0\n");
programP = context3D.createProgram();
programP.upload(vertexShader, fragmentShader);
}
private function setConstant():void {
var mtxP:Matrix3D = new Matrix3D();
mtxP.appendScale(4 / WIDTH, -4 / HEIGHT, 1);
mtxP.appendTranslation(-1, 1, 0);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, mtxP, true);
//
initPosConsts = new Vector.<Vector.<Number>>(NUM_BUFFERS_MAX);
initPosConsts[0] = Vector.<Number>([WIDTH / 2, HEIGHT / 2, 0, 1]);
for (var i:int = 1; i < NUM_BUFFERS_MAX; i++){
initPosConsts[i] = Vector.<Number>([Math.random() * WIDTH, Math.random() * HEIGHT, 0, 1]);
}
radConst = Vector.<Number>([Math.PI, -1, WIDTH, 0]);
}
private function setBuffer():void {
//Particle
var numVertices:uint = NUM_PARTICLES * 4;
var numIndices:uint = NUM_PARTICLES * 6;
//
vBufferVXY = context3D.createVertexBuffer(numVertices, 2);
vBufferRGB = context3D.createVertexBuffer(numVertices, 3);
var cVec:Vector.<Number> = new Vector.<Number>(numVertices * 3);
var vVec:Vector.<Number> = new Vector.<Number>(numVertices * 2);
var radius:Number;
var theta:Number;
var velX:Number;
var velY:Number;
var color:ColorHSV = new ColorHSV();
var r:Number;
var g:Number;
var b:Number;
var index:uint = 0;
var index2:uint = 0;
for (var i:int = 0; i < NUM_PARTICLES; i++){
radius = Math.random() * 2 + 1;
theta = 2 * Math.PI * Math.random();
velX = radius * Math.cos(theta);
velY = radius * Math.sin(theta);
vVec[index++] = velX;
vVec[index++] = velY;
vVec[index++] = velX;
vVec[index++] = velY;
vVec[index++] = velX;
vVec[index++] = velY;
vVec[index++] = velX;
vVec[index++] = velY;
//
color.value = 0x1000000 * Math.random();
r = color.r / 255.0;
g = color.g / 255.0;
b = color.b / 255.0;
cVec[index2++] = r;
cVec[index2++] = g;
cVec[index2++] = b;
cVec[index2++] = r;
cVec[index2++] = g;
cVec[index2++] = b;
cVec[index2++] = r;
cVec[index2++] = g;
cVec[index2++] = b;
cVec[index2++] = r;
cVec[index2++] = g;
cVec[index2++] = b;
}
vBufferVXY.uploadFromVector(vVec, 0, numVertices);
vBufferRGB.uploadFromVector(cVec, 0, numVertices);
//
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++] = -SIZE;
offsetVec[index++] = -SIZE;
offsetVec[index++] = -SIZE;
offsetVec[index++] = SIZE;
offsetVec[index++] = SIZE;
offsetVec[index++] = -SIZE;
offsetVec[index++] = SIZE;
offsetVec[index++] = SIZE;
}
vBufferOffset.uploadFromVector(offsetVec, 0, numVertices);
//
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 + 2;
iVec[index++] = p + 1;
iVec[index++] = p + 1;
iVec[index++] = p + 2;
iVec[index++] = p + 3;
}
iBuffer.uploadFromVector(iVec, 0, numIndices);
}
}
}