一、Java代码
package com.gzdxid.particles;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import android.graphics.Color;
import android.opengl.GLES20;
public class ParticleSystem {
private int mProgram;
private int muMVPMatrixHandle;
private int muTimeHandle;
private int maPositionHandle;
private int maColorHandle;
private int maDirectionVectorHandle;
private int maParticleStartTimeHandle;
private static final int BYTES_PER_FLOAT = 4;
private static final int POSITION_COMPONENT_COUNT = 3;
private static final int COLOR_COMPONENT_COUNT = 3;
private static final int VECTOR_COMPONENT_COUNT = 3;
private static final int PARTICLE_START_TIME_COMPONENT_COUNT = 1;
private static final int TOTAL_COMPONENT_COUNT = POSITION_COMPONENT_COUNT + COLOR_COMPONENT_COUNT + VECTOR_COMPONENT_COUNT + PARTICLE_START_TIME_COMPONENT_COUNT;
private static final int STRIDE = TOTAL_COMPONENT_COUNT * BYTES_PER_FLOAT;
private int maxParticleCount;
private float[] particles;
private FloatBuffer mVertexArrayBuffer;
private int nextParticle=0;
private int currentParticleCount;
private int[] vboID;
public ParticleSystem(int maxParticleCount,int mProgram) {
initVertex(maxParticleCount);
initShader(mProgram);
}
private void initVertex(int maxParticleCount) {
this.maxParticleCount=maxParticleCount;
particles=new float[maxParticleCount*TOTAL_COMPONENT_COUNT];
vboID=new int[1];
ByteBuffer vbb=ByteBuffer.allocateDirect(maxParticleCount*TOTAL_COMPONENT_COUNT*4);
vbb.order(ByteOrder.nativeOrder());
mVertexArrayBuffer=vbb.asFloatBuffer();
}
private void initShader(int mProgram) {
this.mProgram=mProgram;
muMVPMatrixHandle=GLES20.glGetUniformLocation(mProgram, "uMVPMatrix");
muTimeHandle=GLES20.glGetUniformLocation(mProgram, "uTime");
maPositionHandle=GLES20.glGetAttribLocation(mProgram, "aPosition");
maColorHandle=GLES20.glGetAttribLocation(mProgram, "aColor");
maDirectionVectorHandle=GLES20.glGetAttribLocation(mProgram, "aDirectionVector");
maParticleStartTimeHandle=GLES20.glGetAttribLocation(mProgram, "aParticleStartTime");
}
public void addParticle(Point3 position,int color,Vector3 direction,float particleStartTime){
int particleOffset=nextParticle*TOTAL_COMPONENT_COUNT;
int currentOffset=particleOffset;
nextParticle++;
if(currentParticleCount<maxParticleCount){
currentParticleCount++;
}
if(nextParticle==maxParticleCount){
nextParticle=0;
}
particles[currentOffset++]=position.Px;
particles[currentOffset++]=position.Py;
particles[currentOffset++]=position.Pz;
particles[currentOffset++]=Color.red(color)/255f;
particles[currentOffset++]=Color.green(color)/255f;
particles[currentOffset++]=Color.blue(color)/255f;
particles[currentOffset++]=direction.Vx;
particles[currentOffset++]=direction.Vy;
particles[currentOffset++]=direction.Vz;
particles[currentOffset++]=particleStartTime;
mVertexArrayBuffer.position(particleOffset);
mVertexArrayBuffer.put(particles, particleOffset, TOTAL_COMPONENT_COUNT);
mVertexArrayBuffer.position(0);
}
public void drawSelf(float[] viewProjectionMatrix,float elapsedTime){
int dataOffset=0;
GLES20.glUseProgram(mProgram);
GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, viewProjectionMatrix, 0);
GLES20.glUniform1f(muTimeHandle, elapsedTime);
GLES20.glGenBuffers(1, vboID,0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID[0]);
GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mVertexArrayBuffer.capacity()*BYTES_PER_FLOAT, mVertexArrayBuffer, GLES20.GL_DYNAMIC_DRAW);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID[0]);
GLES20.glEnableVertexAttribArray(maPositionHandle);
GLES20.glVertexAttribPointer(maPositionHandle, POSITION_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, dataOffset);
dataOffset+=POSITION_COMPONENT_COUNT*BYTES_PER_FLOAT;
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID[0]);
GLES20.glEnableVertexAttribArray(maColorHandle);
GLES20.glVertexAttribPointer(maColorHandle, COLOR_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, dataOffset);
dataOffset+=COLOR_COMPONENT_COUNT*BYTES_PER_FLOAT;
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID[0]);
GLES20.glEnableVertexAttribArray(maDirectionVectorHandle);
GLES20.glVertexAttribPointer(maDirectionVectorHandle, VECTOR_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, dataOffset);
dataOffset+=VECTOR_COMPONENT_COUNT*BYTES_PER_FLOAT;
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vboID[0]);
GLES20.glEnableVertexAttribArray(maParticleStartTimeHandle);
GLES20.glVertexAttribPointer(maParticleStartTimeHandle, PARTICLE_START_TIME_COMPONENT_COUNT, GLES20.GL_FLOAT, false, STRIDE, dataOffset);
GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, currentParticleCount);
GLES20.glDeleteBuffers(1, vboID,0);
GLES20.glDisableVertexAttribArray(maPositionHandle);
GLES20.glDisableVertexAttribArray(maColorHandle);
GLES20.glDisableVertexAttribArray(maDirectionVectorHandle);
GLES20.glDisableVertexAttribArray(maParticleStartTimeHandle);
}
}
package com.gzdxid.particles;
import java.util.Random;
import android.opengl.Matrix;
public class ParticleShooter {
private Point3 position;
private int color;
private float angleVariance;
private float speedVariance;
private float[] directionVector=new float[4];
private final Random random=new Random();
private float[] resultVector=new float[4];
private float[] rotationMatrix=new float[16];
public ParticleShooter(Point3 position,Vector3 direction,int color,float angleVarianceInDegrees,float speedVariance) {
this.position=position;
this.color=color;
this.speedVariance=speedVariance;
this.angleVariance=angleVarianceInDegrees;
directionVector[0]=direction.Vx;
directionVector[1]=direction.Vy;
directionVector[2]=direction.Vz;
directionVector[3]=0;
}
public void toParticleSystem(ParticleSystem particleSystem,float currentTime,int count){
for(int i=0;i<count;i++){
Matrix.setRotateEulerM(
rotationMatrix,
0,
(random.nextFloat()-0.5f)*angleVariance,
(random.nextFloat()-0.5f)*angleVariance,
(random.nextFloat()-0.5f)*angleVariance);
Matrix.multiplyMV(resultVector, 0, rotationMatrix, 0, directionVector, 0);
float speedAdjustment=1f+random.nextFloat()*speedVariance;
Vector3 thisDirection=new Vector3(
resultVector[0]*speedAdjustment,
resultVector[1]*speedAdjustment,
resultVector[2]*speedAdjustment);
particleSystem.addParticle(position, color, thisDirection, currentTime);
}
}
}
package com.gzdxid.particles;
public class Vector3 {
protected float Vx;
protected float Vy;
protected float Vz;
public Vector3(float x,float y,float z) {
Vx=x;
Vy=y;
Vz=z;
}
}
package com.gzdxid.particles;
public class Point3 {
protected float Px;
protected float Py;
protected float Pz;
public Point3(float x,float y,float z) {
Px=x;
Py=y;
Pz=z;
}
}
二、顶点着色器:
uniform mat4 uMVPMatrix;
uniform float uTime;
attribute vec3 aPosition;
attribute vec3 aColor;
attribute vec3 aDirectionVector;
attribute float aParticleStartTime;
varying vec3 vColor;
varying float vElapsedTime;
void main()
{
vColor=aColor;
vElapsedTime=uTime-aParticleStartTime;
vec3 currentPosition=aPosition+(aDirectionVector*vElapsedTime);
float gravityFactor=vElapsedTime*vElapsedTime/10.0;
currentPosition.y-=gravityFactor;
gl_Position=uMVPMatrix*vec4(currentPosition,1.0);
gl_PointSize=25.0;
}
三、片源着色器:
precision mediump float;
varying vec3 vColor;
varying float vElapsedTime;
void main()
{
float xDistance=0.5-gl_PointCoord.x;
float yDistance=0.5-gl_PointCoord.y;
float distanceFromCenter=sqrt(xDistance*xDistance+yDistance*yDistance);
if(distanceFromCenter>0.5){
discard;
}else{
gl_FragColor=vec4(vColor/vElapsedTime,1.0);
}
}
四、使用方法:
public void initParticle(){
final Point3 position=new Point3(0, 0, 0);
final Vector3 particleDirection=new Vector3(0f, 0.5f, 0);
final float angleVarianceInDegrees=10f;
final float speedVariance=1f;
globalStartTime=System.nanoTime();
particleSystem=new ParticleSystem(10000, ShaderManager.getParticleColorShaderProgram());
particleShooter=new ParticleShooter(position, particleDirection, Color.rgb(255, 50, 5), angleVarianceInDegrees, speedVariance);
grainShooter=new GrainShooter(2, 8000, ShaderManager.getGrainColorShaderProgram());
}
private void drawParticle(){
float currentTime=(System.nanoTime()-globalStartTime)/1000000000f;
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE);
MatrixState.pushMatrix();
MatrixState.translate(-0.2f, -0.6f, 0);
particleShooter.toParticleSystem(particleSystem, currentTime, 5);
particleSystem.drawSelf(MatrixState.getFinalMatrix(),currentTime);
MatrixState.popMatrix();
GLES20.glDisable(GLES20.GL_BLEND);
GLES20.glEnable(GLES20.GL_DEPTH_TEST);
GLES20.glEnable(GLES20.GL_CULL_FACE);
MatrixState.pushMatrix();
MatrixState.rotate(-180, 1, 0, 0);
MatrixState.translate(-0.2f, 0.2f, 0);
grainShooter.drawSelf();
MatrixState.popMatrix();
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
GLES20.glDisable(GLES20.GL_CULL_FACE);
}