1、OpenGL ES Tutorial for Android Part I Im going to write a couple of tutorials on using OpenGL ES on Android phones. The theory of OpenGL ES is the same on different devices so it should be quite easy to convert them to another platform.I cant always remember where I found particular info so I might
2、not always be able to give you the right reference. If you feel that I have borrowed stuff from you but have forgotten to add you as a reference, please e-mail me.In the code examples I will have two different links for each function. The actual function will be linked to the android documentation a
3、nd after that I will also link the OpenGL documentations. Like this:gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); / OpenGL docs.So, lets start.In this tutorial I will show you how to set up your OpenGL ES view thats always a good place to start. Setting up an OpenGL ES ViewSetting up a OpenGL view has ne
4、ver been hard and on Android it is still easy. There really are only two things you need to get started.GLSurfaceViewGLSurfaceView is a API class in Android 1.5 that helps you write OpenGL ES applications. Providing the glue code to connect OpenGL ES to the View system. Providing the glue code to ma
5、ke OpenGL ES work with the Activity life-cycle. Making it easy to choose an appropriate frame buffer pixel format. Creating and managing a separate rendering thread to enable smooth animation. Providing easy-to-use debugging tools for tracing OpenGL ES API calls and checking for errors.If you want t
6、o get going fast with your OpenGL ES application this is where you should start.The only function you need to call on is: public void setRenderer(GLSurfaceView.Renderer renderer)Read more at: GLSurfaceViewGLSurfaceView.RendererGLSurfaceView.Renderer is a generic render interface. In your implementat
7、ion of this renderer you should put all your calls to render a frame.There are three functions to implement:/ Called when the surface is created or recreated.public void onSurfaceCreated(GL10 gl, EGLConfig config) / Called to draw the current frame.public void onDrawFrame(GL10 gl)/ Called when the s
8、urface changed size.public void onSurfaceChanged(GL10 gl, int width, int height)onSurfaceCreatedHere its a good thing to setup things that you dont change so often in the rendering cycle. Stuff like what color to clear the screen with, enabling z-buffer and so on.onDrawFrameHere is where the actual
9、drawing take place.onSurfaceChangedIf 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.Read more at: GLSurfaceView.RendererPutting it togetherFirst we create our activity, we keep it clean
10、 and simple.package se.jayway.opengl.tutorial;import android.app.Activity;import android.opengl.GLSurfaceView;import android.os.Bundle;public class TutorialPartI extends Activity /* Called when the activity is first created. */Overridepublic void onCreate(Bundle savedInstanceState) super.onCreate(sa
11、vedInstanceState);GLSurfaceView view = new GLSurfaceView(this);view.setRenderer(new OpenGLRenderer();setContentView(view);Our renderer takes little bit more work to setup, look at it and I will explain the code a bit more. package se.jayway.opengl.tutorial;import javax.microedition.khronos.egl.EGLCo
12、nfig;import javax.microedition.khronos.opengles.GL10;import android.opengl.GLU;import android.opengl.GLSurfaceView.Renderer;public class OpenGLRenderer implements Renderer /* (non-Javadoc)* see* android.opengl.GLSurfaceView.Renderer#onSurfaceCreated(javax.* microedition.khronos.opengles.GL10, javax.
13、microedition.khronos.* egl.EGLConfig)*/public void onSurfaceCreated(GL10 gl, EGLConfig config) / Set the background color to black ( rgba ).gl.glClearColor(0.0f, 0.0f, 0.0f, 0.5f); / OpenGL docs./ Enable Smooth Shading, default not really needed.gl.glShadeModel(GL10.GL_SMOOTH);/ OpenGL docs./ Depth
14、buffer setup.gl.glClearDepthf(1.0f);/ OpenGL docs./ Enables depth testing.gl.glEnable(GL10.GL_DEPTH_TEST);/ OpenGL docs./ The type of depth testing to do.gl.glDepthFunc(GL10.GL_LEQUAL);/ OpenGL docs./ Really nice perspective calculations.gl.glHint(GL10.GL_PERSPECTIVE_CORRECTION_HINT, / OpenGL docs.G
15、L10.GL_NICEST);/* (non-Javadoc)* see* android.opengl.GLSurfaceView.Renderer#onDrawFrame(javax.* microedition.khronos.opengles.GL10)*/public void onDrawFrame(GL10 gl) / Clears the screen and depth buffer.gl.glClear(GL10.GL_COLOR_BUFFER_BIT | / OpenGL docs.GL10.GL_DEPTH_BUFFER_BIT);/* (non-Javadoc)* s
16、ee* android.opengl.GLSurfaceView.Renderer#onSurfaceChanged(javax.* microedition.khronos.opengles.GL10, int, int)*/public void onSurfaceChanged(GL10 gl, int width, int height) / Sets the current view port to the new size.gl.glViewport(0, 0, width, height);/ OpenGL docs./ Select the projection matrixg
17、l.glMatrixMode(GL10.GL_PROJECTION);/ OpenGL docs./ Reset the projection matrixgl.glLoadIdentity();/ OpenGL docs./ Calculate the aspect ratio of the windowGLU.gluPerspective(gl, 45.0f,(float) width / (float) height,0.1f, 100.0f);/ Select the modelview matrixgl.glMatrixMode(GL10.GL_MODELVIEW);/ OpenGL
18、 docs./ Reset the modelview matrixgl.glLoadIdentity();/ OpenGL docs.FullscreenJust add this lines in the OpenGLDemo class and you will get fullscreen.public void onCreate(Bundle savedInstanceState) super.onCreate(savedInstanceState);this.requestWindowFeature(Window.FEATURE_NO_TITLE); / (NEW)getWindo
19、w().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN); / (NEW). / 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.OpenGL ES Tutorial for Android Part II Previous t
20、utorial was all about setting up the GLSurfaceView. Be sure to read it beacuse its a really importent one to be able to continue.Building a polygonIn this tutorial we will render our first polygon.3D models are built up with smaller elements (vertices, edges, faces, and polygons) which can be manipu
21、lated individually.VertexA vertex (vertices in plural) is the smallest building block of 3D model. A vertex is a point where two or more edges meet. In a 3D model a vertex can be shared between all connected edges, paces and polygons. A vertex can also be a represent for the position of a camera or
22、a light source. You can see a vertex in the image below marked in yellow.To define the vertices on android we define them as a float array that we put into a byte buffer to gain better performance. Look at the image to the right and the code below to match the vertices marked on the image to the cod
23、e. private float vertices = -1.0f, 1.0f, 0.0f, / 0, Top Left-1.0f, -1.0f, 0.0f, / 1, Bottom Left1.0f, -1.0f, 0.0f, / 2, Bottom Right1.0f, 1.0f, 0.0f, / 3, Top Right;/ a float is 4 bytes, therefore we multiply the number if vertices with 4.ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length *
24、4);vbb.order(ByteOrder.nativeOrder();FloatBuffer vertexBuffer = vbb.asFloatBuffer();vertexBuffer.put(vertices);vertexBuffer.position(0);Dont forget that a float is 4 bytes and to multiply it with the number of vertices to get the right size on the allocated buffer. OpenGL ES have a pipeline with fun
25、ctions to apply when you tell it to render. Most of these functions are not enabled by default so you have to remember to turn the ones you like to use on. You might also need to tell these functions what to work with. So in the case of our vertices we need to tell OpenGL ES that its okay to work wi
26、th the vertex buffer we created we also need to tell where it is./ Enabled the vertex buffer for writing and to be used during rendering.gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);/ OpenGL docs./ Specifies the location and data format of an array of vertex/ coordinates to use when rendering.gl.glV
27、ertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); / OpenGL docs.When you are done with the buffer dont forget to disable it./ Disable the vertices buffer.gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);/ OpenGL docs.EdgeEdge is a line between two vertices. They are border lines of faces and polygons. I
28、n a 3D model an edge can be shared between two adjacent faces or polygons. Transforming an edge affects all connected vertices, faces and polygons. In OpenGL ES you dont define the edges, you rather define the face by giving them the vertices that would build up the three edges. If you would like mo
29、dify an edge you change the two vertices that makes the edge. You can see an edge in the image below marked in yellow.FaceFace is a triangle. Face is a surface between three corner vertices and three surrounding edges. Transforming a face affects all connected vertices, edges and polygons.The order
30、does matter.When winding up the faces its important to do it in the right direction because the direction defines what side will be the front face and what side will be the back face. Why this is important is because to gain performance we dont want to draw both sides so we turn off the back face. S
31、o its a good idea to use the same winding all over your project. It is possible to change what direction that defines the front face with glFrontFace.gl.glFrontFace(GL10.GL_CCW); / OpenGL docsTo make OpenGL skip the faces that are turned into the screen you can use something called back-face culling
32、. What is does is determines whether a polygon of a graphical object is visible by checking if the face is wind up in the right order.gl.glEnable(GL10.GL_CULL_FACE); / OpenGL docsIts ofcource possible to change what face side should be drawn or not.gl.glCullFace(GL10.GL_BACK); / OpenGL docsPolygonTi
33、me to wind the faces, remember we have decided to go with the default winding meaning counter-clockwise. Look at the image to the right and the code below to see how to wind up this square. private short indices = 0, 1, 2, 0, 2, 3 ;To gain some performance we also put this ones in a byte buffer./ sh
34、ort is 2 bytes, therefore we multiply the number if vertices with 2.ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);ibb.order(ByteOrder.nativeOrder();ShortBuffer indexBuffer = ibb.asShortBuffer();indexBuffer.put(indices);indexBuffer.position(0);Dont forget that a short is 2 bytes and
35、to multiply it with the number of indices to get the right size on the allocated buffer. RenderTime to get something on the screen, there is two functions used to draw and we have to decide which one to use.The two functions are:public abstract void glDrawArrays(int mode, int first, int count) / Ope
36、nGL docsglDrawArrays draws the vertices in that order they are specified in the construction of our verticesBuffer.public abstract void glDrawElements(int mode, int count, int type, / OpenGL docsBuffer indices) glDrawElements need a little bit more to be able to draw. It needs to know the order whic
37、h to draw the vertices, it needs the indicesBuffer.Since we already created the indicesBuffer Im guessing that you figured out thats the way we are going.What is common for this functions is that they both need to know what it is they should draw, what primitives to render. Since there is some vario
38、us ways to render this indices and some of them are good to know about for debugging reasons. Ill go through them all.What primitives to renderGL_POINTSDraws individual points on the screen.GL_LINE_STRIPSeries of connected line segments.GL_LINE_LOOPSame as above, with a segment added between last an
39、d first vertices.GL_LINESPairs of vertices interpreted as individual line segments.GL_TRIANGLESTriples of vertices interpreted as triangles.GL_TRIANGLE_STRIPDraws a series of triangles (three-sided polygons) using vertices v0, v1, v2, then v2, v1, v3 (note the order), then v2, v3, v4, and so on. The
40、 ordering is to ensure that the triangles are all drawn with the same orientation so that the strip can correctly form part of a surface.GL_TRIANGLE_FANSame as GL_TRIANGLE_STRIP, except that the vertices are drawn v0, v1, v2, then v0, v2, v3, then v0, v3, v4, and so on.I think the GL_TRIANGLES is th
41、e easiest to use so we go with that one for now.Putting it all togetterSo lets putting our square together in a class.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.openg
42、les.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 Left1.0f, -1.0f, 0.0f, / 2, Bottom Right1.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 buf
43、fer.private FloatBuffer vertexBuffer;/ Our index buffer.private ShortBuffer indexBuffer;public Square() / a float is 4 bytes, therefore we multiply the number if/ vertices with 4.ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);vbb.order(ByteOrder.nativeOrder();vertexBuffer = vbb.asFl
44、oatBuffer();vertexBuffer.put(vertices);vertexBuffer.position(0);/ short is 2 bytes, therefore we multiply the number if/ vertices with 2.ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);ibb.order(ByteOrder.nativeOrder();indexBuffer = ibb.asShortBuffer();indexBuffer.put(indices);indexBu
45、ffer.position(0);/* This function draws our square on screen.* param gl*/public void draw(GL10 gl) / Counter-clockwise winding.gl.glFrontFace(GL10.GL_CCW); / OpenGL docs/ Enable face culling.gl.glEnable(GL10.GL_CULL_FACE); / OpenGL docs/ What faces to remove with the face culling.gl.glCullFace(GL10.
46、GL_BACK); / OpenGL docs/ Enabled the vertices buffer for writing and to be used during/ rendering.gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);/ OpenGL docs./ Specifies the location and data format of an array of vertex/ coordinates to use when rendering.gl.glVertexPointer(3, GL10.GL_FLOAT, 0, / Ope
47、nGL docsvertexBuffer);gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,/ OpenGL docsGL10.GL_UNSIGNED_SHORT, indexBuffer);/ Disable the vertices buffer.gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); / OpenGL docs/ Disable face culling.gl.glDisable(GL10.GL_CULL_FACE); / OpenGL docsWe have to initia
48、lize our square in the OpenGLRenderer 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 | / OpenGL docs.GL10.GL_DEPTH_BUFFER_BIT);/ D
49、raw our square.square.draw(gl); / ( NEW )If you run the application now the screen is still black. Why? Because OpenGL ES render from where the current position is, that by default is at point: 0, 0, 0 the same position that the view port is located. And OpenGL ES dont render the things that are too close to the view port. The solution to this is to move the draw position a few steps into the screen before rendering the squ