Android OpenGL ES (2) -- 纹理

 五颜六色的立方体并算是什么太有意思的事情,看上去太假,没什么感觉。 解决办法就是纹理贴图了。

OpenGL 中使用纹理要先用 glEnable 来启用相关功能
gl.glEnable(GL10.GL_TEXTURE_2D);
然后先准备一张图片作为纹理贴图,需要注意的是,有些设备对图片的尺寸有要求,我手上这个G7就只支持方形的纹理图片,其它可能的限制还有长宽必须是 2 的 n 次幂,最大尺寸不能超过256或1024等等。弄好图片之后,把它放到 res/drawable 文件夹中,然后通过资源加载到纹理 。
	private void loadTexture(GL10 gl) {
		InputStream bitmapStream = null;
		Bitmap bitmap = null;
		try {
			bitmapStream = context.getResources().openRawResource(R.drawable.tree);
			bitmap = BitmapFactory.decodeStream(bitmapStream);
 
			int[] textures = new int[1];
			gl.glGenTextures(1, textures, 0);
			texture = textures[0];
 
			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
 
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
					GL10.GL_NEAREST);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
					GL10.GL_LINEAR);
 
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
					GL10.GL_REPEAT);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
					GL10.GL_REPEAT);
 
			GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
 
		} finally {
			if (bitmap != null)
				bitmap.recycle();
 
			if (bitmapStream != null)
				try {
					bitmapStream.close();
				} catch (IOException e) {
				}
		}
	}

BitmapFactory.decodeStream 从流中加载并解码图片并生成Bitmap对象。令人不解的是更解法的 decodeResource 方法在虚拟机中工作良好,但到我的手机中就不行了。

glGenTextures 生成一组纹理并把纹理的ID存入数组参数中。这里只生成了一个。

glBindTexture 将指定ID的纹理绑定到指定的目标中去,接下来对目录所作的操作将针对该纹理进行。

glTexParameterf 设置纹理参数,这里设置了4个参数:

GL_TEXTURE_MIN_FILTER 和 GL_TEXTURE_MAG_FILTER 指定纹理在被缩小或放大时使用的过滤方式,LINEAR (线性插值?)效果要比 NEAREST(最近点?)好但也更需要更多运算。
GL_TEXTURE_WRAP_S 和 GL_TEXTURE_WRAP_T 表示当贴图坐标不在 0.0-1.0 之间时如何处理,这里使用 REPEAT 即平铺贴图。

GLUtils.texImage2D 辅助方法用于将 Bitmap 对象设置到纹理目录中,设置完后 Bitmap 对象即不再需要,可以丢弃。

最后,在 onSurfaceCreated 中调用 loadTexture 加载纹理。将在 HelloWorldRenderer 构造方法中将参数 Main Activity 存入成员变量 context 中以便在 loadTexture 中用于访问资源。

在绘制图元之前,使用 glBindTexture 将纹理绑定到目标中,在接下来的绘制中纹理将自动应用。但在此之前,还需要设置好纹理坐标

    private float[] data_tvertices = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
			1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
			0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
			0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
			0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, };
以上坐标是由3ds max 场景中导出,除此之外将原有的顶点坐标及顶点索引数组的内容也一并更新,也是从同一 3ds max 场景中导出(该场景只有一个立方体)
    private float[] data_vertices = { -5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, -5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f,
			-5.0f, };
    private byte[] data_triangles = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
			13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
			30, 31, 32, 33, 34, 35, };

在 createBuffers 中添加
            tvertices = ByteBuffer.allocateDirect(data_tvertices.length * 4);
            tvertices.order(ByteOrder.nativeOrder());
            tvertices.asFloatBuffer().put(data_tvertices);
            tvertices.position(0);
