Improving performance of a particle system (OpenGL ES)
- by Jason
I'm in the process of implementing a simple particle system for a 2D mobile game (using OpenGL ES 2.0). It's working, but it's pretty slow. I start getting frame rate battering after about 400 particles, which I think is pretty low.
Here's a summary of my approach:
I start with point sprites (GL_POINTS) rendered in a batch just using a native float buffer (I'm in Java-land on Android, so that translates as a java.nio.FloatBuffer).
On GL context init, the following are set:
GLES20.glViewport(0, 0, width, height);
GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
GLES20.glEnable(GLES20.GL_CULL_FACE);
GLES20.glDisable(GLES20.GL_DEPTH_TEST);
Each draw frame sets the following:
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_ONE, GLES20.GL_ONE_MINUS_SRC_ALPHA);
And I bind a single texture:
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle);
GLES20.glUniform1i(mUniformTextureHandle, 0);
Which is just a simple circle with some blur (and hence some transparency)
http://cl.ly/image/0K2V2p2L1H2x
Then there are a bunch of glVertexAttribPointer calls:
mBuffer.position(position);
mGlEs20.glVertexAttribPointer(mAttributeRGBHandle, valsPerRGB, GLES20.GL_FLOAT, false, stride, mBuffer);
...4 more of these
Then I'm drawing:
GLES20.glUniformMatrix4fv(mUniformProjectionMatrixHandle, 1, false, Camera.mProjectionMatrix, 0);
GLES20.glDrawArrays(GLES20.GL_POINTS, 0, drawCalls);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
My vertex shader does have some computation in it, but given that they're point sprites (with only 2 coordinate values) I'm not sure this is the problem:
#ifdef GL_ES
// Set the default precision to low.
precision lowp float;
#endif
uniform mat4 u_ProjectionMatrix;
attribute vec4 a_Position;
attribute float a_PointSize;
attribute vec3 a_RGB;
attribute float a_Alpha;
attribute float a_Burn;
varying vec4 v_Color;
void main()
{
vec3 v_FGC = a_RGB * a_Alpha;
v_Color = vec4(v_FGC.x, v_FGC.y, v_FGC.z, a_Alpha * (1.0 - a_Burn));
gl_PointSize = a_PointSize;
gl_Position = u_ProjectionMatrix * a_Position;
}
My fragment shader couldn't really be simpler:
#ifdef GL_ES
// Set the default precision to low.
precision lowp float;
#endif
uniform sampler2D u_Texture;
varying vec4 v_Color;
void main() {
gl_FragColor = texture2D(u_Texture, gl_PointCoord) * v_Color;
}
That's about it. I had read that transparent pixels in point sprites can cause issues, but surely not at only 400 points?
I'm running on a fairly new device (12 month old Galaxy Nexus).
My question is less about my approach (although I'm open to suggestion) but more about whether there are any specific OpenGL "no no's" that have leaked into my code.
I'm sure there's GL master out there facepalming right now... I'd love to hear any critique.