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

运行效果:

使用NDK和OpenGL ES 3.0来绘制一个三角形可以分为以下几个步骤: 1. 首先,创建一个安卓项目,并配置NDK环境。 2. 在项目的jni目录下,创建一个C/C++源文件triangle.c。 3. 在triangle.c文件中,引入相关的头文件,包括<jni.h>和<GLES3/gl3.h>。 4. 在triangle.c文件中,实现一个JNI函数,用于绘制三角形。函数的参数为Surface对象。 5. 在JNI函数中,通过EGL和GLES初始化OpenGL环境,并创建一个EGLSurface用于后续的绘制操作。 6. 在JNI函数中,创建一个顶点数组和顶点缓冲,并将顶点数据存入顶点缓冲。 7. 在JNI函数中,编写着色器代码,包括顶点着色器和片段着色器,并编译和链接它们。 8. 在JNI函数中,通过glClearColor()函数设置清空屏幕时的颜色。 9. 在JNI函数中,通过glClear()函数清空屏幕,并启用深度测试。 10. 在JNI函数中,通过glViewport()函数设置视口大小。 11. 在JNI函数中,通过glUseProgram()函数使用着色器程序。 12. 在JNI函数中,通过glVertexAttribPointer()函数设置顶点数据的属性,并启用顶点属性。 13. 在JNI函数中,通过glDrawArrays()函数绘制三角形。 14. 在JNI函数中,通过eglSwapBuffers()函数交换绘制的缓冲区。 15. 在JNI函数中,清理OpenGL环境,并释放资源。 16. 在Java层的MainActivity中,通过JNI调用C/C++函数进行绘制。 以上是绘制一个三角形的大致步骤。具体的细节和代码实现可以参考相关的OpenGL ES 3.0和NDK的文档和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值