3D C/C++ tutorials - OpenGL ES 2.0 - OpenGL ES 2.0 on Android
3D C/C++ tutorials -> OpenGL ES 2.0 -> OpenGL ES 2.0 on Android
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
Dependencies and prerequisites
Install


For more information read

OpenGLES20Android.java
package com.example.android.opengl;

import android.app.Activity;
import android.opengl.GLSurfaceView;
import android.os.Bundle;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.opengl.GLUtils;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.BufferedReader;
import java.io.IOException;

import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;

import android.opengl.Matrix;

public class OpenGLES20Android extends Activity implements GLSurfaceView.Renderer
{
    private GLSurfaceView SurfaceView;

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);

        SurfaceView = new GLSurfaceView(this);

        setContentView(SurfaceView);

        SurfaceView.setEGLContextClientVersion(2);
        SurfaceView.setRenderer(this);
    }

    @Override
    protected void onPause()
    {
        super.onPause();
        SurfaceView.onPause();
    }

    @Override
    protected void onResume()
    {
        super.onResume();
        SurfaceView.onResume();
    }

    private int Texture, Program, VertexBufferObject;
    
    private int[] Location = new int[3];

    private float[] Model = new float[16];
    private float[] View = new float[16];
    private float[] ModelView = new float[16];
    private float[] Projection = new float[16];
    private float[] ModelViewProjection = new float[16];

    private static float Angle = 0.0f;

    @Override
    public void onSurfaceCreated(GL10 unused, EGLConfig config)
    {
        Texture = LoadTexture2D(R.raw.texture);

        Program = CreateProgram(R.raw.texturing_vs, R.raw.texturing_fs);

        Location[0] = GLES20.glGetUniformLocation(Program, "ModelViewProjection");
        Location[1] = GLES20.glGetAttribLocation(Program, "vert_TexCoord");
        Location[2] = GLES20.glGetAttribLocation(Program, "vert_Position");

        float data[] = { // s, t, x, y, z
            0.0f, 0.0f, -0.5f, -0.5f, -0.5f,
            1.0f, 0.0f, -0.5f, -0.5f,  0.5f,
            1.0f, 1.0f, -0.5f,  0.5f,  0.5f,
            1.0f, 1.0f, -0.5f,  0.5f,  0.5f,
            0.0f, 1.0f, -0.5f,  0.5f, -0.5f,
            0.0f, 0.0f, -0.5f, -0.5f, -0.5f,

            0.0f, 0.0f,  0.5f, -0.5f,  0.5f,
            1.0f, 0.0f,  0.5f, -0.5f, -0.5f,
            1.0f, 1.0f,  0.5f,  0.5f, -0.5f,
            1.0f, 1.0f,  0.5f,  0.5f, -0.5f,
            0.0f, 1.0f,  0.5f,  0.5f,  0.5f,
            0.0f, 0.0f,  0.5f, -0.5f,  0.5f,

            0.0f, 0.0f, -0.5f, -0.5f, -0.5f,
            1.0f, 0.0f,  0.5f, -0.5f, -0.5f,
            1.0f, 1.0f,  0.5f, -0.5f,  0.5f,
            1.0f, 1.0f,  0.5f, -0.5f,  0.5f,
            0.0f, 1.0f, -0.5f, -0.5f,  0.5f,
            0.0f, 0.0f, -0.5f, -0.5f, -0.5f,

            0.0f, 0.0f, -0.5f,  0.5f,  0.5f,
            1.0f, 0.0f,  0.5f,  0.5f,  0.5f,
            1.0f, 1.0f,  0.5f,  0.5f, -0.5f,
            1.0f, 1.0f,  0.5f,  0.5f, -0.5f,
            0.0f, 1.0f, -0.5f,  0.5f, -0.5f,
            0.0f, 0.0f, -0.5f,  0.5f,  0.5f,

            0.0f, 0.0f,  0.5f, -0.5f, -0.5f,
            1.0f, 0.0f, -0.5f, -0.5f, -0.5f,
            1.0f, 1.0f, -0.5f,  0.5f, -0.5f,
            1.0f, 1.0f, -0.5f,  0.5f, -0.5f,
            0.0f, 1.0f,  0.5f,  0.5f, -0.5f,
            0.0f, 0.0f,  0.5f, -0.5f, -0.5f,

            0.0f, 0.0f, -0.5f, -0.5f,  0.5f,
            1.0f, 0.0f,  0.5f, -0.5f,  0.5f,
            1.0f, 1.0f,  0.5f,  0.5f,  0.5f,
            1.0f, 1.0f,  0.5f,  0.5f,  0.5f,
            0.0f, 1.0f, -0.5f,  0.5f,  0.5f,
            0.0f, 0.0f, -0.5f, -0.5f,  0.5f,
        };

        VertexBufferObject = CreateVertexBufferObject(data);

        Matrix.setLookAtM(View, 0, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f);

        GLES20.glEnable(GLES20.GL_DEPTH_TEST);
        GLES20.glEnable(GLES20.GL_CULL_FACE);
    }

    @Override
    public void onDrawFrame(GL10 unused)
    {
        GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);

        Matrix.setIdentityM(Model, 0);
        Matrix.rotateM(Model, 0, Angle, 0.0f, 1.0f, 0.0f);
        Matrix.rotateM(Model, 0, Angle, 1.0f, 0.0f, 0.0f);

        Angle += 0.25f;

        Matrix.multiplyMM(ModelView, 0, View, 0, Model, 0);
        Matrix.multiplyMM(ModelViewProjection, 0, Projection, 0, ModelView, 0);

        GLES20.glUseProgram(Program);
        GLES20.glUniformMatrix4fv(Location[0], 1, false, ModelViewProjection, 0); 

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, VertexBufferObject);

        GLES20.glEnableVertexAttribArray(Location[1]);
        GLES20.glVertexAttribPointer(Location[1], 2, GLES20.GL_FLOAT, false, 20, 0);

        GLES20.glEnableVertexAttribArray(Location[2]);
        GLES20.glVertexAttribPointer(Location[2], 3, GLES20.GL_FLOAT, false, 20, 8);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,  Texture);

        GLES20.glDrawArrays(GLES20.GL_TRIANGLES, 0, 36);

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,  0);

        GLES20.glDisableVertexAttribArray(Location[2]);
        GLES20.glDisableVertexAttribArray(Location[1]);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        GLES20.glUseProgram(0);
    }

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

        Matrix.perspectiveM(Projection, 0, 45.0f, (float)width / (float)height, 0.125f, 512.0f);
    }
    
    private int GetMaxAnisotropy()
    {
        int[] maxAnisotropy = new int[1];

        GLES20.glGetIntegerv(GLES11Ext.GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, maxAnisotropy, 0);

        return maxAnisotropy[0];
    }
    
    private int LoadTexture2D(int resourceId)
    {
        int[] texture = new int[1];

        GLES20.glGenTextures(1, texture, 0);

        if(texture[0] == 0)
        {
            throw new RuntimeException("\nError creating texture (" + resourceId + ")!");
        }

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inScaled = false;

        Bitmap bitmap = BitmapFactory.decodeResource(getResources(), resourceId, options);

        if(bitmap == null)
        {
            throw new RuntimeException("\nError loading texture (" + resourceId + ")!");
        }

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, texture[0]);

        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR_MIPMAP_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
        GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES11Ext.GL_TEXTURE_MAX_ANISOTROPY_EXT, GetMaxAnisotropy());

        GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);

        GLES20.glGenerateMipmap(GLES20.GL_TEXTURE_2D); 

        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);

        bitmap.recycle();

        return texture[0];
    } 
    
    private String LoadShaderSource(int resourceId)
    {
        InputStream inputStream = getResources().openRawResource(resourceId);
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);

        String string;
        StringBuilder stringBuilder = new StringBuilder();

        try
        {
            while((string = bufferedReader.readLine()) != null)
            {
                stringBuilder.append(string);
                stringBuilder.append('\n');
            }
        }
        catch(IOException e)
        {
            return null;
        }

        return stringBuilder.toString();
    }

    private int CreateShader(int type, int resourceId)
    {
        int shader = GLES20.glCreateShader(type);

        if(shader == 0)
        {
            throw new RuntimeException("\nError creating shader (" + resourceId + ")!");
        }

        GLES20.glShaderSource(shader, LoadShaderSource(resourceId));
        GLES20.glCompileShader(shader);

        int[] compileStatus = new int[1];

        GLES20.glGetShaderiv(shader, GLES20.GL_COMPILE_STATUS, compileStatus, 0);

        if(compileStatus[0] == GLES20.GL_FALSE)
        {
            String info = GLES20.glGetShaderInfoLog(shader);

            GLES20.glDeleteShader(shader);

            throw new RuntimeException("\nError compiling shader (" + resourceId + "):\n" + info);
        }

        return shader;
    }

    private int CreateProgram(int vertexShaderResourceId, int fragmentShaderResourceId)
    {
        int vertexShader = CreateShader(GLES20.GL_VERTEX_SHADER, vertexShaderResourceId);

        int fragmentShader = CreateShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderResourceId);

        int program = GLES20.glCreateProgram();

        if(program == 0)
        {
            GLES20.glDeleteShader(vertexShader);
            GLES20.glDeleteShader(fragmentShader);
            
            throw new RuntimeException("\nError creating program (" + vertexShaderResourceId + ", " + fragmentShaderResourceId + ")!");
        }

        GLES20.glAttachShader(program, vertexShader);
        GLES20.glAttachShader(program, fragmentShader);
        GLES20.glLinkProgram(program);

        int[] linkStatus = new int[1];

        GLES20.glGetProgramiv(program, GLES20.GL_LINK_STATUS, linkStatus, 0);

        if(linkStatus[0] == GLES20.GL_FALSE)
        {
            String info = GLES20.glGetProgramInfoLog(program);

            GLES20.glDetachShader(program, vertexShader);
            GLES20.glDetachShader(program, fragmentShader);
            GLES20.glDeleteShader(vertexShader);
            GLES20.glDeleteShader(fragmentShader);
            GLES20.glDeleteProgram(program);

            throw new RuntimeException("\nError linking program (" + vertexShaderResourceId + ", " + fragmentShaderResourceId + "):\n" + info);
        }

        return program;
    }
    
    private int CreateVertexBufferObject(float[] data)
    {
        int[] vertexBufferObject = new int[1];

        GLES20.glGenBuffers(1,  vertexBufferObject, 0);

        if(vertexBufferObject[0] == 0)
        {
            throw new RuntimeException("\nError creating vertex buffer object!");
        }

        FloatBuffer buffer = ByteBuffer.allocateDirect(data.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
        buffer.put(data).position(0);

        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, vertexBufferObject[0]); 
        GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, buffer.capacity() * 4, buffer, GLES20.GL_STATIC_DRAW);
        GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);

        return vertexBufferObject[0];
    }
}
texturing_vs
uniform mat4 ModelViewProjection;

attribute vec2 vert_TexCoord;
attribute vec3 vert_Position;

varying vec2 TexCoord;

void main()
{
    TexCoord = vert_TexCoord;
    gl_Position = ModelViewProjection * vec4(vert_Position, 1.0);
}
texturing_fs
precision mediump float;

uniform sampler2D Texture;

varying vec2 TexCoord;

void main()
{
    gl_FragColor = texture2D(Texture, TexCoord);
}
Download
OpenGLES20Android.zip (Eclipse Juno)

© 2010 - 2016 Bc. Michal Belanec, michalbelanec (at) centrum (dot) sk
Last update June 25, 2016
OpenGL® is a registered trademark of Silicon Graphics Inc.