2012年11月15日 星期四

Stage3D: 2D Particle Random Motion

2D particle random motion for Stage3D,
Here is a demo with 5000 particles.



code:
package
{
import com.adobe.utils.AGALMiniAssembler;
import flash.display3D.Context3DBlendFactor;
import flash.display3D.Context3DProgramType;
import flash.display3D.Context3DVertexBufferFormat;
import flash.display3D.IndexBuffer3D;
import flash.display3D.VertexBuffer3D;
import flash.events.MouseEvent;
import flash.geom.Matrix3D;
import flash.geom.Vector3D;
import flash.utils.ByteArray;
import flash.utils.getTimer;
import net.hires.debug.Stats;
/**
* stage3D: 5000 particles random motion
* @author flashisobar
*/
public class Particle2D extends Base3D
{
static public const NUM_PARTICLES:uint = 5000; // max: 16383
private var vertexParticle:VertexBuffer3D;
private var indexParticle:IndexBuffer3D;
private var isStart:Boolean = true;
private var particles:Vector.<Number> = new Vector.<Number>(NUM_PARTICLES*4);
private var vertexShader:ByteArray;
private var fragmentShader:ByteArray;
private var matrix3d:Matrix3D;
private var w:int = 400;
private var h:int = 400;
private var size:Number = 4;
private var transformConstants:Vector.<Number>;
private var toPosition:Vector.<Number>;
public function Particle2D()
{
}
override protected function main():void
{
this.addChild(new Stats);
// set vertex/index data
/*
* batch and easy create particle
* disadvantage: sloooowly
*/
/*
var vertices:Vector.<Number> = new Vector.<Number>();
var index:Vector.<uint> = new Vector.<uint>();
for (var i:int = 0; i < NUM_PARTICLES; i++){
vertices = vertices.concat(createParticle((Math.random() - 0.5) * w * 2, (Math.random() - 0.5) * h * 2, Math.random() * 0xFFFFFF));
var s:uint = 4 * i;
index = index.concat(Vector.<uint>([s, s + 1, s + 2, s + 2, s + 3, s]));
}
*/
var vertices:Vector.<Number> = new Vector.<Number>();
var index:Vector.<uint> = new Vector.<uint>();
var r:Number, g:Number, b:Number, color:Number;
var s:uint;
var xPos:Number, yPos:Number;
var idx:uint = 0;
var stime:int = getTimer();
for (var i:int = 0; i < NUM_PARTICLES; i++) {
xPos = (Math.random() - 0.5) * w * 2;
yPos = (Math.random() - 0.5) * h * 2;
color = Math.random() * 0xFFFFFF;
r = (color >> 16) / 255;
g = (color >> 8 & 0xFF) / 255;
b = (color & 0xFF) / 255;
vertices.push(xPos - size, yPos + size, r, g, b);
vertices.push(xPos + size, yPos + size, r, g, b);
vertices.push(xPos + size, yPos - size, r, g, b);
vertices.push(xPos - size, yPos - size, r, g, b);
s = 4 * i;
index.push(s, s + 1, s + 2, s + 2, s + 3, s);
}
vertexParticle = setVertexData(vertices, 5); // 一次上傳一個頂點含有五個 Number(x,y,r,g,b) 資料
indexParticle = setIndexData(index);
trace("Time:" + (getTimer() - stime));
// set register
setVertexBuffer(0, vertexParticle, 0, Context3DVertexBufferFormat.FLOAT_2); // register0: va0(xy)
setVertexBuffer(1, vertexParticle, 2, Context3DVertexBufferFormat.FLOAT_3); // register1: va1(color)
// set constants for setProgramConstantsFromMatrix
transformConstants = new <Number>[1, 1, 1, 1];
toPosition = new <Number>[100, 100, 1000, 1];
var len:int = NUM_PARTICLES * 4;
for (i = 0; i < len; i) {
particles[i++] = -300 + 600 * Math.random();
particles[i++] = -300 + 600 * Math.random();
particles[i++] = 1000 + 3000 * Math.random();
particles[i++] = 1;
}
// AGAL code
var assembler:AGALMiniAssembler = new AGALMiniAssembler();
var code:String = '';
code += "mov vt0, va0\n"; // get vertex position
code += "mov vt1.x vc4.z\n"; // vt1.x = getTimer()/1000;
code += "frc vt1.x vt1.x\n"; // vt1.x = get value from 0 to 1
code += "mul vt1.xy vc5.xy vt1.xx\n"; // vt1.xy = vc5.xy * vt1.xx;
code += "add vt0.xy vt0.xy vt1.xy\n"; // move: vt0.xy = vt0.xy + vt1.xy
code += "m44 op, vt0, vc0\n"; // m44: vt0 * vc0
code += "mov v0, va1\n";
vertexShader = assembler.assemble(Context3DProgramType.VERTEX, code);
code = "mov oc, v0"; // output color: v0
fragmentShader = assembler.assemble(Context3DProgramType.FRAGMENT, code);
setShaders(vertexShader, fragmentShader);
// set projection view or scale
matrix3d = new Matrix3D();
//matrix3d.appendRotation(45, Vector3D.Z_AXIS);
matrix3d.appendScale(1 / w, 1 / h, 1);
/*
* The Matrix3D object you want to upload into constant registers.
* Matrix3Ds are always 4×4 matrices so four registers are needed to store their constants.
* vc0, vc1, vc2, vc3
*/
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix3d, true); // vc0,vc1,vc2,vc3
stage.addEventListener(MouseEvent.CLICK, createParticles);
}
private function createParticles(e:MouseEvent = null):void
{
isStart = !isStart;
}
/*
-0.5, 0.5, 0, 0, 1, // x,y,r,g,b
0.5, 0.5, 0, 0, 1,
0.5, -0.5, 0, 0, 1,
-0.5, -0.5, 0, 0, 1
*/
private function createParticle(xPos:Number, yPos:Number, color:uint):Vector.<Number>
{
var r:Number = (color >> 16) / 255;
var g:Number = (color >> 8 & 0xFF) / 255;
var b:Number = (color & 0xFF) / 255;
return Vector.<Number>([xPos - size, yPos + size, r, g, b, xPos + size, yPos + size, r, g, b, xPos + size, yPos - size, r, g, b, xPos - size, yPos - size, r, g, b]);
}
override protected function draw():void
{
if (!isStart)
{
return;
}
context3D.clear(.4);
/*
matrix3d = new Matrix3D();
matrix3d.appendRotation(getTimer() / 10, Vector3D.Z_AXIS);
matrix3d.appendScale(1 / w, 1 / h, 1);
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix3d); // vc0
*/
//transformConstants[3] = Math.random() * 2 - 1; // w:(-1~1)
//context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, particles); // vc5
var g:Number = getTimer();
for (var i:int = 0; i < NUM_PARTICLES * 4; i = i + 4) {
// z
transformConstants[2] = g / particles[i + 2];
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 4, transformConstants); // vc4
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, new <Number>[particles[i], particles[i+1], particles[i+2], particles[i+3]]); // vc5
// 從 0 開始,畫兩個三角形
context3D.drawTriangles(indexParticle, 6 * i/4, 2);
}
/*
for (var i:int = 0; i < NUM_PARTICLES; i++) {
context3D.setProgramConstantsFromVector(Context3DProgramType.VERTEX, 5, toPosition); // vc5
context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix3d); // vc0
context3D.drawTriangles(indexParticle, 6 * i, 2); // 從 0 開始,畫兩個三角形
}
*/
//context3D.drawTriangles(indexParticle); // draw all index buffer
}
}
}
view raw Particle2D.as hosted with ❤ by GitHub