最后在绘制代码中添加纹理及纹理坐标设置的代码
            // 启用顶点数组、法向量、纹理坐标数组
            gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
 
            // 设置顶点数组指针为 ByteBuffer 对象 vertices
            // 第一个参数为每个顶点包含的数据长度(以第二个参数表示的数据类型为单位)
            gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
            gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tvertices);
            // 绑定纹理
            gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
 
            // 绘制 triangles 表示的三角形
            gl.glDrawElements(GL10.GL_TRIANGLES, triangles.remaining(),
                    GL10.GL_UNSIGNED_BYTE, triangles);
 
            // 禁用顶点、法向量、纹理坐标数组
            gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
            gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
修改投景变换矩阵以显示这个稍大此的立方体
	GLU.gluLookAt(gl, -20f, -20f, 20f, 0f, 0f, 0f, 0, 0, 1);
运行,这次立方体的六个面都贴上了同一张图片。

最终代码:

// file : HelloWorldRenderer.java
 
package com.leftart.android.HelloWorld;
 
import java.nio.*;
import javax.microedition.khronos.egl.*;
import javax.microedition.khronos.opengles.*;
import android.content.Context;
import android.graphics.*;
import android.opengl.GLSurfaceView.Renderer;
import android.opengl.*;
 
public class HelloWorldRenderer implements Renderer {
	public HelloWorldRenderer(Main main) {
		this.context = main;
		createBuffers();
	}
 
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		gl.glDisable(GL10.GL_DITHER); // 颜色抖动据说可能严重影响性能,禁用
		gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f);// 设置清除颜色缓冲区时用的RGBA颜色值
 
		gl.glEnable(GL10.GL_DEPTH_TEST);
		gl.glDepthFunc(GL10.GL_LEQUAL);
		gl.glClearDepthf(1f);
 
		gl.glEnable(GL10.GL_TEXTURE_2D);
		loadTexture(gl);
	}
 
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		// 宽高比
		float aspect = (float) width / (float) (height == 0 ? 1 : height);
 
		// 设置视口
		gl.glViewport(0, 0, width, height);
 
		// 设置当前矩阵堆栈为投影矩阵,并将矩阵重置为单位矩阵
		gl.glMatrixMode(GL10.GL_PROJECTION);
		gl.glLoadIdentity();
 
		GLU.gluPerspective(gl, 45.0f, aspect, 0.1f, 200.0f);
		GLU.gluLookAt(gl, -20f, -20f, 20f, 0f, 0f, 0f, 0, 0, 1);
	}
 
	public void onDrawFrame(GL10 gl) {
		// 清除颜色缓冲
		gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
 
		// 设置当前矩阵堆栈为模型堆栈,并重置堆栈,
		// 即随后的矩阵操作将应用到要绘制的模型上
		gl.glMatrixMode(GL10.GL_MODELVIEW);
		gl.glLoadIdentity();
 
		// 将旋转矩阵应用到当前矩阵堆栈上,即旋转模型
		gl.glRotatef(anglez, 0, 0, 1);
		gl.glRotatef(angley, 0, 1, 0);
		gl.glRotatef(anglex, 1, 0, 0);
		anglex += 0.1; // 递增角度值以便每次以不同角度绘制
		angley += 0.2;
		anglez += 0.3;
 
		// 启用顶点数组、法向量、纹理坐标数组
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
 
		// 设置正面
		gl.glFrontFace(GL10.GL_CW);
		
		// 设置顶点数组指针为 ByteBuffer 对象 vertices
		// 第一个参数为每个顶点包含的数据长度(以第二个参数表示的数据类型为单位)
		gl.glVertexPointer(3, GL10.GL_FLOAT, 0, vertices);
		gl.glTexCoordPointer(2, GL10.GL_FLOAT, 0, tvertices);
		// 绑定纹理
		gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
 
		// 绘制 triangles 表示的三角形
		gl.glDrawElements(GL10.GL_TRIANGLES, triangles.remaining(),
				GL10.GL_UNSIGNED_BYTE, triangles);
 
		// 禁用顶点、法向量、纹理坐标数组
		gl.glDisableClientState(GL10.GL_VERTEX_ARRAY);
		gl.glDisableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
	}
 
	private void createBuffers() {
		// 创建顶点缓冲,顶点数组使用 float 类型,每个 float 长4个字节
		vertices = ByteBuffer.allocateDirect(data_vertices.length * 4);
		// 设置字节顺序为本机顺序
		vertices.order(ByteOrder.nativeOrder());
		// 通过一个 FloatBuffer 适配器,将 float 数组写入 ByteBuffer 中
		vertices.asFloatBuffer().put(data_vertices);
		// 重置Buffer的当前位置
		vertices.position(0);
 
		// 创建索引缓冲,索引使用 byte 类型,所以无需设置字节顺序,也无需写入适配。
		triangles = ByteBuffer.allocateDirect(data_triangles.length * 2);
		triangles.put(data_triangles);
		triangles.position(0);
 
		normals = ByteBuffer.allocateDirect(data_normals.length * 4);
		normals.order(ByteOrder.nativeOrder());
		normals.asFloatBuffer().put(data_normals);
		normals.position(0);
 
		tvertices = ByteBuffer.allocateDirect(data_tvertices.length * 4);
		tvertices.order(ByteOrder.nativeOrder());
		tvertices.asFloatBuffer().put(data_tvertices);
		tvertices.position(0);
	}
 
	private void loadTexture(GL10 gl) {
		InputStream bitmapStream = null;
		Bitmap bitmap = null;
		try {
			bitmapStream = context.getResources().openRawResource(R.drawable.tree);
			bitmap = BitmapFactory.decodeStream(bitmapStream);
 
			int[] textures = new int[1];
			gl.glGenTextures(1, textures, 0);
			texture = textures[0];
 
			gl.glBindTexture(GL10.GL_TEXTURE_2D, texture);
 
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MIN_FILTER,
					GL10.GL_NEAREST);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_MAG_FILTER,
					GL10.GL_LINEAR);
 
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_S,
					GL10.GL_REPEAT);
			gl.glTexParameterf(GL10.GL_TEXTURE_2D, GL10.GL_TEXTURE_WRAP_T,
					GL10.GL_REPEAT);
 
			GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
 
		} finally {
			if (bitmap != null)
				bitmap.recycle();
 
			if (bitmapStream != null)
				try {
					bitmapStream.close();
				} catch (IOException e) {
				}
		}
	}

	private Context context;
	private ByteBuffer vertices;
	private ByteBuffer triangles;
	private ByteBuffer normals;
	private ByteBuffer tvertices;
	private int texture;
 
	private float anglex = 0f;
	private float angley = 0f;
	private float anglez = 0f;
 
	private float[] data_vertices = { -5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, -5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f,
			-5.0f, };
	private float[] data_normals = {  -5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, -5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, 5.0f, -5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			-5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f, 5.0f, -5.0f, -5.0f, 5.0f,
			-5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f, 5.0f, 5.0f, 5.0f, 5.0f,
			5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, -5.0f, -5.0f, -5.0f, -5.0f,
			-5.0f, 5.0f, -5.0f, -5.0f, 5.0f, -5.0f, 5.0f, 5.0f, -5.0f, 5.0f,
			-5.0f, };
 
	private float[] data_tvertices = { 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f,
			1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f,
			1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f,
			0.0000f, 0.0000f, 0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f,
			1.0000f, 1.0000f, 0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f,
			0.0000f, 1.0000f, 0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f,
			0.0000f, 1.0000f, 0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f,
			0.0000f, 1.0000f, 1.0000f, 1.0000f, 1.0000f, 0.0000f, 1.0000f,
			0.0000f, 0.0000f, 0.0000f, 0.0000f, 1.0000f, };
 
	private byte[] data_triangles = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12,
			13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
			30, 31, 32, 33, 34, 35, };
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值