Before diving deep into OpenGL complex graphics concepts, in this article, we set up some basic stuff, write simple classes, and make those run on some emulator or actual android device if you have. But, the important aspect of writing a simple example is to understand the basics of OpenGL usage, why we are using some of the classes provided in API, etc. I feel, unless one understands purpose behind existence of these simple things, it would be difficult to appreciate the complex concepts, which are built on these simple concepts. As readers of this article have reached OpenGL, the environment setup and basic concept of Android application are already known, hence not getting into those. To start with, we use API version 1.0, though we have 2.0 available. New things and changes in recent API are discussed wherever applicable.
Let us get going. Now you have already created an Android project in (Android) Eclipse following simple self-explanatory steps. In this Open GL ES application, we need three simple classes – Activity (MyOpenGLSampleActivity.java), View (MyOpenGLSurfaceView.java), Renderer (MyOpenGLSurfaceRenderer.java). What is responsibility of each class is discussed in detail below.
Activity
Any Android application would need an activity. Core class that extends from android.app.Activity class, and implements onCreate() method. (We have been doing this in our non OpenGL apps also.) In this method, we need to set the content view, i.e. the actual contents that are going to be displayed when activity runs. Here the first change comes; the content view is implementation of android.opengl.GLSurfaceView instead of R.layout.main. We have implemented it in MyOpenGLSurfaceView.java, hence, inside onCreate() method, set instance of it. This means we have provided view to our activity.
Next change is related to possibility of two additional events in case of Graphics activity. The activity can be paused and resumed, which we don’t need to worry in case of non-graphics activities sometimes. We override onPause and onResume methods and perform respective actions on the view. Note, these operations getting performed on parent class (super class) before the view class. This can give idea of sequence of execution, and how the activity can be controlled from this class. The view will pause/resume renderer to show the effect in the end.
If we want to do any complex view operations, such as on pause, the view needs to show some time clock, or some message, that can be controlled from here.
package com.mysite.openglsamples;
import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;
public class MyOpenGLSamplesActivity extends Activity {
private GLSurfaceView glview;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
glview = new MyOpenGLSurfaceView(this);
setContentView(glview);
}
@Override
protected void onPause() {
super.onPause();
glview.onPause();
}
@Override
protected void onResume() {
super.onResume();
glview.onResume();
}
}
View
This is the implementation of android.opengl.GLSurfaceView class. Here we have two important steps. First, telling the client config version to the config chooser. This is an important setting, as it decides whether our program is compatible to the end device or not, sometimes whether the graphical output is how it is supposed to be. If the end device is Open GL ES 2.0 enabled, we specify 2 as input parameter to setEGLContextClientVersion, otherwise let it be 1, which is older one and most of the devices are supporting.
In the next line, we set renderer for this view. It is instance of our custom renderer MyOpenGLSurfaceRenderer.java. The renderer will do surface rendering job for us. Here instead of just setting renderer inside constructor, alternatively we can start a different thread in start() method of this class. Bit complex stuff, the renderer will keep running in different thread, and this class and activity class will have to do inter thread communication with handle of renderer. We end up in this scenario because, the UI and this activity have to be separate, then only user events on UI to control view can be handled. Suppose a key down even occurs on UI, this event can be captured here using onKeyDown method, in which queueEvent(Runnable) can be used pass on this event to the renderer thread to do the actual change. If you feel it complex, leave it for now and look at simple code below.
package com.mysite.openglsamples;
import android.content.Context;
import android.opengl.GLSurfaceView;
public class MyOpenGLSurfaceView extends GLSurfaceView {
public MyOpenGLSurfaceView(Context context){
super(context);
setEGLContextClientVersion(1);
setRenderer(new MyOpenGLSurfaceRenderer());
}
}
Renderer
This class is responsible to draw the surface. It is registered with view. In surface rendering or drawing business, we have three behaviors possible – onDrawFrame, onSurfaceChanged, and onSurfaceCreated.
onDrawFrame: This is called to draw current frame. If we want to change picture frame by frame, we call this method repeatedly with some logic, it will paint the frames with changes in picture and motion graphics will occurs. There are other ways to achieve motion, we leave it for later. In our code, we are using GLES10 class that provides numerous implementations to manage surface rendering in OpenGL ES version 1.0. Similar class is available in later version.
OnSurfaceChanged: It gets called when surface size changes.
OnSurfaceCreated: It gets called when surface gets created/recreated. First time, when rendering starts, the surface gets created. Recreation occurs when due to some reasons (Android device sleep/wakeup) the context is lost. In these cases if we want to create some resources those can be created here.
package com.mysite.openglsamples;
import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES10;
import android.opengl.GLSurfaceView.Renderer;
public class MyOpenGLSurfaceRenderer implements Renderer {
@Override
public void onDrawFrame(GL10 arg0) {
GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT |
GLES10.GL_DEPTH_BUFFER_BIT);
}
@Override
public void onSurfaceChanged(GL10 arg0, int arg1, int arg2) {
}
@Override
public void onSurfaceCreated(GL10 arg0, EGLConfig arg1) {
GLES10.glClearColor(0.5f, 0.5f, 0.5f, 1.0f);
}
}
Let us limit discussion to understand and see what is outcome of our program on emulator. Instead of black background, we have grey.