AGAL code:
code += "mov vt0, va0\n";                      // get vertex position
code += "mov vt1.x vc4.z\n";  // vt1.x = getTimer()/1000;
code += "frc vt1.x vt1.x\n";  // vt1.x = get value from 0 to 1
code += "mul vt1.xy vc5.xy vt1.xx\n";    // vt1.xy = vc5.xy * vt1.xx;
code += "add vt0.xy vt0.xy vt1.xy\n";     // move: vt0.xy = vt0.xy + vt1.xy
code += "m44 op, vt0, vc0\n";               // m44: vt0 * vc0
code += "mov v0, va1\n";

createParticle: 方便批次產生正方形粒子,但產生海量資料時會拖慢速度,改用另外作法。
setProgramConstantsFromMatrix: Matrix3Ds are always 4×4 matrices so four registers are needed to store their constants.
ie: context3D.setProgramConstantsFromMatrix(Context3DProgramType.VERTEX, 0, matrix3d, true);
這樣會佔掉 vc0, vc1, vc2, vc3
drawTriangles: draw from indexBuffer, firstIndex, numTriangles
ie: context3D.drawTriangles(indexParticle, 6, 2);
索引緩衝區中取資料,從第六筆資料開始,繪製兩個三角形

TODO:
- Reduce draw calls.
- Due to the VertexBuffer and IndexBuffer limit(around 16383 particles per draw call).

NUM_PARTICLES 超過 16383 會噴 error
[Fault] exception, information=ArgumentError: Error #3670: Buffer too big

沒有留言:

張貼留言