The previous recipes describe OpenGL ES 1.x on the Android NDK. This recipe covers how to use OpenGL ES 2.0 in Android NDK.
Readers are recommended to read the introduction of this chapter before going through this recipe. A lot of graphic basics are covered in the following recipes; it is suggested that we go through them first:
The following steps create an Android project that renders a 3D cube with OpenGL ES 2.0 API in Android NDK:
CubeG2
. Set the package name as cookbook.chapter4.cubeg2
. Please refer to the Loading native libraries and registering native methods recipe of Chapter 2, Java Native Interface, if you want more detailed instructions.CubeG2
, select Android Tools | Add Native Support.MyActivity.java
, MyRenderer.java
, and MySurfaceView.java
. We only list a part of MyRenderer.java
here, since the other two files—MyActivity.java
and MySurfaceView.java
—are similar to the files in the previous recipe:@Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { String vertexShaderStr = LoadShaderStr(mContext, R.raw.vshader); String fragmentShaderStr = LoadShaderStr(mContext, R.raw.fshader); naInitGL20(vertexShaderStr, fragmentShaderStr); } @Override public void onDrawFrame(GL10 gl) { naDrawGraphics(mAngleX, mAngleY); } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { naSurfaceChanged(width, height); }
Cube.cpp
, matrix.cpp
, CubeG2.cpp
, Cube.h
, matrix.h
, and mylog.h
files under the jni
folder. The content of the files are summarized as follows:Cube
object and method to draw a 3D cube.Here, we list a part of Cube.cpp
and CubeG2.cpp
.
Cube.cpp
:
… void Cube::draw(GLuint pvPositionHandle) { glVertexAttribPointer(pvPositionHandle, 3, GL_FLOAT, GL_FALSE, 0, vertices); glEnableVertexAttribArray(pvPositionHandle); glDrawArrays(GL_TRIANGLES, 0, 36); } ...
CubeG2.cpp
: It includes the loadShader
, createProgram
, naInitGL20
, and naDrawGraphics
methods, which are explained as follows:
loadShader
: This method creates a shader, attaches a source, and compiles the shader:GLuint loadShader(GLenum shaderType, const char* pSource) { GLuint shader = glCreateShader(shaderType); if (shader) { glShaderSource(shader, 1, &pSource, NULL); glCompileShader(shader); GLint compiled = 0; glGetShaderiv(shader, GL_COMPILE_STATUS, &compiled); if (!compiled) { GLint infoLen = 0; glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLen); if (infoLen) { char* buf = (char*) malloc(infoLen); if (buf) { glGetShaderInfoLog(shader, infoLen, NULL, buf); free(buf); } glDeleteShader(shader); shader = 0; } } } return shader; }
createProgram
: This method creates a program object, attaches shaders, and links the program:GLuint createProgram(const char* pVertexSource, const char* pFragmentSource) { GLuint vertexShader = loadShader(GL_VERTEX_SHADER, pVertexSource); GLuint pixelShader = loadShader(GL_FRAGMENT_SHADER, pFragmentSource); GLuint program = glCreateProgram(); if (program) { glAttachShader(program, vertexShader); glAttachShader(program, pixelShader); glLinkProgram(program); } return program; }
naInitGL20
: This method sets up the OpenGL ES 2.0 environment, gets the shader source string, and gets the shader attribute and uniform positions:void naInitGL20(JNIEnv* env, jclass clazz, jstring vertexShaderStr, jstring fragmentShaderStr) { glDisable(GL_DITHER); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glClearDepthf(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); const char *vertexStr, *fragmentStr; vertexStr = env->GetStringUTFChars(vertexShaderStr, NULL); fragmentStr = env->GetStringUTFChars(fragmentShaderStr, NULL); setupShaders(vertexStr, fragmentStr); env->ReleaseStringUTFChars(vertexShaderStr, vertexStr); env->ReleaseStringUTFChars(fragmentShaderStr, fragmentStr); gvPositionHandle = glGetAttribLocation(gProgram, "vPosition"); gmvP = glGetUniformLocation(gProgram, "mvp"); }
naDrawGraphics
: This method applies model transforms (rotate, scale, and translate) and the projection transform:void naDrawGraphics(JNIEnv* env, jclass clazz, float pAngleX, float pAngleY) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClearColor(0.0, 0.0, 0.0, 1.0f); glUseProgram(gProgram); // GL1x: glRotatef(pAngleX, 0, 1, 0); //rotate around y-axis // GL1x: glRotatef(pAngleY, 1, 0, 0); //rotate around x-axis //rotate rotate_matrix(pAngleX, 0.0, 1.0, 0.0, aRotate); rotate_matrix(pAngleY, 1.0, 0.0, 0.0, aModelView); multiply_matrix(aRotate, aModelView, aModelView); // GL1x: glScalef(0.3f, 0.3f, 0.3f); // Scale down scale_matrix(0.5, 0.5, 0.5, aScale); multiply_matrix(aScale, aModelView, aModelView); // GL1x: glTranslate(0.0f, 0.0f, -3.5f); translate_matrix(0.0f, 0.0f, -3.5f, aTranslate); multiply_matrix(aTranslate, aModelView, aModelView); // gluPerspective(45, aspect, 0.1, 100); perspective_matrix(45.0, (float)gWidth/(float)gHeight, 0.1, 100.0, aPerspective); multiply_matrix(aPerspective, aModelView, aMVP); glUniformMatrix4fv(gmvP, 1, GL_FALSE, aMVP); mCube.draw(gvPositionHandle); }
raw
under the res
folder, and add the following two files to it:vshader
: This is the vertex shader source:attribute vec4 vPosition; uniform mat4 mvp; void main() { gl_Position = mvp * vPosition; }
fshader
: This is the fragment shader source:void main() { gl_FragColor = vec4(0.0,0.5,0.0,1.0); }
Android.mk
file under the jni
folder as follows. Note that we must link to OpenGL ES 2.0 by LOCAL_LDLIBS := -lGLESv2
:LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := CubeG2 LOCAL_SRC_FILES := matrix.cpp Cube.cpp CubeG2.cpp LOCAL_LDLIBS := -lGLESv2 -llog include $(BUILD_SHARED_LIBRARY)
<application>...</application>
in the AndroidManifest.xml
file, which indicates that the Android application uses the OpenGL ES 2.0 feature:<uses-feature android:glEsVersion="0x00020000" android:required="true" />
The sample project renders a 3D cube using OpenGL ES 2.0. OpenGL ES 2.0 provides a programmable pipeline, where a vertex shader and fragment shader can be supplied to control how the vertex and fragment are processed:
Shaders are programmed using OpenGL Shading Language, which is discussed next.
Here, we briefly introduce GLSL.
bool
, int
, float
, and sampler
. There are also vector types for the first three types—bvec2
, bvec3
, bvec4
refer to 2D, 3D, and 4D boolean vectors. ivec2
, ivec3
, and ivec4
represent integer vectors. vec2
, vec3
, and vec4
refer to floating point vectors. Samplers are used for texture sampling and have to be uniform.gl_Vertex
: It is an attribute—a 4D vector representing the vertex position.gl_Color
: It is an attribute—a 4D vector representing the vertex color.gl_ModelViewMatrix
: It is an uniform—the 4x4 model view matrix.gl_ModelViewProjectionMatrix
: It is a uniform. The 4x4 model view projection matrix.gl_Position
: It is only available as vertex shader output. It's a 4D vector representing the final processed vertex position.gl_FragColor
: It is only available as fragment shader output. It's a 4D vector representing the final color to be written to the frame buffer.In our sample project, the vertex shader program simply multiplies every cube vertex with the model-view-projection matrix, and the fragment shader sets green color to every fragment. The following steps should be followed to use the shader source code:
glCreateShader
: It creates a GL_VERTEX_SHADER
or GL_FRAGMENT_SHADER
shader. A non-zero value is returned by it, by which the shader can be referenced.glShaderSource
: It puts the source code in a shader object. The source code stored previously will be completely replaced.glCompileShader
: It compiles the source code of the shader object.glCreateProgram
: It creates an empty program object to which shaders can be attached. Program objects essentially provide a mechanism to link everything needed to be executed together.glAttachShader
: It attaches a shader to a program object.glLinkProgram
: It links a program object. If any GL_VERTEX_SHADER
objects are attached to the program object, they will be used to create an executable running on the vertex processor. If any GL_FRAGMENT_SHADER
shaders are attached, they will be used to create an executable running on the fragment processor.glUseProgram
: A program object as part of current rendering state is installedglGetAttribLocation
: It returns an attribute variable's locationglVertexAttribPointer
: It specifies the location and data format of the array of generic vertex attributes to use at renderingglEnableVertexAttribArray
: It enables a vertex attribute arrayglGetUniformLocation
: It returns a uniform variable's locationglUniform
: It specifies the value of a uniform variableglDrawArrays
: It renders primitives from the array data.The sample project performs model-view transform and projection transform through matrix operations. The details of these transforms are tedious and not within the scope of this book, therefore we won't cover them here. However, detailed comments are provided along with the code. Interested readers could also easily find online resources about these operations.