MobileJava MobileOpenGL ES for Android Graphics Programming

OpenGL ES for Android Graphics Programming

Developer.com content and product recommendations are editorially independent. We may make money when you click on links to our partners. Learn More.

Android offers support for 2D and 3D graphics with the OpenGL ES API. OpenGL ES is just a variation of OpenGL specifically designed for embedded systems. Since Android 1.0, the OpenGL ES 1.0 and 1.1 APIs have been included. The support for OpenGL ES 2.0 started from Android 2.2. This tutorial presents the basics for working on OpenGL ES software in Android by working through a few examples. You can download the entire source code for all the demo projects in the examples.

Android OpenGL References, Versions and Device Support

The Android OpenGL ES reference has version descriptions and comparisons among OpenGL ES 1.0/1.1 and 2.0. Since these versions differ in many ways, you should make your selection based on the performance, device compatibility, coding convenience, and graphics control for your implementation.

To find out which OpenGL ES versions your current device supports, you can use the following code:

String GL_RENDERER = gl.glGetString(GL10.GL_RENDERER);
String GL_VERSION = gl.glGetString(GL10.GL_VERSION);
ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);
String GL_ES_VERSION = am.getDeviceConfigurationInfo().getGlEsVersion();

Android Classes for OpenGL GL

The two most important classes you need for OpenGL projects in Android are android.opengl.GLSurfaceView and android.opengl.GLSurfaceView.Renderer. You also must implement Renderer, an interface for the minimal required drawing methods, as follows:

public void onSurfaceCreated(GL10 gl, EGLConfig config) 

public void onDrawFrame(GL10 gl)

public void onSurfaceChanged(GL10 gl, int width, int height)

GLSurfaceView is a view class which you use to draw the graphics objects. You will also need it to capture user interaction events. Now you can instantiate this view class and call setRenderer with the renderer implementation you must provide.

OpenGL Geometric Elements, Rendering Primitives and Colors

Before we move on to our first example, there are some brief descriptions that should help you understand the code better if you are an OpenGL beginner.

  • Geometric elements include points, lines, areas, and so on. For points, OpenGL uses gl.glVertexPointer to refer to the buffer for vertices contained in an array with X, Y, Z values. For lines, they are formed by connecting the indices from the vertex array. For areas, they are constructed by connecting oriented triangles. You can specify the vertex order used for the triangles with gl.glFrontFace(GL10.GL_CCW) for counter clockwise orientation or gl.glFrontFace(GL10.GL_CW) for clockwise.
  • Rendering primitives include GL_POINTS for points. GL_LINES, GL_LINE_STRIP, and GL_LINE_LOOP are available for lines. For triangles, the options are GL_TRIANGLES, GL_TRIANGLE_STRIP, and GL_TRIANGLE_FAN. These can be rendered through gl.glDrawElements with their byte buffers provided.
  • Basic coloring in Open GL contains four float values ranging from 0.0 to 1.0, representing Red, Green, Blue, and Alpha (Transparency) channels through gl.glColorf. For more complex coloring, we can create an array of the four float values and use gl.glColorPointer to refer to its byte buffer.

Build Your First Basic OpenGL ES Project in Android

As mentioned before, two essential classes required by all OpenGL ES projects are android.opengl.GLSurfaceView and android.opengl.GLSurfaceView.Renderer. Therefore, the main activity containing the minimal implementation is as follows:

public class TutorialOnOpenGL extends Activity {
private GLSurfaceView mView;
private MyRenderer mRenderer;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mView = new GLSurfaceView(this);
mRenderer = new MyRenderer(this);
mView.setRenderer(mRenderer);
setContentView(mView);
}

public boolean onTouchEvent(MotionEvent event) {
return mRenderer.onTouchEvent(event);
}
}

We will implement a Renderer interface with our own MyRenderer below. Because onSurfaceCreated, onSurfaceChanged and onTouchEvent will pretty much remain the same in other implementations, they will not be relisted later. This example basically has a list of five vertices representing a pyramid with vertex indices of 0, 1, 2, 3 at the base and 4 at the top.In setAllBuffers, each vertex component is a float value. Since a float value equals 4 bytes, byte buffer length needs to be multiplied by 4. Similarly, the border index is a short value and thus byte buffer length needs to be multiplied by 2.

Border color is set to solid green. The main code to render all the lines is:

gl.glDrawElements(GL10.GL_LINES, mNumOfTriangleBorderIndices, 
GL10.GL_UNSIGNED_SHORT, mTriangleBorderIndicesBuffer);

