OpenGL ES 2.0
The underlying framework for spacedust is based on what I learned from the book Beginning Android Games, which uses OpenGL ES 1.0. With the help of the book OpenGL ES 2 for Android, I have updated my framework to use OpenGL ES 2.0 instead!
I am in no way an OpenGL expert, so there may be better ways to do some of these things, but I wanted to document the main changes I made to my code.
AndroidManifest.xml
Add the following line to AndroidManifest.xml to require version 2.0.
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
GLSurfaceView
Call the following function on a GLSurfaceView to tell it to use version 2.
GLSurfaceView view;
view.setEGLContextClientVersion(2);
Functions
The following function calls…
GL10 gl;
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glEnable(GL10.GL_TEXTURE_2D);
gl.glEnable(GL10.GL_BLEND);
gl.glBlendFunc(GL10.GL_SRC_ALPHA, GL10.GL_ONE_MINUS_SRC_ALPHA);
gl.glDrawElements(GL10.GL_TRIANGLES, numVertices, GL10.GL_UNSIGNED_SHORT, indices);
gl.glGenTextures(1, ids, 0);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
gl.glDeleteTextures(1, ids, 0);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_NEAREST);
… all have equivalent version 2 functions.
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
GLES20.glEnable(GLES20.GL_TEXTURE_2D);
GLES20.glEnable(GLES20.GL_BLEND);
GLES20.glBlendFunc(GLES20.GL_SRC_ALPHA, GLES20.GL_ONE_MINUS_SRC_ALPHA);
GLES20.glDrawElements(GLES20.GL_TRIANGLES, numVertices, GLES20.GL_UNSIGNED_SHORT, indices);
GLES20.glGenTextures(1, ids, 0);
GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
GLES20.glDeleteTextures(1, ids, 0);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST);
GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST);
glColor4f
One of the main differences I ran into was no longer being able to use the glColor4f function. I mainly used this function to fade textures in and out by changing the alpha value.
GL10 gl;
gl.glColor4f(1, 1, 1, 0.5);
In version 2, shaders are required to accomplish the same functionality.
Viewport & Projection Matrix
With version 1, setting up the viewport and projection matrix looks something like this:
GL10 gl;
gl.glViewport(0, 0, width, height);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, right, bottom, 0, 1, -1);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();
To do something similar in version 2, it looks like this:
float[] projectionMatrix = new float[16];
GLES20.glViewport(0, 0, width, height);
Matrix.orthoM(projectionMatrix, 0, 0, right, bottom, 0, 1, -1);
Shaders
The main difference between version 1 and 2 is shaders. When drawing a vertex, OpenGL uses two shaders. One to control the position (vertex shader) and one to control the color (fragment shader).
A simple vertex shader:
uniform mat4 u_Matrix;
attribute vec4 a_Position;
attribute vec2 a_TextureCoordinates;
varying vec2 v_TextureCoordinates;
void main()
{
v_TextureCoordinates = a_TextureCoordinates;
gl_Position = u_Matrix * a_Position;
}
A simple fragment shader:
precision mediump float;
uniform sampler2D u_TextureUnit;
varying vec2 v_TextureCoordinates;
void main()
{
gl_FragColor = texture2D(u_TextureUnit, v_TextureCoordinates);
}
Setting up the shaders looks something like this:
int vid = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
int fid = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
GLES20.glShaderSource(vid, vertex_shader_source);
GLES20.glShaderSource(fid, fragment_shader_source);
GLES20.glCompileShader(vid);
GLES20.glCompileShader(fid);
int pid = GLES20.glCreateProgram();
GLES20.glAttachShader(pid, vid);
GLES20.glAttachShader(pid, fid);
GLES20.glLinkProgram(pid);
GLES20.glUseProgram(pid);
uMatrixLocation = GLES20.glGetUniformLocation(pid, "u_Matrix");
uTextureUnitLocation = GLES20.glGetUniformLocation(pid, "u_TextureUnit");
aPositionLocation = GLES20.glGetAttribLocation(pid, "a_Position");
aTextureCoordinatesLocation = GLES20.glGetAttribLocation(pid, "a_TextureCoordinates");
Texture Binding
GL10 gl;
gl.glBindTexture(GL10.GL_TEXTURE_2D, id);
When binding a texture, version 2 requires a few more function calls to coordinate the information passed to the shaders.
GLES20.glUniformMatrix4fv(uMatrixLocation, 1, false, projectionMatrix, 0);
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, id);
GLES20.glUniform1i(uTextureUnitLocation, 0);
Vertices
In version 1, function calls tell OpenGL how to find the texture and position information in an array of vertex information.
GL10 gl;
FloatBuffer vertices;
vertices.position(0);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL10.GL_FLOAT, vertexSize, vertices);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
vertices.position(2);
gl.glTexCoordPointer(2, GL10.GL_FLOAT, vertexSize, vertices);
# unbind
gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
In version 2, different functions are called to pass the same kind of information to the shaders instead.
FloatBuffer vertices;
vertices.position(0);
GLES20.glVertexAttribPointer(aPositionLocation, 2, GL_FLOAT, false, vertexSize, vertices);
GLES20.glEnableVertexAttribArray(aPositionLocation);
vertices.position(2);
GLES20.glVertexAttribPointer(aTextureCoordinatesLocation, 2, GL_FLOAT, false, vertexSize,
GLES20.glEnableVertexAttribArray(aTextureCoordinatesLocation);
Conclusion
Converting my code to use OpenGL ES 2.0 went better than I expected. It was complicated but took fewer code changes than I was anticipating.
I am looking forward to what I can do with shaders!