Basic rendering with OpenGL ES
Let us add some graphics to our sample Android application App2
. Here, we show how to create an off-screen bitmap, and then copy it to the screen using the OpenGL ES Version 2 or 3 available on your Android device.
Getting ready
We assume that the reader is somewhat familiar with OpenGL and the GL Shading Language (GLSL). Refer to http://www.opengl.org/documentation for the desktop OpenGL, and http://www.khronos.org/opengles for the mobile OpenGL ES documentation.
How to do it…
- We need to write a simple vertex and fragment GLSL shader that will render our framebuffer on the screen using OpenGL ES. Let's put them directly into
jni/Wrappers.cpp
as strings. The following code shows the vertex shader:static const char g_vShaderStr[] = "#version 100\n" "precision highp float;\n" "attribute vec3 vPosition;\n" "attribute vec3 vCoords;\n" "varying vec2 Coords;\n" "void main()\n" "{\n" " Coords = vCoords.xy;\n" " gl_Position = vec4( vPosition, 1.0 );\n" "}\n";
- The fragment shader is as follows:
static const char g_fShaderStr[] = "#version 100\n" "precision highp float;\n" "varying vec2 Coords;\n" "uniform sampler2D Texture0;\n" "void main()\n" "{\n" " gl_FragColor = texture2D( Texture0, Coords );\n" "}\n";
- We will also need the following helper function to load our shaders into OpenGL ES:
static GLuint LoadShader( GLenum type, const char* shaderSrc ) { GLuint shader = glCreateShader( type ); glShaderSource ( shader, 1, &shaderSrc, NULL ); glCompileShader ( shader ); GLint compiled; glGetShaderiv ( shader, GL_COMPILE_STATUS, &compiled ); GLsizei MaxLength = 0; glGetShaderiv( shader, GL_INFO_LOG_LENGTH, &MaxLength ); char* InfoLog = new char[MaxLength]; glGetShaderInfoLog( shader, MaxLength, &MaxLength, InfoLog ); LOGI( "Shader info log: %s\n", InfoLog ); return shader; }
How it works…
We will not go into all the details about the OpenGL ES programming here, and will instead focus on a minimal application (App3
) that should initialize the GLView
in Java; create fragment and vertex programs, create and fill the vertex array consisting of two triangles that form a single quadrilateral, and then render them with a texture, which is updated from g_FrameBuffer
contents. This is it—just draw the offscreen framebuffer. The following is the code to draw the full-screen quad textured with the offscreen buffer content:
const GLfloat vVertices[] = { -1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, -1.0f, 1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f }; const GLfloat vCoords[] = { 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f }; glUseProgram ( g_ProgramObject );
These attribute variables are declared in a vertex shader. See the value of g_vShaderStr[]
in the preceding code.
GLint Loc1 = glGetAttribLocation(g_ProgramObject,"vPosition"); GLint Loc2 = glGetAttribLocation(g_ProgramObject,"vCoords"); glBindBuffer( GL_ARRAY_BUFFER, 0 ); glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 ); glVertexAttribPointer( Loc1, 3, GL_FLOAT, GL_FALSE, 0, vVertices ); glVertexAttribPointer( Loc2, 3, GL_FLOAT, GL_FALSE, 0, vCoords ); glEnableVertexAttribArray( Loc1 ); glEnableVertexAttribArray( Loc2 ); glDisable( GL_DEPTH_TEST ); glDrawArrays( GL_TRIANGLES, 0, 6 ); glUseProgram( 0 ); glDisableVertexAttribArray( Loc1 ); glDisableVertexAttribArray( Loc2 );
We also need a few JNI callbacks. The first one handles the surface size changes, as seen in the following code:
JNIEXPORT void JNICALLJava_com_packtpub_ndkcookbook_app3_App3Activity_SetSurfaceSize(JNIEnv* env, jclass clazz, int Width, int Height ) { LOGI( "SurfaceSize: %i x %i", Width, Height ); g_Width = Width; g_Height = Height; GLDebug_LoadStaticProgramObject(); glGenTextures( 1, &g_Texture ); glBindTexture( GL_TEXTURE_2D, g_Texture );
Disable mip-mapping through the following code:
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST ); glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA,ImageWidth, ImageHeight, 0, GL_RGBA,GL_UNSIGNED_BYTE, g_FrameBuffer ); }
The second callback does the actual frame rendering:
JNIEXPORT void JNICALL Java_com_packtpub_ndkcookbook_app3_App3Activity_DrawFrame( JNIEnv* env, jobject obj ) {
Invoke our frame rendering callback through the following code:
OnDrawFrame(); glActiveTexture( GL_TEXTURE0 ); glBindTexture( GL_TEXTURE_2D, g_Texture ); glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,ImageWidth, ImageHeight, GL_RGBA,GL_UNSIGNED_BYTE, g_FrameBuffer ); GLDebug_RenderTriangle(); }