Android – Use OpenGL to Render Triangle

In the previous guide, we look at how to startup an OpenGLES Android App with a view and an attached renderer object. Now, let us continue creating both a shader and geometry to render a simple primitive object onto the display. In addition, we will start by looking at OpenGL|ES 2.0. Afterward, we will look at 3.0, which resembles more modern OpenGL and allow for better and more efficient code. There are some differences in both how the shader can be written as well as the OpenGL code between the versions. All of the source code can be found on GitHub.

At the end of this guide, we should have something similar to the following figure.

Example of Rendering Triangle with OpenGLES

Geometry Buffer Data Layout

First, let us take a look at geometry. For the data for the geometry, we will be using a data array of vertices only, rather than using indices, which for this example does not make sense. However, we will take a look at it in the next tutorial where it actually makes sense to us indices over a vertex array.

The data layout is important to know since the OpenGL needs that information in order to pass the correct data to the shader program. Otherwise, the result may become unexpected.

Triangle Data Layout

Let us take a look at our layout for the triangle. It contains 3 vertices, each vertex structured with six floats. Thus, the two first-line of float values is a single vertex. The first line is the vertex location whereas the second is the color, in this case. Finally, the order in the array is just as important to be aware of. Simply, the order will determine the culling.

float[] triangleGeometryData = new float[]{
		-1.0f, -1.0f, 0.0f, /*  Vertex0 location. */
		1.0f, 0.0f, 0.0f,   /*  Vertex0 color. */
		-0.0f, 1.0f, 0.0f,  /*  Vertex1 location. */
		0.0f, 1.0f, 0.0f,   /*  Vertex1 color. */
		1.0f, -1.0f, 0.0f,  /*  Vertex2 location. */
		0.0f, 0.0f, 1.0f,   /*  Vertex2 color. */
};

Creating Shader Program Object

When creating a shader from source code, using GLSL code. It is similar to when compiling source code from any other programming language. It does mimic C/C++ much since it contains a compile and link phase, and not to mention the language is very similar. First, create a shader object for each shader stage, in this case, vertex and fragment. Next, provide the source code as a string object, attach and compile. Finally, check for errors before attaching it to the shader object to the shader program. Once attached, link the program, and once again, check for error. Finally, the shader object should be detached and deleted unless used for creating another additional shader program.

The error checking is important in order to determine if it will execute properly. Thus throw an exception can be later used, for instance, a fallback shader and etc.

private void checkShaderError(int shader) {
	int[] lstatus = new int[1];
	/*  */
	GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
	/*	*/
	if (lstatus[0] != GLES20.GL_TRUE) {
		String log = GLES20.glGetShaderInfoLog(shader);
		throw new RuntimeException(String.format("Shader compilation error\n%s", log));
	}
}

public int loadShader(String vertexSource, String fragmentSource) {

	int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
	int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

	GLES20.glShaderSource(vshader, vertexSource);
	GLES20.glCompileShader(vshader);
	checkShaderError(vshader);
	GLES20.glShaderSource(fshader, fragmentSource);
	GLES20.glCompileShader(fshader);
	checkShaderError(vshader);

	int program = GLES20.glCreateProgram();

	GLES20.glBindAttribLocation(program, 0, vertexName);
	GLES20.glBindAttribLocation(program, 1, colorName);

	GLES20.glAttachShader(program, vshader);
	GLES20.glAttachShader(program, fshader);

	/*  */
	GLES20.glLinkProgram(program);

	int[] lstatus = new int[1];
	GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
	if (lstatus[0] != GLES20.GL_TRUE) {
		String log = GLES20.glGetProgramInfoLog(program);
		throw new RuntimeException(String.format("Shader link error\n%s", log));
	}

	/*  */
	GLES20.glDetachShader(program, vshader);
	GLES20.glDeleteShader(vshader);
	GLES20.glDetachShader(program, fshader);
	GLES20.glDeleteShader(fshader);

	return program;
}

OpenGLES 2.0

First, let us go through the OpenGLES 2.0 shader version. Afterward, the OpenGLES 3.0 will be covered.

Vertex Shader

