用OpenGL ES绘制一个红色三角形(二)

以下均转自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
			
		}
		
	}
}

运行效果:

你可能感兴趣的:(Android)