You are on page 1of 16

OpenGL A cross-platform API for writing 2D and 3D graphic applications Developed by SGI in 1992 Used mainly in video games,

mes, CAD, etc. Managed by the non-profit Khronos group.

OpenGL ES A subset of OpenGL for mobile and embedded devices. Contains functionality stripped down from the original OpenGL API. o For example, using fixed-point data types, since mobile devices often lack a Floating-point unit (FPU). o No glBegin, glEnd. Android is using OPENGL ES and not OpenGL, since OpenGL requires high graphical computation ability (usually from a graphic card such as Nvidia GForce or ATI Radeon) which phones still lack of. In android, although we write Java code, OpenGL runs on native code. OpenGL ES version can be found here: http://www.khronos.org/opengles/

Installing and Using OpenGL ES AI Using OpenGL ES with android is easy, since its function are already built in. To start programing in OpenGL ES , all you have to do is: Write a custom View subclass. Obtain a handle to an OpenGLContext which provides access to the OpenGL functionality. In your View's onDraw() method, get a handle to a GL object, and use its methods to perform GL operations. Rendering The process of generation of image from a model of the world. Performed usually in hardware by the GPU (Graphical Processing Unit) This happens several times a second. o The rate is called frames per second (FPS).

GLSurfaceView A view which displays results of OpenGL rendering. Supports both on-demand and continuous rendering. Renders on a dedicated thread. Accepts a user-provided Renderer object that does the actual rendering.

GLSurfaceView.Renderer Interface Contains 3 methods to implement:

o o

onSurfaceCreated - Called when the surface is created or recreated (init). Used to setup features in the rendering cycle, such as- the color to clear the screen with, enabling/disabling z-buffer, etc. onDrawFrame - Called to draw the current frame. The actual drawing takes place here. onSurfaceChanged - Called when the surface changed size. If your device supports flipping between landscape and portrait you will get a call to this function when it happens. What you do here is setting upp the new ratio.

Putting it Together The onCreate method (in the class that extends Activity) is very simple: package se.jayway.opengl.tutorial; import android.app.Activity; import android.opengl.GLSurfaceView; import android.os.Bundle; public class MyActivity extends Activity { /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); GLSurfaceView view = new GLSurfaceView(this); view.setRenderer(new MyRenderer()); setContentView(view); } }

package se.jayway.opengl.tutorial; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; import android.opengl.GLU; import android.opengl.GLSurfaceView.Renderer;