First, the variables with the attribute qualifier are where the data from the VBO will be provided via. The varying on the other hand is a variable that it will pass to the next shader stage, which would be the fragment state. Finally, the main function is the entry point. Similar to many other languages that are using main as the first function to call. Inside, the gl_Position is a built-in variable for which the vertex data is passed, in order to be passed over to the rasterizer.

attribute vec3 vPosition;
attribute vec3 color;
precision mediump float;

varying	vec3 vColor;

void main(void) {
	gl_Position = vec4(vPosition, 1.0);
	vColor = color;
}

Fragment Shader

precision mediump float;
varying	vec3 vColor;

void main(void) {
	gl_FragColor = vec4(vColor, 1.0f);
}

OpenGLES 3.0

Creating VBO (Vertex Buffer Object) and VAO (Vertex Array Object)

The VBO vertex buffer object is a storage object in OpenGL. It contains data, similar to a texture object, which we will take a look guide. There exist multiple types of VBO object types, such as an array, element, uniform, transform feedback and etc. This type is simply used for the OpenGL to handle the data more proficiently based on what it is expected to be used for. But they can be thought of as how you’d allocate memory with new. For instance, the triangle only needs a single buffer for storing the vertex location and the vertex color.

The Vertex array object (VAO) is on the other hand a container object. It will contain information on multiple buffers and how they are configured. Thus, when calling drawArray, binding the VAO will handle all of the rest of the information. Whereas, using VBO only requires each draw to be setup with each attribute and how the data is passed via the shader, adding additional OpenGL calls.

Rendering Logic for Drawing a Triangle

Finally, it is time for the drawing logic. It can be seen that using VAO also reduces the number of method calls in the drawing logic, making it more readable and simpler.

OpenGL 2.0 Rendering Logic

	private void drawOpenGL20() {
		/*  Draw Triangle.  */
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangle_vbo);
		GLES20.glUseProgram(program);
		int vertexLocation = GLES20.glGetAttribLocation(program, vertexName);
		int colorLocation = GLES20.glGetAttribLocation(program, colorName);
		GLES20.glEnableVertexAttribArray(vertexLocation);
		GLES20.glEnableVertexAttribArray(colorLocation);
		GLES20.glVertexAttribPointer(vertexLocation, 3, GLES20.GL_FLOAT, false, 24, 0);
		GLES20.glVertexAttribPointer(colorLocation, 3, GLES20.GL_FLOAT, false, 24, 12);
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
	}

OpenGL 3.0 Rendering Logic

	private void drawOpenGL30() {
		GLES20.glUseProgram(program);
		GLES30.glBindVertexArray(vba);
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
	}

Final Source code file

package org.sample.opengles_triangle;

import android.content.Context;
import android.opengl.GLES10;
import android.opengl.GLES20;
import android.opengl.GLES30;
import android.opengl.GLSurfaceView;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.Buffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

public class GLGraphicRenderer implements GLSurfaceView.Renderer {
	public int triangle_vbo;
	public int program;
	Context context;
	private int vba;

	final String vertexName = "vPosition";
	final String colorName = "color";

