Upload
carlos-salinas
View
157
Download
8
Embed Size (px)
Citation preview
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:
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);
}
}
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.
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
}
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.
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);
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
);
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.
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 )
}
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:
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);
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.
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++;
}
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
);