Android OpenGL ES Tutorial, Part 1: Rendering a colored triangle to the screen

What is OpenGL ES?

If you are familiar with OpenGL, then the ES variety is simply a stripped down version of OpenGL 1.3 with a few developer friendly features added. If you have never used OpenGL before, then just think of OpenGL ES as your triangle rendering ally in your quest to develop awesome games and applications.

By reading this tutorial, I am assuming that:

  1. You are familiar with Java (syntax, object-orientated paradigm, etc.). If you have no programming experience and would like to learn, there are plenty of resources freely available on the internet as well as books that you purchase in analog and digital formats.
  2. You are somewhat familiar with Android. If you are not, that is fine too; this tutorial is very thorough.
  3. You have an IDE (preferably Eclipse) installed and configured for Android development. If you do not, read my guide here.
  4. OpenGL ES 1.0 is supported by Android devices 1.6 and above, so I am assuming you have an Android device with a firmware 1.6 or higher. Emulators render with OpenGL very slowly, so I recommend testing OpenGL ES based app and games only on actual devices.
  5. You have an interest in developing graphically demanding applications. Most platforms provide an effective means of drawing and displaying GUI elements, so if that is all you plan on developing, you are better off sticking those native drawing and layout methods (unless you really want to get your feet wet with some graphics programming).


If you are interested in downloading the source code for this project, refer to the next section; otherwise, continue on to "Getting Started".

Click "Finish" and voila!
Click "Finish" and voila!

Source Code

The source code for this tutorial can be downloaded here. You will receive a zip file. Extract the contents of this zip file to your workspace. In Eclipse:

  1. File > Import > General > Existing Projects into Workspace
  2. Select root directory: browse for the "opengl-es-tutorial" folder and click it.
  3. Your screen should look like the one on the right -->




Click thumbnail to view full-size

Getting Started

Okay, let's create an OpenGL ES Android app! Assuming you have Eclipse installed and configured properly for Android development:

  1. File > New > Android Project
  2. Follow the screenshots provided on the right -->
  3. Name your project "openglestutorial"
  4. Set "Android 1.6" as your Build Target
  5. Set the Application Name as "Opengl-es-tutorial"
  6. Set the package name to your website starting with the domain name and followed by the project name. For example, I used: "com.technegames.openglestutorial"
  7. Make sure "Create Activity:" is checked; name it "TriangleActivity"
  8. The Minimum SDK should be set to 4, since OpenGL ES is not supported below that.

AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.technegames.openglestutorial"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk android:minSdkVersion="4" android:targetSdkVersion="4" />

    <application android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:launchMode="singleTask"
        android:theme="@android:style/Theme.NoTitleBar.Fullscreen" >
        
        <activity android:name="com.technegames.openglestutorial.TriangleActivity"
            android:label="@string/app_name" >
            
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        
        </activity>
    
    </application>

</manifest>

About the AndroidManifest

Most of this will be set up for you by Eclipse by default.

I added one important line:

android:theme="@android:style/Theme.NoTitleBar.Fullscreen"

Adding this line will cause any activities in your application to launch in fullscreen mode.

TriangleActivity.java

package com.technegames.openglestutorial;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
import android.util.Log;

public class TriangleActivity extends Activity implements android.opengl.GLSurfaceView.Renderer
{
	private static final String TAG = TriangleActivity.class.getSimpleName();
	
	private static final int Coordinates_In_A_Vertex = 2;
	private static final int Vertices_In_A_Triangle = 3;
	private static final int Bits_In_A_Byte = 8;
	private static final int Bits_In_A_Float = Float.SIZE;
	private static final int Bytes_In_A_Float = Bits_In_A_Float / Bits_In_A_Byte;
	
	private GLSurfaceView glSurfaceView;
	private FloatBuffer vertices;
	private int width;
	private int height;
	
	@Override
	public void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		
		glSurfaceView = new GLSurfaceView(this);
		glSurfaceView.setRenderer(this);
		setContentView(glSurfaceView);
	}
	
	@Override
	public void onResume()
	{
		super.onResume();
		glSurfaceView.onResume();
	}
	
	@Override
	public void onPause()
	{
		glSurfaceView.onPause();
		super.onPause();
	}
	
	@Override
	public void onDrawFrame(GL10 gl)
	{
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
		gl.glColor4f(0.64313725490196f, 0.77647058823529f, 0.22352941176471f, 1);
		gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertices);
		gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height)
	{
		Log.d(TAG, "Surface changed! width = " + String.valueOf(width) + ", height = " + String.valueOf(height));
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config)
	{
		Log.d(TAG, "Surface Created!");
		
		width = glSurfaceView.getWidth();
		height = glSurfaceView.getHeight();
		
		gl.glViewport(0, 0, width, height);
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
		gl.glOrthof(0, 1, 0, 1, 1, -1);
		gl.glClearColor(1, 1, 1, 1);
		
		ByteBuffer byteBuffer = ByteBuffer.allocateDirect(Coordinates_In_A_Vertex * Vertices_In_A_Triangle * Bytes_In_A_Float);
		byteBuffer.order(ByteOrder.nativeOrder());
		
		vertices = byteBuffer.asFloatBuffer();
		
		vertices.put(new float[]
				{ 
					0.0f, 0.0f,
					0.5f, 1.0f,
					1.0f, 0.0f
				}
		);
		
		vertices.flip();
		
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
	}
}

TriangleActivity members

