OpenGL ES 2.0 Java bindings for Android
I’m a nice guy so i wrote a complete JNI bridge to OpenGL ES 2.0 for Android. You can now use OpenGL ES 2.0 from within your Java application directly. Besides the native bindings i also stole the GLJNIView from the hello-gl2 example in the NDK and modified it to become a proper GLSurfaceView implementation. Here’s an example how to decide wheter OpenGL ES 2.0 is supported and instantiation a proper GLSurfaceView then:
import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.IntBuffer; import javax.microedition.khronos.egl.EGL10; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.egl.EGLContext; import javax.microedition.khronos.egl.EGLDisplay; import javax.microedition.khronos.opengles.GL10; import com.badlogic.gdx.backends.android.AndroidGL20; import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView; import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView20; import com.badlogic.gdx.backends.android.surfaceview.GLSurfaceView.Renderer; import com.badlogic.gdx.graphics.GL20; import android.app.Activity; import android.content.Context; import android.os.Bundle; import android.util.Log; public class GL2Test extends Activity { GLSurfaceView view; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if( checkGL20Support( this ) ) view = new GLSurfaceView20( this ); else view = new GLSurfaceView( this ); view.setRenderer( new TestRenderer() ); setContentView(view); } protected void onPause( ) { super.onPause(); view.onPause(); } protected void onResume( ) { super.onResume(); view.onResume(); } private boolean checkGL20Support( Context context ) { EGL10 egl = (EGL10) EGLContext.getEGL(); EGLDisplay display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY); int[] version = new int[2]; egl.eglInitialize(display, version); int EGL_OPENGL_ES2_BIT = 4; int[] configAttribs = { EGL10.EGL_RED_SIZE, 4, EGL10.EGL_GREEN_SIZE, 4, EGL10.EGL_BLUE_SIZE, 4, EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL10.EGL_NONE }; EGLConfig[] configs = new EGLConfig[10]; int[] num_config = new int[1]; egl.eglChooseConfig(display, configAttribs, configs, 10, num_config); egl.eglTerminate(display); return num_config[0] > 0; } class TestRenderer implements Renderer { AndroidGL20 gl2 = new AndroidGL20(); FloatBuffer vertices; int program; int viewportWidth, viewportHeight; @Override public void onDrawFrame(GL10 gl) { gl2.glClearColor( 0.7f, 0.7f, 0.7f, 1 ); gl2.glClear( GL20.GL_COLOR_BUFFER_BIT ); gl2.glViewport ( 0, 0, viewportWidth, viewportHeight ); gl2.glUseProgram ( program ); gl2.glVertexAttribPointer ( 0, 3, GL20.GL_FLOAT, false, 0, vertices ); gl2.glEnableVertexAttribArray ( 0 ); gl2.glDrawArrays ( GL20.GL_TRIANGLES, 0, 3 ); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { viewportWidth = width; viewportHeight = height; } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { String vertexShaderSrc = "attribute vec4 vPosition; \n" + "void main() \n" + "{ \n" + " gl_Position = vPosition; \n" + "} \n"; String fragmentShaderSrc = "precision mediump float;\n" + "void main() \n" + "{ \n" + " gl_FragColor = vec4 ( 1.0, 0.0, 0.0, 1.0 );\n" + "} \n"; int vertexShader = loadShader( GL20.GL_VERTEX_SHADER, vertexShaderSrc ); int fragmentShader = loadShader( GL20.GL_FRAGMENT_SHADER, fragmentShaderSrc ); program = gl2.glCreateProgram(); if( program == 0 ) throw new RuntimeException( "creating program didn't work" ); gl2.glAttachShader( program, vertexShader ); gl2.glAttachShader( program, fragmentShader ); gl2.glBindAttribLocation( program, 0, "vPosition" ); gl2.glLinkProgram( program ); ByteBuffer tmp = ByteBuffer.allocateDirect(4); tmp.order(ByteOrder.nativeOrder()); IntBuffer intbuf = tmp.asIntBuffer(); gl2.glGetProgramiv( program, GL20.GL_LINK_STATUS, intbuf ); int linked = intbuf.get(0); if( linked == 0 ) { gl2.glGetProgramiv( program, GL20.GL_INFO_LOG_LENGTH, intbuf ); int infoLogLength = intbuf.get(0); if( infoLogLength > 1 ) { Log.d( "GL2", "couldn't link program: " + gl2.glGetProgramInfoLog( program ) ); } throw new RuntimeException( "Creating program didn't work" ); } float vVertices[] = { 0.0f, 0.5f, 0.0f, -0.5f, -0.5f, 0.0f, 0.5f, -0.5f, 0.0f }; tmp = ByteBuffer.allocateDirect( 3 * 3 * 4 ); tmp.order(ByteOrder.nativeOrder()); vertices = tmp.asFloatBuffer(); vertices.put( vVertices ); vertices.position(0); } private int loadShader( int type, String source ) { ByteBuffer tmp = ByteBuffer.allocateDirect(4); tmp.order(ByteOrder.nativeOrder()); IntBuffer intbuf = tmp.asIntBuffer(); int shader = gl2.glCreateShader( type ); if( shader == 0 ) throw new RuntimeException( "creating the shader didn't work" ); gl2.glShaderSource( shader, source ); gl2.glCompileShader( shader ); gl2.glGetShaderiv( shader, GL20.GL_COMPILE_STATUS, intbuf ); int compiled = intbuf.get(0); if( compiled == 0 ) { gl2.glGetShaderiv( shader, GL20.GL_INFO_LOG_LENGTH, intbuf ); int infoLogLength = intbuf.get(0); if( infoLogLength > 1 ) { String infoLog = gl2.glGetShaderInfoLog( shader ); Log.d( "GL2", "shader info: " + infoLog ); } throw new RuntimeException( "creating the shader didn't work" ); } return shader; } } } |
This is more or less the hello triangle example from the purple book.
Here’s a couple of things that i changed:
- glGetActiveAttrib and glGetActiveUniform return a string which is
the name of the attribute/uniform.
- glGetProgramLogInfo and glGetShaderLogInfo also directly return a
string. the log is limited to 10k characters internally
- glShaderSource only takes the shader handle and a single java
string.
- glGetShaderSource is not implemented, i will add that some time soon
- glGetVertexAttribPointerv is not implemented and i don’t plan on
implementing it
Everything else is implemented 100% to the specification. I actually
took the gl2.h file from the khronos site to generate the interfaces
and stubs.
You can find the full source code at http://code.google.com/p/gl2-android/,
check it out via subversion from http://gl2-android.googlecode.com/svn/trunk/.
You’ll get an Android eclipse project which a pre-build shared
library. If you want to rebuild the native code you have to add an
Application.mk to your NDK apps/ folder somewhere. The eclipse project
is set to target 2.0, however the library works fine with 1.5 devices
too as long as you don’t try to access OpenGL ES 2.0 functionality.
Simply use the check for OpenGL ES 2.0 from above to decide which
render path to go.
And for your convenience i also packaged the shared library as well as
the jar file with the Java classes. You can find that at
http://gl2-android.googlecode.com/files/androidgl2-0.1.zip. The whole thing is apache 2 licensed so you can do with it whatever you want.
Now write some awesome shaders!


Pingback: Froyo OpenGL 2.0 Java Bindings
Pingback: google - Nexus One Forum - Google Phone Forum