Figure 1 shows the result, a wireframe pyramid.

class MyRenderer implements Renderer { 
private Context mContext;
private FloatBuffer mVertexBuffer = null;
private ShortBuffer mTriangleBorderIndicesBuffer = null;
private int mNumOfTriangleBorderIndices = 0;

public float mAngleX = 0.0f;
public float mAngleY = 0.0f;
public float mAngleZ = 0.0f;
private float mPreviousX;
private float mPreviousY;
private final float TOUCH_SCALE_FACTOR = 0.6f;

public MyRenderer(Context context) {
mContext = context;
}

public void onDrawFrame(GL10 gl) {
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW);
gl.glLoadIdentity();

gl.glTranslatef(0.0f, 0.0f, -3.0f);
gl.glRotatef(mAngleX, 1, 0, 0);
gl.glRotatef(mAngleY, 0, 1, 0);
gl.glRotatef(mAngleZ, 0, 0, 1);

gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mVertexBuffer);

// Set line color to green gl.glColor4f(0.0f, 1.0f, 0.0f, 1.0f);

// Draw all lines
gl.glDrawElements(GL10.GL_LINES, mNumOfTriangleBorderIndices,
GL10.GL_UNSIGNED_SHORT, mTriangleBorderIndicesBuffer);
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
gl.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST);
gl.glEnable(GL10.GL_DEPTH_TEST);

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);

// Get all the buffers ready
setAllBuffers();
}

public void onSurfaceChanged(GL10 gl, int width, int height) {
gl.glViewport(0, 0, width, height);
float aspect = (float)width / height;
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glFrustumf(-aspect, aspect, -1.0f, 1.0f, 1.0f, 10.0f);
}

private void setAllBuffers(){
// Set vertex buffer
float vertexlist[] = {
-1.0f, 0.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, 0.0f, 1.0f,
1.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f,
};
ByteBuffer vbb = ByteBuffer.allocateDirect(vertexlist.length * 4);
vbb.order(ByteOrder.nativeOrder());
mVertexBuffer = vbb.asFloatBuffer();
mVertexBuffer.put(vertexlist);
mVertexBuffer.position(0);

// Set triangle border buffer with vertex indices
short trigborderindexlist[] = {
4, 0, 4, 1, 4, 2, 4, 3, 0, 1, 1, 3, 3, 2, 2, 0, 0, 3
};
mNumOfTriangleBorderIndices = trigborderindexlist.length;
ByteBuffer tbibb = ByteBuffer.allocateDirect(trigborderindexlist.length * 2);
tbibb.order(ByteOrder.nativeOrder());
mTriangleBorderIndicesBuffer = tbibb.asShortBuffer();
mTriangleBorderIndicesBuffer.put(trigborderindexlist);
mTriangleBorderIndicesBuffer.position(0);
}

public boolean onTouchEvent(MotionEvent e) {
float x = e.getX();
float y = e.getY();
switch (e.getAction()) {
case MotionEvent.ACTION_MOVE:
float dx = x - mPreviousX;
float dy = y - mPreviousY;
mAngleY = (mAngleY + (int)(dx * TOUCH_SCALE_FACTOR) + 360) % 360;
mAngleX = (mAngleX + (int)(dy * TOUCH_SCALE_FACTOR) + 360) % 360;
break;
}
mPreviousX = x;
mPreviousY = y;
return true;
}
}

Android OpenGL

Figure 1: Wireframe Pyramid

Rendering Triangles with Colors in an Android OpenGL Project

The example on the previous page illustrates most of the basic pieces used in an OpenGL ES project. Let us modify the code further so that it will treat each face as a triangle instead of just drawing border lines. Each triangle contains 3 indices from vertex array. We then construct the mTriangleIndexBuffer byte buffer. This time we also introduce the color array for multiple colors assigned to all vertices. The color byte array is stored in mColorBuffer. While rendering these triangles, we use the GL10.GL_TRIANGLES primitive.

