OpenGL ES
OpenGL ES Texture Mapping
With this example we are going to demonstrate how to perform OpenGL ES Texture mapping (OpenGL ES on Android). We shall create a square and we will apply a texture onto it.
A texture is nothing more than a bitmap image. This method is very useful when creating 2D games and is the preferred way to display images using OpenGL.
In short, to use OpenGL ES Texture Mapping, follow these steps:
- Create a class (here named Square) responsible for drawing with OpenGL
- Implement the Android Renderer interface
- Draw the objects in the onDrawFrame and onSurfaceChanged methods
- Create an Android activity (here named Run)
- Use the Android GLSurfaceView to draw the various objects on it
Follow the code snippet(s) below.
package net.obviam.opengl; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.opengl.GLU; import android.opengl.GLSurfaceView.Renderer; public class GlRenderer implements Renderer { private Square square; // the square private Context context; /** Constructor to set the handed over context */ public GlRenderer(Context context) { this.context = context; // initialise the square this.square = new Square(); } @Override public void onDrawFrame(GL10 gl) { // clear Screen and Depth Buffer gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Reset the Modelview Matrix gl.glLoadIdentity(); // Drawing gl.glTranslatef(0.0f, 0.0f, -5.0f); // move 5 units INTO the screen // is the same as moving the camera 5 units away // gl.glScalef(0.5f, 0.5f, 0.5f); // scale the square to 50% // otherwise it will be too large square.draw(gl); // Draw the triangle } @Override public void onSurfaceChanged(GL10 gl, int width, int height) { if(height == 0) { //Prevent A Divide By Zero By height = 1; //Making Height Equal One } gl.glViewport(0, 0, width, height); //Reset The Current Viewport gl.glMatrixMode(GL10.GL_PROJECTION); //Select The Projection Matrix gl.glLoadIdentity(); //Reset The Projection Matrix //Calculate The Aspect Ratio Of The Window GLU.gluPerspective(gl, 45.0f, (float)width / (float)height, 0.1f, 100.0f); gl.glMatrixMode(GL10.GL_MODELVIEW); //Select The Modelview Matrix gl.glLoadIdentity(); //Reset The Modelview Matrix } @Override public void onSurfaceCreated(GL10 gl, EGLConfig config) { // Load the texture for the square square.loadGLTexture(gl, this.context); gl.glEnable(GL10.GL_TEXTURE_2D); //Enable Texture Mapping ( NEW ) gl.glShadeModel(GL10.GL_SMOOTH); //Enable Smooth Shading gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); //Black Background gl.glClearDepthf(1.0f); //Depth Buffer Setup gl.glEnable(GL10.GL_DEPTH_TEST); //Enables Depth Testing gl.glDepthFunc(GL10.GL_LEQUAL); //The Type Of Depth Testing To Do //Really Nice Perspective Calculations gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); } }
package net.obviam.opengl; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.opengl.GLUtils; public class Square { private FloatBuffer vertexBuffer; // buffer holding the vertices private float vertices[] = { -1.0f, -1.0f, 0.0f, // V1 - bottom left -1.0f, 1.0f, 0.0f, // V2 - top left 1.0f, -1.0f, 0.0f, // V3 - bottom right 1.0f, 1.0f, 0.0f // V4 - top right }; private FloatBuffer textureBuffer; // buffer holding the texture coordinates private float texture[] = { // Mapping coordinates for the vertices 0.0f, 1.0f, // top left (V2) 0.0f, 0.0f, // bottom left (V1) 1.0f, 1.0f, // top right (V4) 1.0f, 0.0f // bottom right (V3) }; /** The texture pointer */ private int[] textures = new int[1]; public Square() { // a float has 4 bytes so we allocate for each coordinate 4 bytes ByteBuffer byteBuffer = ByteBuffer.allocateDirect(vertices.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); // allocates the memory from the byte buffer vertexBuffer = byteBuffer.asFloatBuffer(); // fill the vertexBuffer with the vertices vertexBuffer.put(vertices); // set the cursor position to the beginning of the buffer vertexBuffer.position(0); byteBuffer = ByteBuffer.allocateDirect(texture.length * 4); byteBuffer.order(ByteOrder.nativeOrder()); textureBuffer = byteBuffer.asFloatBuffer(); textureBuffer.put(texture); textureBuffer.position(0); } /** * Load the texture for the square * @param gl * @param context */ public void loadGLTexture(GL10 gl, Context context) { // loading texture Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.drawable.android); // generate one texture pointer gl.glGenTextures(1, textures, 0); // ...and bind it to our array gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); // create nearest filtered texture gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST); gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR); //Different possible texture parameters, e.g. GL10.GL_CLAMP_TO_EDGE // gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S, GL10.GL_REPEAT); // gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T, GL10.GL_REPEAT); // Use Android GLUtils to specify a two-dimensional texture image from our bitmap GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0); // Clean up bitmap.recycle(); } /** The draw method for the square with the GL context */ public void draw(GL10 gl) { // bind the previously generated texture gl.glBindTexture(GL10.GL_TEXTURE_2D, textures[0]); // Point to our buffers gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY); // Set the face rotation gl.glFrontFace(GL10.GL_CW); // Point to our vertex buffer gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, textureBuffer); // Draw the vertices as triangle strip gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3); //Disable the client state before leaving gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY); } }
package net.obviam.opengl; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import javax.microedition.khronos.opengles.GL10; public class Triangle { private FloatBuffer vertexBuffer; // buffer holding the vertices private float vertices[] = { -0.5f, -0.5f, 0.0f, // V1 - first vertex (x,y,z) 0.5f, -0.5f, 0.0f, // V2 - second vertex 0.0f, 0.5f, 0.0f // V3 - third vertex // 1.0f, 0.5f, 0.0f // V3 - third vertex }; public Triangle() { // a float has 4 bytes so we allocate for each coordinate 4 bytes ByteBuffer vertexByteBuffer = ByteBuffer.allocateDirect(vertices.length * 4); vertexByteBuffer.order(ByteOrder.nativeOrder()); // allocates the memory from the byte buffer vertexBuffer = vertexByteBuffer.asFloatBuffer(); // fill the vertexBuffer with the vertices vertexBuffer.put(vertices); // set the cursor position to the beginning of the buffer vertexBuffer.position(0); } /** The draw method for the triangle with the GL context */ public void draw(GL10 gl) { gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // set the colour for the background // gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // to show the color (paint the screen) we need to clear the color buffer // gl.glClear(GL10.GL_COLOR_BUFFER_BIT); // set the colour for the triangle gl.glColor4f(0.0f, 1.0f, 0.0f, 0.5f); // Point to our vertex buffer gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); // Draw the vertices as triangle strip gl.glDrawArrays(GL10.GL_TRIANGLE_STRIP, 0, vertices.length / 3); //Disable the client state before leaving gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); } }
package net.obviam.opengl; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; public class Run extends Activity { /** The OpenGL view */ private GLSurfaceView glSurfaceView; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // requesting to turn the title OFF requestWindowFeature(Window.FEATURE_NO_TITLE); // making it full screen getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // Initiate the Open GL view and // create an instance with this activity glSurfaceView = new GLSurfaceView(this); // set our renderer to be the main renderer with // the current activity context glSurfaceView.setRenderer(new GlRenderer(this)); setContentView(glSurfaceView); } /** * Remember to resume the glSurface */ @Override protected void onResume() { super.onResume(); glSurfaceView.onResume(); } /** * Also pause the glSurface */ @Override protected void onPause() { super.onPause(); glSurfaceView.onPause(); } }
This was an example of how to use OpenGL ES Texture Mapping.
Related Article:
Reference: Texture Mapping – OpenGL Android (Displaying Images using OpenGL and Squares) from our JCG partner Tamas Jano at the “Against The Grain” blog.