翻译:OpenGL ES Tutorial for Android – Part II – Building a polygon
原文地址: 点击这里========================以下为译文================================================
之前的教程都是关于设置GLSurfaceView。确保你阅读过它,因为对于接下来的那是非常重要的。
建立一个多边形
在这个教程中,我们将会渲染第一个多边形。
3D模型是由更小的可以单独操作的元素构成的(点、线/边、面和多边形)。
点
点是3D模型中最小的构件。点是2条或更多条线相交的地方。在3D模型中点可以被所有相邻的线、面(原文
paces,如不正确请联系我)和多边形共享。点也可以表现为视角或者光源。你可以在下图看到点的示意图,黄色表示。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/vertex.png)
为了在Android中定义点,我们用浮点数组表示,并且将数组放入一个字节缓冲区来获得较好的性能。例如,下面图上的点将会用图下方的代码表示出来。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/12/poly1.png)
private float vertices[] = {
-1.0f, 1.0f, 0.0f, // 0, 左上
-1.0f, -1.0f, 0.0f, // 1, 左下
1.0f, -1.0f, 0.0f, // 2, 右下
1.0f, 1.0f, 0.0f, // 3, 右上
};
// 浮点型占用4字节,所以我们要将点的数目乘以4.
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
FloatBuffer vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
别忘记浮点型占用4字节,所以我们要将点的数目乘以4来分配正确的缓冲区。
OpenGL ES 有一系列的方法可以套用,当你想要它
渲染时。默认情况下大多数的方法并没有启用,所以你要记得去启用你想要的。也许你需要知道这些方法的功能是什么。现在这个例子中我们要告诉OpenGL ES 去处理刚才创建的点(点的缓冲区),当然我们得告诉它那些点在哪里。
// 设置点的缓冲区可,将在渲染时用到。
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// OpenGL docs.
// 指定点的数组和它的类型(浮点)。
// 渲染时坐标。
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertexBuffer); // OpenGL docs.
当你用完了缓冲区后别忘了关闭它。
//关闭缓冲区
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);// OpenGL docs.
线/边
边是两点之间的线(我们话就是两点确定一直线)。他是面和多边形的边。在3D模型中边可以被相邻的面和多边形共享(面相交只有一条直线)。改变一条线将会影响到所有相连的点、面和多边形。在OpenGL ES 中不定义线,而是通过给定3点的方式来定义面来确定3条直线。如果你想要修改一条线,那么你改变定义这条线的2个点。你可以在下图中看见一条线的示意,黄色表示。![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/edge.png)
面
面就是一个三角形。面是由三个不在一条直线上的点形成的表面,由这3个点形成的线包围。改变一个面会影响到所有相连的点、线和多边形。一样,面在下面的图中由黄色表示。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/face.png)
顺序是有说法的
定义一个面时,弄对方向是很重要的,因为方向决定了哪个是正面,哪个是反面。为什么这个很重要?因为我们想要获得好的性能,所以我们不要把正反面都画出来---关掉背面好了。所以在项目中最好是用同样的构建方式。可以用glFrontFace来改变正面。
gl.glFrontFace(GL10.GL_CCW); // OpenGL docs
为了让OpenGL 略过那些面向里的面,我们用些可以叫做背面删除的东西。它的作用是用来决定图形对象中一个多边形在当前构建方式下是否可见。
gl.glEnable(GL10.GL_CULL_FACE); // OpenGL docs
当然了,我们可以决定哪个面画或者不画。
gl.glCullFace(GL10.GL_BACK); // OpenGL docs
多边形
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/polygon.png)
终于改说多边形了,记住我们已经决定按照默认的构建方式继续下去了,也就是逆时针方向。看着下图和图下的代码来了解如何构建途中的正方形。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/12/poly2.png)
private short[] indices = { 0, 1, 2, 0, 2, 3 };
同样,为了性能,我们将这些点放入字节缓冲区。
// short类型是2字节,所以我们用点的数目乘以2。
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
ShortBuffer indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
别忘记short类型占用2字节,所以我们要将点的数目乘以2来分配正确的缓冲区。
渲染
是时候在屏幕上显示点什么了,这儿有2个方法可以用来画图,我们得选一个。
这两个方法是:glDrawArrays
public abstract void glDrawArrays(int mode, int first, int count) // OpenGL docs
glDrawArrays按照我们在缓冲区建立的点的顺序来绘图。
public abstract void glDrawElements(int mode, int count, int type, Buffer indices)
当我完成了缓冲索引,我想你也就该明白是什么回事了。
这两个方法的共同点就是它们都需要知道他们该画什么。因为有多种方法去按照顺序渲染这个缓冲区中的点,并且其中一些对于调试有好处,所以我将介绍所有的方法。
该画什么样的几何体
GL_POINTS
在屏幕上画单独的点。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_points.jpg)
GL_LINE_STRIP
一系列相连的线段。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_line_strip.jpg)
GL_LINE_LOOP
和上面相同,不过在首末点之间增加了一条线段。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_line_loop.jpg)
GL_LINES
单独的,不相连线段。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_lines.jpg)
GL_TRIANGLES
不相连的三角形。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_triangles.jpg)
GL_TRIANGLE_STRIP
按照v0、v1、v2 然后 v2、v1、v3 接着v2、v3、v4 等等的顺序,画一系列的三角形(三面的多边形)。这样的顺序是为了确保所有的三角形都是按照一个方向画的,那样线段就能正确的构成这面的一部分。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_triangle_strip.jpg)
GL_TRIANGLE_FAN
和GL_TRIANGLE_STRIP相同,不过是按照先v0、v1、v2然后v0、v2、v3接着v0、v3、v4等等顺序。
![](http://blog.jayway.com/wordpress/wp-content/uploads/2009/11/Gl_triangle_fan.jpg)
我想GL_TRIANGLES是最容易用的了,所以就从这里下手。
把所有的都合起来
我们在类中把正方形都放一起。
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 {
// 我们的一堆点.
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
};
// 按照我们的顺序去链接他们.
private short[] indices = { 0, 1, 2, 0, 2, 3 };
// 点缓冲区.
private FloatBuffer vertexBuffer;
// 索引缓冲.
private ShortBuffer indexBuffer;
public Square() {
//浮点4字节,说过了哦
ByteBuffer vbb = ByteBuffer.allocateDirect(vertices.length * 4);
vbb.order(ByteOrder.nativeOrder());
vertexBuffer = vbb.asFloatBuffer();
vertexBuffer.put(vertices);
vertexBuffer.position(0);
// short类型2字节
ByteBuffer ibb = ByteBuffer.allocateDirect(indices.length * 2);
ibb.order(ByteOrder.nativeOrder());
indexBuffer = ibb.asShortBuffer();
indexBuffer.put(indices);
indexBuffer.position(0);
}
/**
* 这个方法将在屏幕上画个正方形.
* @param gl
*/
public void draw(GL10 gl) {
// 逆时针方向.
gl.glFrontFace(GL10.GL_CCW); // OpenGL docs
// 开启面删除.
gl.glEnable(GL10.GL_CULL_FACE); // OpenGL docs
// 决定不画哪个面.
gl.glCullFace(GL10.GL_BACK); // OpenGL docs
// 开启点缓冲.
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);// OpenGL docs.
// 指定点坐标的位置和类型.
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, // OpenGL docs
vertexBuffer);
gl.glDrawElements(GL10.GL_TRIANGLES, indices.length,// OpenGL docs
GL10.GL_UNSIGNED_SHORT, indexBuffer);
// 关闭缓冲.
gl.glDisableClientState(GL10.GL_VERTEX_ARRAY); // OpenGL docs
// 关闭面删除.
gl.glDisable(GL10.GL_CULL_FACE); // OpenGL docs
}
}
我们需要在OpenGLRenderer类中初始化正方形。
Square square = new Square();
然后在绘图方法中请求正方形绘制。
public void onDrawFrame(GL10 gl) {
// 清除屏幕和缓冲.
gl.glClear(GL10.GL_COLOR_BUFFER_BIT | // OpenGL docs.
GL10.GL_DEPTH_BUFFER_BIT);
// 画正方形.
square.draw(gl); // ( NEW )
}
如果你现在运行程序屏幕仍然是黑色。为啥?因为OpenGL ES 从当前位置渲染,默认当前位置是在:0,0,0 和视点是同一个位置。并且,OpenGL ES 不去渲染那些离视点很近的东西。解决办法是在渲染正方形之前,把绘图区域往屏幕里(深度)移一点:
// 往里移动4个单位.
gl.glTranslatef(0, 0, -4); // OpenGL docs
我会在下一个教程说到不同的移动。
再次运行程序,你会看到正方形被画好了------但是飞快的往屏幕里移动。OpenGL ES 没有重置2帧之间的点,这些由我们自己搞定。
// 重置当前的矩阵为单位矩阵
gl.glLoadIdentity(); // OpenGL docs
如果现在你运行程序,你会看到正方形固定在那里。
引用
教程中引用的内容从以下地方收集:
你可以从这里下载本章教程的源码:
Tutorial_Part_II
也可以从这里:
code.google.com