OpenGL ES纹理映射

本文介绍如何在OpenGL ES中实现纹理映射,包括纹理坐标的概念、纹理对象的创建及绑定、过滤器的选择等关键步骤,并提供了一个具体的纹理三角形绘制实例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

以下均转自Android游戏编程入门经典,转载请标明出处

为了将一个位图映射到一个三角形上,需要为三角形的每个顶点增加纹理坐标(texture coordinates)。纹理坐标把纹理(上传的位图)中的一个点映射到三角形的一个顶点上。纹理映射通常是2D的。

相对位置坐标通常是x,y,z,纹理坐标通常称为u、v或s、t。s等价于标准坐标系统里的x坐标,t等价于y坐标。s轴指右,t轴指向下。加载任何图像,不论它有多宽和多高的像素,都将嵌入到这个坐标系统里。图像左上角都是(0, 0),右下角也一直都是(1, 1),即使宽度和高度不等。

int VERTEX_SIZE = (2 + 2) * 4;
ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * VERTEX_SIZE);
			byteBuffer.order(ByteOrder.nativeOrder());
			vertices = byteBuffer.asFloatBuffer();
			vertices.put(new float[]{
					0.0f,   0.0f,   0.0f,   1.0f,
				  319.0f,   0.0f,   1.0f,   1.0f,
				  160.0f, 479.0f,   0.5f,   0.0f
			});
			vertices.flip();

OpenGL ES会把顶点间填充的颜色和三角形映射部分的纹理颜色进行动态混合。这样还需要调整缓冲区的大小和VERTEX_SIZE常量(例如(2 + 4 + 2) * 4)。为了告诉OpenGL ES顶点含有纹理坐标数据,需要再次调用glEnableClientState()和glTexCoordPointer()方法。

gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			
			vertices.position(0);
			gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);
			vertices.position(2);
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);

创建纹理对象:

GL10.glGenTextures(int numTextures, int[] ids, int offset)

此时纹理对象仍然是空的,这意味着它还没有任何数据。下面上传位图。首先,需要做的是绑定纹理。在OpenGL ES中绑定某种对象,意味着后续所有的调用中使用指定的对象,直到再次更改绑定。此时需要绑定一个纹理对象,使用glBindTexture()方法实现。一旦绑定一个纹理,就可以操作它的属性了。

gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);

在使用纹理对象之前,还有一件事需要做。三角形所在屏幕上占据的区域可能比纹理投影区域大很多或小很多。对于每种情况,都需要告诉OpenGL ES应该把纹理放大还是缩小。放大和缩小操作在OpenGL ES中也称为放大倍数和缩小倍数过滤器。这些过滤器是纹理对象的属性。在设置它们之前,需要确定纹理对象已经通过glBindTexture()调用进行绑定。如果已经绑定,可以这样设置:

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_NEAREST);

两次调用都使用了方法GL10.glTexParameterf(),该方法设置纹理属性。第一个调用指明了缩小倍数过滤器;第二个调用使用了放大倍数过滤器。最后一个参数指明了过滤器的类型。此处有两个选择:GL10.GL_NEAREST和GL10.GL_LINEAR。

第一种过滤器总是选择映射到像素的纹理映射中最接近的纹元。第二种过滤器类型采样距待处理三角形像素最近的4个纹元,取平均值后得到的该像素最终颜色。如果需要得到像素化的图像,用第一种类型过滤器,如果想得到平滑的图像,则需要使用第二种类型的过滤器。


一旦完成了纹理的定义,取消纹理绑定是通常的做法。当不再需要位图时,也应当对其进行回收。

gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
				bitmap.recycle();

0是一个特殊的ID,他告诉OpenGL ES解除当前绑定对象。

综合实例:

package org.example.androidgames.glbasics;

import java.io.IOException;
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;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.opengl.GLUtils;
import android.util.Log;

public class TexturedTriangleTest extends GLGame {

	@Override
	public Screen getStartScreen() {
		// TODO Auto-generated method stub
		return new TexturedTriangleScreen(this);
	}
	
	class TexturedTriangleScreen extends Screen{
		final int VERTEX_SIZE = (2 + 2) * 4;
		GLGraphics glGraphics;
		FloatBuffer vertices;
		int textureId;

		public TexturedTriangleScreen(Game game) {
			super(game);
			glGraphics = ((GLGame)game).getGLGraphics();
			
			ByteBuffer byteBuffer = ByteBuffer.allocateDirect(3 * VERTEX_SIZE);
			byteBuffer.order(ByteOrder.nativeOrder());
			vertices = byteBuffer.asFloatBuffer();
			vertices.put(new float[]{
					0.0f,   0.0f,   0.0f,   1.0f,
				  319.0f,   0.0f,   1.0f,   1.0f,
				  160.0f, 479.0f,   0.5f,   0.0f
			});
			vertices.flip();
			textureId = loadTexture("bobrgb888.png");
		}
		
		public int loadTexture(String fileName){
			try{
				Bitmap bitmap =
						BitmapFactory.decodeStream(game.getFileIO().readAsset(fileName));
				GL10 gl = glGraphics.getGL();
				int textureIds[] = new int[1];
				gl.glGenTextures(1, textureIds, 0);
				int textureId = textureIds[0];
				gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
				GLUtils.texImage2D(GL10.GL_TEXTURE_2D, 0, bitmap, 0);
				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_NEAREST);
				gl.glBindTexture(GL10.GL_TEXTURE_2D, 0);
				bitmap.recycle();
				return textureId;
			}catch(IOException e){
				Log.d("TexturedTriangleTest", "couldn't load asset 'bobrgb888.png'");
				throw new RuntimeException("couldn't load asset '" + fileName + "'");
			}
		}

		@Override
		public void update(float deltaTime) {
			// TODO Auto-generated method stub
			
		}

		@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.glEnable(GL10.GL_TEXTURE_2D);
			gl.glBindTexture(GL10.GL_TEXTURE_2D, textureId);
			
			gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
			gl.glEnableClientState(GL10.GL_TEXTURE_COORD_ARRAY);
			
			vertices.position(0);
			gl.glVertexPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, vertices);
			vertices.position(2);
			gl.glTexCoordPointer(2, GL10.GL_FLOAT, VERTEX_SIZE, 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
			
		}
		
	}

}

运行效果


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值