class MyRenderer implements Renderer { 
public void onDrawFrame(GL10 gl) {
...

// Draw all triangles
gl.glColorPointer(4, GL10.GL_FLOAT, 0, mColorBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, mNumOfTriangleIndices,
GL10.GL_UNSIGNED_SHORT, mTriangleIndexBuffer);
}

private void setAllBuffers(){
...

// Set triangle buffer with vertex indices
short trigindexlist[] = {
1, 3, 0, 2, 0, 3, 4, 0, 2, 4, 2, 3, 4, 3, 1, 4, 1, 0
};
mNumOfTriangleIndices = trigindexlist.length;
ByteBuffer ibb = ByteBuffer.allocateDirect(trigindexlist.length * 2);
ibb.order(ByteOrder.nativeOrder());
mTriangleIndexBuffer = ibb.asShortBuffer();
mTriangleIndexBuffer.put(trigindexlist);
mTriangleIndexBuffer.position(0);

// Set triangle color buffer
float trigcolorlist[] = {
1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f,
0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,
};
ByteBuffer cbb = ByteBuffer.allocateDirect(trigcolorlist.length * 4);
cbb.order(ByteOrder.nativeOrder());
mColorBuffer = cbb.asFloatBuffer();
mColorBuffer.put(trigcolorlist);
mColorBuffer.position(0);
}
}

Figure 2 shows the result.

Android OpenGL

Figure 2: Colored Triangles

Mapping Textures in an Android OpenGL Project

We have made the object more interesting by rendering triangles with colors. Another popular implementation is to map bitmap textures onto geometric objects. Surface vertices need to be mapped onto the texture space. We have our pyramid base composed of 2 triangles as in Figure 3.

Android OpenGL

Figure 3: Texture Coordinates

The green text represents the 2D texture space (U, V) while the white text represents the vertex indices. We also need to generate the texture byte buffer as in mTextureBuffer. Additionally, we need to generate the texture object (gl.glGenTextures), bind it (gl.glBindTexture), and refer it to a 2-D bitmap (GLUtils.texImage2D). The result is shown in Figure 4.

class MyRenderer3 implements Renderer { 
private FloatBuffer mTextureBuffer = null;
private int [] mTextureList = null;

public void onDrawFrame(GL10 gl) {
...

// Draw all textured triangles
gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, mTextureBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, mNumOfTriangleIndices,
GL10.GL_UNSIGNED_SHORT, mTriangleIndexBuffer);
}

public void onSurfaceCreated(GL10 gl, EGLConfig config) {
...

// Set texture-related stuff
Bitmap b = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.t);
gl.glEnable(GL10.GL_TEXTURE_2D);

mTextureList = new int[1];
gl.glGenTextures(1, mTextureList, 0);
gl.glBindTexture(GL10.GL_TEXTURE_2D, mTextureList[0]);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_LINEAR);
gl.glTexEnvf(GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE);
gl.glClientActiveTexture(GL10.GL_TEXTURE0);
gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, b, 0);
b.recycle();

// Get all the buffers ready
setAllBuffers();
}

private void setAllBuffers(){
...

// Set texture buffer
float texturecoords[] = {0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.5f, 0.5f};
ByteBuffer tbb = ByteBuffer.allocateDirect(texturecoords.length * 4);
tbb.order(ByteOrder.nativeOrder());
mTextureBuffer = tbb.asFloatBuffer();
mTextureBuffer.put(texturecoords);
mTextureBuffer.position(0);
}
}

Android OpenGL

Figure 4: Textured Pyramid

Conclusion

This tutorial is intended to introduce the basics you need to start developing OpenGL ES applications in Android. OpenGL itself is a comprehensive graphics library used broadly in commercial software in various platforms, so there is no way I could cover all its details in a short tutorial like this. But here are some helpful pointers:

  • For performance consideration, you can use GLSurfaceView.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
  • You can easily add gesture control in Android to manipulate objects for better user interaction.
  • For more OpenGL tutorials, I found these references very useful:

References

  1. You can get yourself started in Android by finding the comprehensive suite of Android development tools, plug-ins, sample projects, and documentation at Google’s site for developers.
  2. The entire OpenGL project for Android — Download and import into Eclipse by selecting “Creating project from existing source.”
  3. Android OpenGL
  4. Android Developers
  5. Androidlet

About the Author

Chunyen Liu has been a software professional for many years. Some of his applications were among winners at programming contests administered by Sun, ACM, and IBM. He has co-authored software patents, written 25+ articles, reviewed books, and also created numerous hobby apps at Androidlet and The J Maker. He holds advanced degrees in Computer Science with knowledge from 20+ graduate-level courses. On the non-technical side, he is a tournament-ranked table tennis player, certified umpire, and certified coach of USA Table Tennis.

Get the Free Newsletter!

Subscribe to Developer Insider for top news, trends & analysis

Latest Posts

Related Stories