16
OpenGL A cross-platform API for writing 2D and 3D graphic applications Developed by SGI in 1992 Used mainly in video games, 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:

OpenGL for Android Developers Tutorial

Embed Size (px)

Citation preview

Page 1: OpenGL for Android Developers Tutorial

OpenGL

A cross-platform API for writing 2D and 3D graphic applications

Developed by SGI in 1992

Used mainly in video games, 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:

Page 2: OpenGL for Android Developers Tutorial

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.

o onDrawFrame - Called to draw the current frame. The actual drawing takes place here.

o 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);

}

}

Page 3: OpenGL for Android Developers Tutorial

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.

Page 4: OpenGL for Android Developers Tutorial

Full Screen

Just add these lines in theMyActivity class and you will get fullscreen:

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.

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();

}

}

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

}

Page 5: OpenGL for Android Developers Tutorial

GL10 Class

Used for actually drawing the objects on the screen.

o For example, glDrawElements, glRotate

If you’ve 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.

Page 6: OpenGL for Android Developers Tutorial

Defining the Vertices

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

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.

Defining the Indices Buffer

The indices buffer contains the indices of the defined vertices in the order they should be drawn on

the screen.

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, 1.0f, 0.0f, // 3, Top Right

};

// 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);

// 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);

Page 7: OpenGL for Android Developers Tutorial

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

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:

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:

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);

gl.glDrawElements(

GL10.GL_TRIANGLES,

indices.length,

GL10.GL_UNSIGNED_SHORT,

indexBuffer

);

Page 8: OpenGL for Android Developers Tutorial
Page 9: OpenGL for Android Developers Tutorial

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.0f, -1.0f, 0.0f, // 1, Bottom Left

1.0f, -1.0f, 0.0f, // 2, Bottom Right

1.0f, 1.0f, 0.0f, // 3, Top Right

};

// 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.

Page 10: OpenGL for Android Developers Tutorial

We have to initialize our square in the MyRenderer class.

And in the draw function call on the square to draw.

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);

}

}

// Initialize our square

Square square = new Square();

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 )

}

Page 11: OpenGL for Android Developers Tutorial

Doesn’t 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 Doesn’t 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:

Page 12: OpenGL for Android Developers Tutorial

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.

// Translates 4 units into the screen

gl.glTranslatef(0, 0, -4);

Page 13: OpenGL for Android Developers Tutorial

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.

Page 14: OpenGL for Android Developers Tutorial

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.

Page 15: OpenGL for Android Developers Tutorial

Don't forget to add angle to MyRendere class.

Colors

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.

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++;

}

Page 16: OpenGL for Android Developers Tutorial

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.

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!

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

);