public class MyRenderer implements Renderer { public void onSurfaceCreated(GL10 gl, EGLConfig config) { // you're implementation comes here // some default settings: // Set the background color to black ( rgba ). gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Enable Smooth Shading, default not really needed. gl.glShadeModel(GL10.GL_SMOOTH); // Depth buffer setup. gl.glClearDepthf(1.0f); // Enables depth testing. gl.glEnable(GL10.GL_DEPTH_TEST); // The type of depth testing to do. gl.glDepthFunc(GL10.GL_LEQUAL); // Really nice perspective calculations. gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, GL10.GL_NICEST); } public void onDrawFrame(GL10 gl) { // you're implementation comes here // some default settings: // Clears the screen and depth buffer. gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } cont.

cont. public void onSurfaceChanged(GL10 gl, int width, int height) { // you're implementation comes here // Sets the current view port to the new size. gl.glViewport(0, 0, width, height); // Select the projection matrix gl.glMatrixMode(GL10.GL_PROJECTION); // Reset the projection matrix gl.glLoadIdentity(); // Calculate the aspect ratio of the window GLU.gluPerspective(gl, 45.0f, (float) width / (float) height, 0.1f, 100.0f); // Select the modelview matrix gl.glMatrixMode(GL10.GL_MODELVIEW); // Reset the modelview matrix gl.glLoadIdentity(); } }

Full Screen Just add these lines in theMyActivity class and you will get fullscreen: public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); // ... Previous code }

This is pretty much all you need to get your view up and running. If you compile and run it you will see a nice black screen.

GL10 Class Used for actually drawing the objects on the screen. o For example, glDrawElements, glRotate If youve programmed in OpenGL, this class contains all of the functions you are familiar with.

Vertex, Edge, Face Vertex a point. The smallest graphical unit. 2 vertices define an edge. Can also define a point light source or the camera position. Edge a line. Defined by 2 point. 3 edges define a face. Edges are border lines of faces and polygons. Face a triangle surface. Defined by 3 edges. A group of connected faces can compose and represent an object in the scene.

Drawing a Square In order to draw a square we first need to define it's vertices.

Defining the Vertices Vertices are defined as a float array that is put into a byte buffer to gain better performance.

Float[] vertices = { -1.0f, 1.0f, 0.0f, // 0, Top Left -1.0f, -1.0f, 0.0f, // 1, Bottom Left 1.0f, -1.0f, 0.0f, // 2, Bottom Right 1.0f, }; // a float is 4 bytes, therefore we multiply the number if vertices by 4 ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); FloatBuffer vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0); 1.0f, 0.0f, // 3, Top Right

The OpenGL Camera When programming in OpenGL, one must always keep the camera in mind. By default, the camera is located at (0,0,0), and looking at the -z direction.

Configuring Face Parameters The order DOES matter! The direction (=the order of vertices in the polygon) defines the front and back sides of the polygon. So it's a good idea to use the same winding all over your project. The default winding is counter-clockwise. However, It is still possible to change the direction that defines the front face with glFrontFace.

// Direction of front of face gl.glFrontFace(GL10.GL_CCW); // Cull back faces (skip the faces that are turned into the screen) gl.glEnable(GL10.GL_CULL_FACE); gl.glCullFace(GL10.GL_BACK);

Defining the Indices Buffer The indices buffer contains the indices of the defined vertices in the order they should be drawn on the screen.

Again, we allocate a byte buffer to gain some performance.

short[] indices = { 0, 1, 2, 0, 2, 3 }; // short is 2 bytes ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); ShortBuffer indexBuffer = ibb.asShortBuffer(); indexBuffer.put(indices); indexBuffer.position(0);

Render There are 2 functions to draw elements with: o glDrawArrays(int mode, int first, int count); draws the vertices in that order they are specified in the construction of our verticesBuffer. o glDrawElements(int mode, int count, int type, Buffer indices); needs to know the order which to draw the vertices, it needs the indicesBuffer.

Actually Drawing We used glDrawElements and our indices buffer in order to draw a triangle:

gl.glDrawElements( GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer );

Note that the first parameter of both functions is mode. This parameter defines which primitive the function should draw.

Other Kinds of Shapes (primitives) The following figure displays the different primitives that can be drawn on screen, simply by changing the mode parameter:

Putting it all together package se.jayway.opengl.tutorial; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.nio.ShortBuffer; import javax.microedition.khronos.opengles.GL10; public class Square { // Our vertices private float[] vertices = { -1.0f, 1.0f, 0.0f, // 0, Top Left // 1, Bottom Left // 2, Bottom Right // 3, Top Right

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

// The order we like to connect them private short[] indices = { 0, 1, 2, 0, 2, 3 }; // Our vertex buffer private FloatBuffer vertexBuffer; // Our index buffer private ShortBuffer indexBuffer; public Square() { ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4); vbb.order(ByteOrder.nativeOrder()); vertexBuffer = vbb.asFloatBuffer(); vertexBuffer.put(vertices); vertexBuffer.position(0);

ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2); ibb.order(ByteOrder.nativeOrder()); indexBuffer = ibb.asShortBuffer(); indexBuffer.put(indices); indexBuffer.position(0); } cont.

cont. // This function draws our square on screen public void draw(GL10 gl) { // Counter-clockwise winding gl.glFrontFace(GL10.GL_CCW); // Enable face culling gl.glEnable(GL10.GL_CULL_FACE); // What faces to remove with the face culling gl.glCullFace(GL10.GL_BACK); // Enables the vertices buffer for writing and rendering gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); // Specifies the location and data format of an array of // vertex coordinates to use when rendering gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); gl.glDrawElements(GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer); // Disable the vertices buffer gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); // Disable face culling gl.glDisable(GL10.GL_CULL_FACE); } }

We have to initialize our square in the MyRenderer class. // Initialize our square Square square = new Square(); And in the draw function call on the square to draw. public void onDrawFrame(GL10 gl) { // Clears the screen and depth buffer gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Draw our square square.draw(gl); // ( NEW ) }

Doesnt Work! Why? Since by default the camera is located at (0,0,0), we need to move the square away. Note that the default camera direction id towards the (0,0,-1) direction, i.e looking at the Z axis. Do this by calling: gl.glTranslatef(0, 0, -4); before the actual draw in onDrawFrame.

Still Doesnt Work! Why? glTranslatef will be called every frame, so the square will move every frame! Therefore, we need to zero the ModelView matrix before drawing anything: gl.glLoadIdentity();

Final Result

Transformation in OpenGL We use transformations to move objects in the scene and to move the camera's view of the scene. When OpenGL renders a mesh it multiplies all vertices with a matrix. The transformations manipulate the vertices in different ways by modifying this matrix.

Coordinate system OpenGL uses a right-handed coordinate system. The default alignment of the axis is (direction is negative towards positive): o X-axis: left to right. o Y-axis: down to up. o Z-axis: far to near.

glTranslate Used to move the object "as is" (same size, same orientation, different location). Recall: in the previous section we moved the camera by 4 units (on the z-axis) using:

// Translates 4 units into the screen gl.glTranslatef(0, 0, -4);

Each parameter specifies how much to move the object on each axis: x, y and z. Recall that translations are a commutative operation, i.e. the order of translations doesn't matter, as long as there are no other operations involved. When rotating along the translation the order of the operation DOES matter.

glRotate Performs rotations of objects. For example, to rotate by 90 degrees around the x-axis: gl.glRotatef(90f, 1.0f, 0.0f, 0.0f); First we specify the angle (in degrees), then the rotation axis (x, y, z coordinates). With no translation before it, the rotation is around the origin. Note that rotations are NOT a commutative operation. i.e. the order of rotations DOES matter.

If you do a translation on the mesh first and then rotate it, the translation is made on the current state of the mesh coordinate system and then rotated at the new location.

If you first rotate and the move the mesh it will be moved accordingly to its own rotated coordinate system.

glScale Scales the objects. gl.glScalef(2.0f, 2.0f, 2.0f);

As before, if you want to translate and scale the object- the order DOES matter. Think why!

glLoadIdentity Loads the unit matrix - "resets" the position.

glPushMatrix and glPopMatrix OpenGL has a stack of matrices in the memory. Enables us to save a setting and reverting back to it when needed. glPushMatrix() copies the current matrix and pushes it onto the matrix stack. glPopMatrix() pops a matrix from the stack and discards it. This mechanism is useful for drawing complex object, which are composed from smaller objects.

Putting it all together Draw 3 squares: A, B and C. Scale them so that B is 50% smaller than A, and C is 50% smaller than B. Rotate A counter-clockwise in the center of the screen. Rotate B clockwise around A, and finally rotate C clockwise around B and counter-clockwise in a high speed around it's own center.

public void onDrawFrame(GL10 gl) { // Clears the screen and depth buffer gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); // Replace the current matrix with the identity matrix gl.glLoadIdentity(); // Translates 10 units into the screen gl.glTranslatef(0, 0, -10); // SQUARE A // Save the current matrix gl.glPushMatrix(); // Rotate square A counter-clockwise gl.glRotatef(angle, 0, 0, 1); // Draw square A square.draw(gl); // Restore the last matrix gl.glPopMatrix();

// SQUARE B // Save the current matrix gl.glPushMatrix(); // Rotate square B before moving it, making it rotate around A gl.glRotatef(-angle, 0, 0, 1); // Move square B gl.glTranslatef(2, 0, 0); // Scale it to 50% of square A gl.glScalef(.5f, .5f, .5f); // Draw square B square.draw(gl); cont.

cont.

// SQUARE C // Save the current matrix gl.glPushMatrix(); // Make the rotation around B gl.glRotatef(-angle, 0, 0, 1); gl.glTranslatef(2, 0, 0); // Scale it to 50% of square B gl.glScalef(.5f, .5f, .5f); // Rotate around it's own center gl.glRotatef(angle*10, 0, 0, 1); // Draw square C square.draw(gl);

// Restore to the matrix as it was before C gl.glPopMatrix(); // Restore to the matrix as it was before B gl.glPopMatrix();

// Increse the angle angle++; }

Colors

Don't forget to add angle to MyRendere class.

OpenGL ES uses a color model called RGBA (Red, Green, Blue and Alpha ). Alpha is the transparency parameter. We will learn how to perform vertex coloring. Once you set a color, OpenGL will use it until you change it. So, remember that anything rendered after you set a color uses the same color and that this spans over frames and will not be reset in-between.

Flat Coloring Flat coloring is setting each vertex in the polygon to have the same color. This implies that the polygon will have a uniform color. Simply use: gl.glColor4f(0.5f, 0.5f, 1.0f, 1.0f); Call this function once, before drawing the square.

Smooth Coloring Smooth coloring is setting each vertex in the polygon to have it's own unique color. In this case, OpenGL will interpolate the colors inside the polygon. Call the glColor4f function before each vertex of the polygon.

float[] colors = { 1f, 0f, 0f, 1f, // vertex 0 red 0f, 1f, 0f, 1f, // vertex 1 green 0f, 0f, 1f, 1f, // vertex 2 blue 1f, 0f, 1f, 1f, // vertex 3 magenta }; // float has 4 bytes, colors (RGBA) * 4 bytes ByteBuffer cbb = ByteBuffer.allocateDirect(colors.length * 4); cbb.order(ByteOrder.nativeOrder()); colorBuffer = cbb.asFloatBuffer(); colorBuffer.put(colors); colorBuffer.position(0); // Enable the color array buffer to be used during rendering. gl.glEnableClientState(GL10.GL_COLOR_ARRAY); // Point out the where the color buffer is. gl.glColorPointer(4, GL10.GL_FLOAT, 0, colorBuffer); // Like before gl.glDrawElements( GL10.GL_TRIANGLES, indices.length, GL10.GL_UNSIGNED_SHORT, indexBuffer );

Conclusion You now know the basic concepts of OpenGL ES. You now can draw simple images and animation in your android application. For more complex images and animations, continue on learning the great features of OpenGL ES, such as complex meshes and textures Good Luck!