以下均转自Android游戏编程入门经典,转载请标明出处首先,定义一个三角形的组成部分:
一个三角形由3个点组成;
每个点都称为顶点;
一个顶点对应3D空间中的一个位置;
3D空间中的一个位置由3个浮点数表示,分别是x、y、z坐标;
一个顶点可以有一些附加属性,例如颜色或纹理坐标,这些属性也由浮点数表示。
OpenGL ES采用数组的形式定义三角形。但是,OpenGL ES实际上是通过C API提供接口,因此无法使用标准Java数组。这里使用的替代方法是采用Java的NIO缓冲区,该缓冲区是一段连续字节的内存块。
为了保证完全准确,需要使用直接NIO缓冲区。这意味着这块内存不是在虚拟机的堆内存中,而是在主机的堆内存中。为了构造这样的一个直接NIO缓冲区,可以使用下面的代码:
ByteBuffer buffer = ByteBuffer.allocateDirect(NUMBER_OF_BYTES);
buffer.order(ByteOrder.nativeOrder());
上面的代码将分配一个总长NUMBER_OF_BYTES字节的ByteBuffer,并且保证字节的顺序等于底层的CPU的字节顺序。一个NIO缓冲区有3个属性:
Capacity: 缓冲区可以容纳的元素总个数;
Position: 下一个元素将要写入或读取的当前位置;
Limit: 最后一个元素的索引加一。
一个缓冲区的容量实际上是它的大小。ByteBuffer的容量是字节数。Position和Limit属性可以看作是在缓冲区中从位置开始到界限(不包括在内)位置定义的一段。
既然需要使用浮点数表示顶点,那么最好不用处理字节。可以将ByteBuffer实例转换成FloatBuffer实例,这样就可以使用浮点数了:
FloatBuffer floatBuffer = buffer.asFloatBuffer();
在FloatBuffer中,容量,位置和界限都是浮点型。这些缓冲区的使用方式也很简单--如下所示:
float[] vertices = {...};
floatBuffer.clear();
floatBuffer.put(vertices);
floatBuffer.flip(); // 将位置值和界限值交换
发送顶点给OpenGL ES:
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * 2 * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vertices = byteBuffer.asFloatBuffer();
vertices.put(new float[]{
0.0f, 0.0f,
319.0f, 0.0f,
160.0f, 479.0f
});
vertices.flip();
注意:只能够用x、y坐标指定顶点, OpenGL ES会自动将Z坐标设置为0.
一旦NIO缓冲区可用,OpenGL ES便可根据它的当前状态进行绘制(即视口和投影矩阵)。
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertices);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
glEnableClientState()方法需要告诉OpenGL ES这些需要绘制的顶点都有一个位置。
glVertexPointer()方法,需要告诉OpenGL ES从何处取得顶点的位置和其他一些附加信息。第一个参数告诉OpenGL ES每个顶点的位置是由x、y坐标组成。如果需要指定x、y和z值,那么需要将参数设置为3.第二个参数告诉OpenGL ES坐标使用的数据类型。在这里是GL10.GL_FLOAT,这说明坐标值使用的是浮点型,占用4个字节。第三个参数是步长,告诉OpenGL顶点位置之间的字节距离。本例中,步长为零,说明位置值是紧密封装的。最后一个参数是FloatBuffer,它代表原生堆中的一个内存块,并有一个起始地址。
当需要绘制缓冲区中的内容时,OpenGL ES将从该位置读取这些顶点值。
最后调用的是glDrawArrays()方法,它将绘制一个三角形。第一个参数指明了将要绘制的物体类型。本例中通过GL10.GL_TRIANGLES指明将渲染三角形。下一个参数是与顶点指针指向第一个顶点相关联的偏移量。该偏移量是以顶点为单位进行测量的,而不是以字节或浮点为单位。如果指定了多个三角形,那么可以使用这个偏移量渲染三角形列表的一个子集。最后一个参数告诉OpenGL ES渲染时使用的顶点数量。
每一个顶点可能拥有多个属性,不仅仅只是位置。其他的属性可能是顶点颜色。
在没有指定顶点属性时,OpenGL ES会将这些属性设为默认值。绝大多数默认值可直接设置。例如,当需要为绘制的所有顶点设置默认颜色时,使用如下方法;
GL10.glColor4f(float r, float g, float b, float a)
OpenGL ES默认颜色值从(1, 1, 1, 1 )开始,这意味着从白色开始。
FirstTriangleTest.java
package org.example.androidgames.glbasics;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import javax.microedition.khronos.opengles.GL10;
import org.example.androidgames.framework.Game;
import org.example.androidgames.framework.Screen;
import org.example.androidgames.framework.impl.GLGame;
import org.example.androidgames.framework.impl.GLGraphics;
public class FirstTriangleTest extends GLGame {
@Override
public Screen getStartScreen() {
// TODO Auto-generated method stub
return new FirstTriangleScreen(this);
}
class FirstTriangleScreen extends Screen{
GLGraphics glGraphics;
FloatBuffer vertices;
public FirstTriangleScreen(Game game){
super(game);
glGraphics = ((GLGame)game).getGLGraphics();
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * 2 * 4);
byteBuffer.order(ByteOrder.nativeOrder());
vertices = byteBuffer.asFloatBuffer();
vertices.put(new float[]{
0.0f, 0.0f,
319.0f, 0.0f,
160.0f, 479.0f
});
vertices.flip();
}
@Override
public void update(float deltaTime) {
// TODO Auto-generated method stub
game.getInput().getTouchEvents();
game.getInput().getKeyEvents();
}
@Override
public void present(float deltaTime) {
// TODO Auto-generated method stub
GL10 gl = glGraphics.getGL();
gl.glViewport(0, 0, glGraphics.getWidth(), glGraphics.getHeight());
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glMatrixMode(GL10.GL_PROJECTION);
gl.glLoadIdentity();
gl.glOrthof(0, 320, 0, 480, 1, -1);
gl.glColor4f(1, 0, 0, 1);
gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
gl.glVertexPointer(2, GL10.GL_FLOAT, 0, vertices);
gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
}
@Override
public void pause() {
// TODO Auto-generated method stub
}
@Override
public void resume() {
// TODO Auto-generated method stub
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
}
}