Android includes support for high-performance 2D and 3D graphics with the Open Graphics Library (OpenGL). Specifically, the OpenGL API is a cross-platform graphics API that specifies a standard software interface for 3D graphics processing hardware. OpenGL ES is an adaptation of the OpenGL specification intended for embedded devices.

Android supports several versions of the OpenGL API.

It is an API that specifies how 3D models are made... there is a player looking at the world, everything is composed of triangles, the working matrices are perfectly defined, it is part of Khronos, and it was created for 3D systems.

Khronos is a for-profit foundation; everything is open/free, but they make money through hardware. It was created in the C language, it is stateful/sequential, and works with matrices; it was C without complications because it was intended for machine code. The key aspect is that the algorithm for calculating a surface can be executed either by hardware or, if hardware support isn't implemented, by software.

So if you have a program that needs to handle lighting, keep in mind that hardware is much faster than using software.

There are several versions; these refer to OpenGL for mobile devices, WebGL is used with JavaScript in browsers, and plain OpenGL is for desktop PCs running Windows, Linux, Apple...

The main and most relevant versions of OpenGL are:

  • OpenGL ES 1.0 and 1.1 - API specification supported by Android 1.0 and higher.
  • OpenGL ES 2.0 - API specification supported by Android 2.2 (API level 8), 4.3, and higher.
  • OpenGL ES 3.0 - API specification supported by Android (API level 18) 4.3 and higher.

The specific API provided by Android is similar to the J2ME JSR239 OpenGL ES API, but it is not identical.

There are two fundamental classes in Android that allow you to create and manipulate graphics with the OpenGL ES API:

  • GLSurfaceView.
  • GLSurfaceView.Renderer.

GL.SurfaceView

This class is a View where you can draw and manipulate objects using OpenGL API calls, and it is similar in function to a SurfaceView. You can use this class by creating an instance of GLSurfaceView and adding its Renderer to it. However, if we want to capture touch screen events, we must extend the GLSurfaceView class to implement touch listeners.

GLSurfaceView is a special view that manages OpenGL surfaces for us and draws on Android's view system. It also adds a large number of features that make OpenGL easier to use, including but not limited to:

  • Provides a dedicated rendering thread for OpenGL so it does not affect the main thread.
  • Allows us to draw whenever we want.
  • Takes care of screen setup for us using EGL, the interface between OpenGL and the underlying window system.

Essentially we will have a view—just as we have buttons, images, and TextViews, we will have a GLSurfaceView—everything drawn there will be hardware-accelerated. To use OpenGL, the Java APIs are recommended. OpenGL is specified in C, but since it consists of matrix calculations, there aren't hundreds of lines of code; instead, they are matrix transformations that can be brought to any programming language. You can do it with any programming language.

GLSurfaceView.Renderer

This interface defines the methods needed to draw graphics on a GLSurfaceView. An implementation of this interface must be provided as a separate class and attached to the GLSurfaceView instance using GLSurfaceView.setRenderer().

It requires you to implement the following methods:

  • onSurfaceCreated(): The system calls this method once, upon creating the GLSurfaceView. Use this method to perform actions that only need to happen once, such as configuring OpenGL environment parameters or initializing OpenGL graphics objects.
  • onDrawFrame(): The system calls this method on each redraw of the GLSurfaceView. Use this method as the main execution point to process (and redraw) graphics objects.
  • onSurfaceChanged(): The system calls this method when the GLSurfaceView changes geometry, including changes in the size of the GLSurfaceView or device screen orientation. For example, the system calls this method when the device switches from portrait to landscape. Use this method to respond to changes in the container GLSurfaceView.

If the application uses OpenGL features that are not available on all devices, it must include these requirements in the AndroidManifest.xml file:

<uses-feature android:glEsVersion="0x00020000" android:required="true" /> <uses-feature android:glEsVersion="0x00030000" android:required="true" />

The OpenGL ES 3.0 API is backward compatible with 2.0, which means you can be more flexible with your OpenGL ES implementation in the application. Declaring OpenGL 2.0 is a requirement, and by checking API 3.0 availability at runtime, if supported, it will use it.

Code example:

@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mGLSurfaceView = new GLSurfaceView(this); // supports OpenGL ES 2.0? final ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); final ConfigurationInfo configurationInfo = activityManager.getDeviceConfigurationInfo(); final boolean supportsEs2 = configurationInfo.reqGlEsVersion >= 0x20000; if (supportsEs2){ // compatible context mGLSurfaceView.setEGLContextClientVersion(2); mGLSurfaceView.setRenderer(new NuestroRenderer()); } else{ // Use OpenGL ES 1, for example } // use the whole activity area setContentView(mGLSurfaceView); }

Before our processor displays anything, we need something to show. In OpenGL ES 2, we specify arrays of numbers. These numbers can represent positions, colors, or anything else. In this demo, we will show three triangles.

// Data is stored in float buffers.

private final FloatBuffer mTriangle1Vertices;

private final FloatBuffer mTriangle2Vertices;

private final FloatBuffer mTriangle3Vertices;

// number of bytes per float

private final int mBytesPerFloat = 4;

// Initialization.

public NuestroRenderer(){

  • *final float[] triangle1VerticesData= {
  • * // X, Y, Z,
  • * // R, G, B, A
  • * -0.5f, -0.25f, 0.0f,
  • * 1.0f, 0.0f, 0.0f, 1.0f,
  • * 0.5f, -0.25f, 0.0f,
  • * 0.0f, 0.0f, 1.0f, 1.0f,
  • * 0.0f, 0.559016994f, 0.0f,
  • *0.0f, 1.0f, 0.0f, 1.0f
  • *}
  • *// Buffer initialization.
  • mTriangle1Vertices = ByteBuffer.allocateDirect(triangle1VerticesData.length mBytesPerFloat).order(ByteOrder.nativeOrder()).asFloatBuffer();
  • *...
  • *mTriangle1Vertices.put(triangle1VerticesData).position(0);
  • *...

}

It is normal for coding to be in Java on Android, but the internal implementation of OpenGL ES 2 is written in C. Before passing our data to OpenGL, we have to convert it to a format it can understand. Java and the native operating system might not store their bytes in the same order, so it is necessary to use a special set of buffer classes, create a ByteBuffer large enough to hold our data, and instruct it to store data using native byte order. Then we convert it into a FloatBuffer so that we can use it to store floating-point data. Finally, we copy our array into buffer memory.

Notes.

Example.