3. OpenGL GraphicsThe Android platform supports OpenGL graphics in roughly the
same way that a silk hat supports rabbits. Although this is certainly
among the most exciting technologies in Android, it is definitely at the
edge of the map. It also appears that just before the final beta
release, the interface underwent major changes. Much of the code and
many of the suggestions found on the Web are obsolete and no longer
work. The API V1_r2 release is an implementation of OpenGL ES 1.0 and
much of ES 1.1. It is, essentially, a domain-specific language embedded
in Java. Someone who has been doing gaming UIs for a while is likely to
be much more comfortable developing Android OpenGL programs than a Java
programmer, even a programmer who is a Java UI expert. Before discussing the OpenGL graphics library itself, we should
take a minute to consider exactly how pixels drawn with OpenGL appear on
the display. OpenGL is a language in
which an application describes an entire scene that will be rendered by
an engine that is not only outside the JVM, but possibly running on
another processor altogether (the Graphics Processing Unit, or GPU).
Coordinating the two processors’ views of the screen is tricky. The SurfaceView, discussed
earlier, is nearly the right thing. Its purpose is to create a surface
on which a thread other than the UI graphics thread can draw. The tool
we’d like is an extension of SurfaceView that has a bit more support for
concurrency, combined with support for OpenGL. It turns out that there is exactly such a tool. All of the demo
applications in the Android SDK distribution that do OpenGL animation
depend on the utility class GLSurfaceView. Since the demo applications
written by the creators of Android use this class, considering it for
other applications seems advisable. GLSurfaceView defines an
interface, GLSurfaceView.Renderer,
which dramatically simplifies the otherwise overwhelming complexity of
using OpenGL and GLSurfaceView.
GLSurfaceView calls the renderer
method getConfigSpec to get its
OpenGL configuration information. Two other methods, sizeChanged and surfaceCreated, are called by the GLSurfaceView to inform the renderer that its
size has changed or that it should prepare to draw, respectively.
Finally, drawFrame, the heart of the
interface, is called to render a new OpenGL frame. Example 7 shows the
important methods from the implementation of an OpenGL renderer. Example 7. Frame-by-frame animation with OpenGL// ... some state set up in the constructor
@Override public void surfaceCreated(GL10 gl) { // set up the surface gl.glDisable(GL10.GL_DITHER);
gl.glHint( GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_FASTEST);
gl.glClearColor(0.4f, 0.2f, 0.2f, 0.5f); gl.glShadeModel(GL10.GL_SMOOTH); gl.glEnable(GL10.GL_DEPTH_TEST);
// fetch the checker-board initImage(gl); }
@Override public void drawFrame(GL10 gl) { gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity();
GLU.gluLookAt(gl, 0, 0, -5, 0f, 0f, 0f, 0f, 1.0f, 0.0f);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
// apply the checker-board to the shape gl.glActiveTexture(GL10.GL_TEXTURE0);
gl.glTexEnvx( GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_MODULATE); gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); gl.glTexParameterx( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT);
// animation int t = (int) (SystemClock.uptimeMillis() % (10 * 1000L)); gl.glTranslatef(6.0f - (0.0013f * t), 0, 0);
// draw gl.glFrontFace(GL10.GL_CCW); gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuf); gl.glEnable(GL10.GL_TEXTURE_2D); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuf); gl.glDrawElements( GL10.GL_TRIANGLE_STRIP, 5, GL10.GL_UNSIGNED_SHORT, indexBuf); }
private void initImage(GL10 gl) { int[] textures = new int[1]; gl.glGenTextures(1, textures, 0); gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[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_LINEAR); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE); gl.glTexParameterf( GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE); gl.glTexEnvf( GL10.GL_TEXTURE_ENV, GL10.GL_TEXTURE_ENV_MODE, GL10.GL_REPLACE);
InputStream in = context.getResources().openRawResource(R.drawable.cb); Bitmap image; try { image = BitmapFactory.decodeStream(in); } finally { try { in.close(); } catch(IOException e) { } }
GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, image, 0);
image.recycle(); }
|
The surfaceCreated method
prepares the scene. It sets several OpenGL attributes that need to be
initialized only when the widget gets a new drawing surface. In
addition, it calls initImage, which
reads in a bitmap resource and stores it as a 2D texture. Finally, when
drawFrame is called, everything is
ready for drawing. The texture is applied to a plane whose vertices were
set up in vertexBuf by the
constructor, the animation phase is chosen, and the scene is
redrawn.
|