	public GLGraphicRenderer(Context context) {
		this.context = context;private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);
private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, vshader);
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}
	}

	private void checkShaderError(int shader) {
		int[] lstatus = new int[1];
		/*  */
		GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, lstatus, 0);
		/*	*/
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetShaderInfoLog(shader);
			throw new RuntimeException(String.format("Shader compilation error\n%s", log));
		}
	}

	public int loadShader(String vertexSource, String fragmentSource) {

		int vshader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
		int fshader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);

		GLES20.glShaderSource(vshader, vertexSource);
		GLES20.glCompileShader(vshader);
		checkShaderError(vshader);
		GLES20.glShaderSource(fshader, fragmentSource);
		GLES20.glCompileShader(fshader);
		checkShaderError(vshader);

		int program = GLES20.glCreateProgram();

		GLES20.glBindAttribLocation(program, 0, vertexName);
		GLES20.glBindAttribLocation(program, 1, colorName);

		GLES20.glAttachShader(program, vshader);
		GLES20.glAttachShader(program, fshader);

		/*  */
		GLES20.glLinkProgram(program);

		int[] lstatus = new int[1];
		GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, lstatus, 0);
		if (lstatus[0] != GLES20.GL_TRUE) {
			String log = GLES20.glGetProgramInfoLog(program);
			throw new RuntimeException(String.format("Shader link error\n%s", log));
		}

		/*  */
		GLES20.glDetachShader(program, vshader);
		GLES20.glDeleteShader(vshader);
		GLES20.glDetachShader(program, fshader);
		GLES20.glDeleteShader(fshader);

		return program;
	}

	public int createBuffer(float[] data) {

		Buffer buffer = FloatBuffer.wrap(data);
		int[] buf = new int[1];
		GLES20.glGenBuffers(1, buf, 0);
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, buf[0]);
		GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, data.length * 4, buffer, GLES20.GL_STATIC_DRAW);

		int vertexLocation = GLES20.glGetAttribLocation(program, vertexName);
		int colorLocation = GLES20.glGetAttribLocation(program, colorName);

		GLES20.glEnableVertexAttribArray(vertexLocation);
		GLES20.glEnableVertexAttribArray(colorLocation);
		GLES20.glVertexAttribPointer(vertexLocation, 3, GLES20.GL_FLOAT, false, 24, 0);
		GLES20.glVertexAttribPointer(colorLocation, 3, GLES20.GL_FLOAT, false, 24, 12);
		return buf[0];
	}

	public String readRawStringFile(Context context, int id) {
		InputStream inputStream = context.getResources().openRawResource(id);
		String str = "";
		StringBuffer buf = new StringBuffer();
		try {
			BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
			if (inputStream != null) {
				while ((str = reader.readLine()) != null) {
					buf.append(str + "\n");
				}
			}
		} catch (IOException e) {
		} finally {
			try {
				inputStream.close();
			} catch (Throwable ignore) {
			}
		}
		return buf.toString();
	}

	public void onSurfaceCreated(GL10 unused, EGLConfig config) {
		// Set the background frame color
		GLES20.glClearColor(0.2f, 0.2f, 0.2f, 1.0f);

		String vertexSource = readRawStringFile(context, R.raw.vertex_es_20);
		String fragmentSource = readRawStringFile(context, R.raw.fragment_es_20);

		float[] triangleGeometryData = new float[]{
				-1.0f, -1.0f, 0.0f, /*  Vertex0 location. */
				1.0f, 0.0f, 0.0f,   /*  Vertex0 color. */
				-0.0f, 1.0f, 0.0f,  /*  Vertex1 location. */
				0.0f, 1.0f, 0.0f,   /*  Vertex1 color. */
				1.0f, -1.0f, 0.0f,  /*  Vertex2 location. */
				0.0f, 0.0f, 1.0f,   /*  Vertex2 color. */
		};
		program = loadShader(vertexSource, fragmentSource);

		IntBuffer buf = IntBuffer.allocate(1);
		GLES30.glGenVertexArrays(1, buf);
		GLES30.glBindVertexArray(buf.get(0));
		triangle_vbo = createBuffer(triangleGeometryData);
		GLES30.glBindVertexArray(0);
		vba = buf.get(0);
	}

	private void drawOpenGL20() {
		/*  Draw Triangle.  */
		GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, triangle_vbo);
		GLES20.glUseProgram(program);
		int vertexLocation = GLES20.glGetAttribLocation(program, vertexName);
		int colorLocation = GLES20.glGetAttribLocation(program, colorName);
		GLES20.glEnableVertexAttribArray(vertexLocation);
		GLES20.glEnableVertexAttribArray(colorLocation);
		GLES20.glVertexAttribPointer(vertexLocation, 3, GLES20.GL_FLOAT, false, 24, 0);
		GLES20.glVertexAttribPointer(colorLocation, 3, GLES20.GL_FLOAT, false, 24, 12);
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
	}

	private void drawOpenGL30() {
		GLES20.glUseProgram(program);
		GLES30.glBindVertexArray(vba);
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
	}

	public void onDrawFrame(GL10 unused) {
		// Redraw background color
		GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

		/*  Draw Triangle - Using OpenGLES 2.0  */
		//drawOpenGL20();

		/*  Draw Triangle - Using OpenGLES 3.0  */
		drawOpenGL30();
	}

	public void onSurfaceChanged(GL10 unused, int width, int height) {
		GLES20.glViewport(0, 0, width, height);
	}
}