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) { } } }
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, };
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, }; }