We have 10 class-level fields in total.

  • TAG is used when writing logs
  • Coordinates_In_A_Vertex stores the number of coordinates for a vertex. On a 2D plane, there are only x and y coordinates, so this number is 2.
  • Vertices_In_A_Triangle should be pretty self explanatory.
  • There are 8 bits in a byte, and there are 32 bits in a Float data type (we know this because Float.SIZE returns 32).
  • OpenGL ES requires that we allocate memory in terms of bytes, so we store 4 in the Bytes_In_A_Float variable (More on this below).
  • glSurfaceView is responsible for displaying all of the OpenGL rendering
  • vertices is a FloatBuffer that stores all of the coordinates of our triangle
  • we use width and height to store the dimensions of our GLSurfaceView

TriangleActivity onCreate, onResume, and onPause

You may have noticed that our TriangleActivity implements android.opengl.GLSurfaceView.Renderer. When creating a GLSurfaceView, we can use the activity class itself to supply the renderer via the setRenderer() function.

In onCreate(), we simply instatiate our glSurfaceView, set its renderer, and finally set the content view to the glSurfaceView.

In onResume and onPause, we call the respective glSurfaceView methods. Note that we only resume our glSurfaceView after we call the super implementation of onResume. The opposite is true for onPause. The reason behind this is because our glSurfaceView cannot be alive and rendering unless the activity is in the foreground.

onSurfaceCreated, onSurfaceChanged, and onDrawFrame

In this section, I will explain each line of OpenGL ES code. If this your first time learning OpenGL ES, read and reread this section until everything makes sense.

  • In onSurfaceChanged, we simply log the dimensions of the glSurfaceView whenever it changes.
  • In onSurfaceCreated, we perform the bulk of the initialization work. After setting width and height, we have to initialize our rendering system:
  • glViewport() sets the portion of the screen that we render to.
  • glMatrixMode(GL_PROJECTION) tells OpenGL ES that we are drawing in a 2-dimensional environment.
  • glLoadIdentity() resets the matrix to its original state.
  • glOrthof() defines our own coordinate system. I set the values so that our triangle would be drawn onto a coordinate system that is 1 unit wide and 1 unit tall.
  • glClearColor(r, g, b, a) specifies what color the screen will be cleared with when
    glClear is called.
  • As for the triangle, we must use a ByteBuffer to supply all of the float coordinates. Each of our coordinate pairs are stored as Floats, since this is what OpenGL ES expects.
  • There are 2 Floats in a vertex, 3 vertices in a triangle, and 4 bytes in a Float. This means we have to allocate 24 bytes to our ByteBuffer.
  • After we allocate our bytes, we transform this ByteBuffer to a FloatBuffer and store it in our vertices variable. After this, we place all of our coordinates in our vertices.
  • Once our coordinates are loaded, we call flip() so that our vertices can be read from the beginning by OpenGL ES.
  • Finally, we enable the client state to GL_VERTEX_ARRAY so that OpenGL ES knows we are going to be drawing vertices.
  • onDrawFrame is where your triangle is actually drawn to the screen! Before drawing our triangle, we clear our screen with the color white via the glClear method. We call the glColor4f method so that our triangle will be drawn with the "Android green" color.
  • The glVertexPointer method is used to describe our vertices to OpenGL ES. Our Vertices have 2 coordinates stored as Floats.
  • In the glDrawArrays method, we tell OpenGL ES that we are drawing a triangle (GL10.GL_TRIANGLES), and we are drawing 3 vertices (1 triangle).

All Done!

Congratulations! If you have done everything correctly, your device screen should look like the one on the right -->

I plan on writing more tutorials, so comment below and let me know how I did and what I should write about in my future articles.

More by this Author


Comments 10 comments

dianetrotter profile image

dianetrotter 4 years ago from Fontana

Why would I want to do this? Is it a programming lesson? I'm serious. That's a lot of work to end up with a triangle.


Alucard_1990 profile image

Alucard_1990 4 years ago from Nashville, TN Author

@dianetrotter,

Yes, this example starts with only one triangle. However, with just one line of code we can make this triangle rotate on the screen or grow and shrink in size. In future tutorials, I will demonstrate binding a texture to groups of triangles to render sprites and animations.

Learning OpenGL ES takes time but pays dividends much in the style of an exponential growth curve.

Keep in mind that hardware accelerated graphics is usually unnecessary for most application development and is almost exclusively used in the gaming development world.

Thanks for reading!


Yerst 4 years ago

Hi!

I was searching for a good Tutorial for days, and now i have found it.

Thanks!

Make more! (Rotating, CameraView,...)


Alucard_1990 profile image

Alucard_1990 4 years ago from Nashville, TN Author

@Yerst,

Will do!

=D


h1 3 years ago

hi, java.nio cannot be resolved? how can I add automatically the library


Alucard_1990 profile image

Alucard_1990 3 years ago from Nashville, TN Author

@h1,

The java.nio.* package is automatically included with the android.jar. If your project cannot resolve it, try doing a project clean


Long Bunly profile image

Long Bunly 3 years ago

wow, I must learn OpenGL from you, hope I can make my dream come true. I will try my best to follow you Alucard :)


Alucard_1990 profile image

Alucard_1990 3 years ago from Nashville, TN Author

@Long Bunly,

I have been so tied up working on a game for Android/iOS, but I will definitely resume this OpenGL ES tutorial series asap!


Vishal 3 years ago

It was really awesome to draw a triangle. Could you just tell me how to draw a custom shape over it? It can be any object


Amit 3 years ago

Excellent Tutorial....please help me in Understanding MAP Activities also.

    Sign in or sign up and post using a HubPages Network account.

    0 of 8192 characters used
    Post Comment

    No HTML is allowed in comments, but URLs will be hyperlinked. Comments are not for promoting your articles or other sites.


    Click to Rate This Article
    working