Here is a demo with 5000 particles.
code:
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
} | |
} | |
} |
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
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
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
沒有留言:
張貼留言