OpenGL ES Tutorial for Android – Part II – Building a polygon

Previous tutorial was all about setting up the GLSurfaceView. Be sure to read it beacuse it's a really importent one to be able to continue.

 

根据openGL ES的介绍, 关于 GL_TRIANGLE_STRIP的顺序是当n是奇数时,构成一个三角形的三个点的顺序是n,n+1,n+2.

当n是偶数时, n+1, n, n+2。 n是从1开始的,最多可以有N-2个三角形。例如,vertex indices {1,2,3,4}的N为4, 那么我们可以建立N-2共2个三角形,第一个是1,2,3. 第二个是3,2,4. 而 GL_TRIANGLE_FAN的顺序是1, n+1,n+2.

 

利用indices数组,我们可以使用尽可能小的vertex数组,但是vertex的排列最好是Z顺序,例如,我们要定义一个正方形的四个点,我们应该先定义左上,右上,左下,右下,这样当我们使用GL_TRIANGLE_STRIP的方式连接顶点时会把逆时针画正面显示出来。

 

 

Building a polygon

In this tutorial we will render our first polygon.

3D models are built up with smaller elements (vertices, edges, faces, and polygons) which can be manipulated individually.

Vertex

A 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 a light source. You can see a vertex in the image below marked in yellow.

Vertex
Vertices for a square.

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

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

// 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());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);

Don't 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 functions 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 it’s okay to work with 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.glVertexPointer (3, GL10.GL_FLOAT, 0, vertexBuffer); // OpenGL docs.

When you are done with the buffer don't forget to disable it.

// Disable the vertices buffer.
gl.glDisableClientState (GL10.GL_VERTEX_ARRAY);// OpenGL docs.

Edge

Edge is a line between two vertices. They are border lines of faces and polygons. In 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 don't define the edges, you rather define the face by giving them the vertices that would build up the three edges. If you would like modify an edge you change the two vertices that makes the edge. You can see an edge in the image below marked in yellow.

Edge

Face

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

Face
The order does matter.

When winding up the faces it's 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 don't want to draw both sides so we turn off the back face. So it's 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 docs

To make OpenGL skip the faces that are turned into the screen you can use something called back-face culling. 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 docs

It's ofcource possible to change what face side should be drawn or not.

 gl.glCullFace
(GL10.GL_BACK); // OpenGL docs

Polygon

Polygon
Indices for a square.

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

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

Don't forget that a short is 2 bytes and to multiply it with the number of indices to get the right size on the allocated buffer.

Render

Time 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) // OpenGL docs

glDrawArrays 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 docs

Buffer indices)

glDrawElements need a little bit more to be able to draw. It needs to know the order which to draw the vertices, it needs the indicesBuffer.

Since we already created the indicesBuffer I'm guessing that you figured out that's 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 various ways to render this indices and some of them are good to know about for debugging reasons. I'll go through them all.

What primitives to render

GL_POINTS

Draws individual points on the screen.

Gl_points
GL_LINE_STRIP

Series of connected line segments.

Gl_line_strip
GL_LINE_LOOP

Same as above, with a segment added between last and first vertices.

Gl_line_loop
GL_LINES

Pairs of vertices interpreted as individual line segments.

Gl_lines
GL_TRIANGLES

Triples of vertices interpreted as triangles.

Gl_triangles
GL_TRIANGLE_STRIP

Draws 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 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_strip
GL_TRIANGLE_FAN

Same as GL_TRIANGLE_STRIP, except that the vertices are drawn v0, v1, v2, then v0, v2, v3, then v0, v3, v4, and so on.

Gl_triangle_fan

I think the GL_TRIANGLES is the easiest to use so we go with that one for now.

Putting it all togetter

So let's 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.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() {
// 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.asFloatBuffer();
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);
indexBuffer.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.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, // OpenGL docs
vertexBuffer);

gl.glDrawElements (GL10.GL_TRIANGLES, indices.length,// OpenGL docs
GL10.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 docs
}

}

We have to initialize 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);

// Draw 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 don’t 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 square:

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

I will talk about the different transformations in the next tutorial.

Run the application again and you will see that the square is drawn but quickly moves further and further into the screen. OpenGL ES doesn’t reset the drawing point between the frames that you will have to do yourself:

// Replace the current matrix with the identity matrix
gl.glLoadIdentity (); // OpenGL docs

Now if you run the application you will see the square on a fixed position.

References

The info used in this tutorial is collected from:
Android Developers
OpenGL ES 1.1 Reference Pages

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值