从零开始学OpenGLES开发——第二章

从零开始学OpenGLES开发——第二章


第二章 OpenGLES1.0的纹理贴图


将上一章的代码精简一下,只画留一个三角形的绘制。
数据初始化代码:
	private FloatBuffer vertexBuffer = null ;
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		
		float[] vertexs = new float[]{
				-5.0f,   5.0f, -30.0f, 
				 5.0f,  -5.0f, -30.0f, 
				 5.0f,   5.0f, -30.0f, 
		};
		vertexBuffer = ByteBuffer.allocateDirect(vertexs.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
		vertexBuffer.put(vertexs);
		vertexBuffer.position(0);
	}


绘制的代码:
	public void onDrawFrame(GL10 gl) {
		GLES10.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 清空场景为黑色。
		GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT | GLES10.GL_DEPTH_BUFFER_BIT);// 清空相关缓存。


		GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
		
		GLES10.glVertexPointer(3, GLES10.GL_FLOAT, 0, vertexBuffer);
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
		
		GLES10.glFlush();
	}


关于OpenGLES1.0的贴图,需要讲几点:


1、纹理贴图是以纹理坐标方式告诉OpenGL怎么贴的。纹理坐标范围是0-1。
2、一个顶点对应一个纹理坐标,纹理坐标只有两个float值。纹理坐标对应的是图片上的范围。而图片的左上角的纹理坐标是(0,0),右下角是(1,1)。
3、OpenGLES对于纹理的图片也有要求,官方推荐的纹理的图片的长和宽都必须是2的次幂倍数,比如64,128,256这种,官方还要求纹理的尺寸长和宽都不能小于64。
4、对于图片的长宽的长度是2的次幂这个限制,其实具体支持不支持是不同的显卡决定的,比如我的垃圾 ME525+ 就不支持不是2的次幂倍的纹理,同时的小米就支持任意尺寸的。


说完了,正式开始教大家怎么贴图了。


首先肯定要先整一张图到工程里面嘛,整一个128x128的jpg图片,放到android工程下的res/raw目录下,我这里的图片叫image_test.jpg




然后在onSurfaceCreated的最后,加上初始化一个纹理的代码:

private FloatBuffer vertexBuffer = null ;
private int			textureId	 = 0 ;
public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		
	float[] vertexs = new float[]{
				-5.0f,   5.0f, -30.0f, 
				 5.0f,  -5.0f, -30.0f, 
				 5.0f,   5.0f, -30.0f, 
	};
	vertexBuffer = ByteBuffer.allocateDirect(vertexs.length * 4)
						.order(ByteOrder.nativeOrder()).asFloatBuffer();
	vertexBuffer.put(vertexs);
	vertexBuffer.position(0);
		
	int[] tempArray = new int[1];
	GLES10.glGenTextures(1, tempArray, 0);
	GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, tempArray[0]);
		
	Bitmap image = BitmapFactory.decodeStream(getResources().openRawResource(R.raw.image_test)) ;
	GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, image, 0);
	image.recycle();
		
	GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_NEAREST);
	GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_NEAREST);
		
	textureId = tempArray[0] ;
}


以上代码解释如下:(上一章解释过的不继续解释了)
glGenTextures 创建n个纹理对象(OpenGL的对象),第一个参数是1,所以我只需要创建一个纹理对象,并把得到的纹理对象的句柄放入数组。


glBindTexture 这句话的含义,其实详细说起来需要引入一个更大的体系(那就是多纹理贴图,以及1D,2D,3D纹理的类型)
简单说一下吧:OpenGL的纹理分几个大类,1D,2D,3D。但是OpenGLES1.0里面默认只支持2D(也就是平面图像)。在标准的OpenGL版本里面,还有GL_TEXTURE_1D,GL_TEXTURE_3D等类型。而对于纹理的操作,默认一种类型一次只能操作一个(OpenGL的状态和当前操作对象切换的习惯设计)。glBindTexture的意思就是说,把当前这个我创建的这个纹理,绑定到GL_TEXTURE_2D的类型上,那么后面凡是对GL_TEXTURE_2D这个类型的纹理进行操作,就是操作的我创建的这个纹理。


后面的代码就是加载我那个图片文件,得到Bitmap,然后调用GLUtils.texImage2D将这个图片,设置到当前活动的2D纹理,注意它第一个参数GLES10.GL_TEXTURE_2D(就像我前面说的,实际操作的是这个类型下,当前活动的对象)。


图像被设置到纹理对象之后,纹理对象保存的位置是服务端,就相当于图像已经被存储到显卡中了,所以这个Bitmap可以立即recycle。


后面两句代码glTexParameterx,设置当前操作的这个纹理的贴图操作,主要讲的是当一个文理坐标具体在查找像素点的时候,如果发现它位于两个像素之间时,怎么取值的问题,GLES10.GL_NEAREST 表示查找最近的像素点。比如我有4个像素的图片
而纹理坐标最后映射的时候,标准的纹理坐标只能取0.0 0.25 0.5 0.75等几个值,如果介于这几个之间怎么办呢,所以需要设置参数告诉OpenGL这种时候怎么取
GLES10.GL_NEAREST告诉OpenGL,取最近的1个像素点。
GLES10.GL_LINEAR 告诉OpenGL,取最近的几个像素点求平均值。还有其他的参数,可以查看更加详细的资料。
实际画出来的效果是 GLES10.GL_NEAREST 会有锯齿感,图像更加锐利;GLES10.GL_LINEAR会有平滑感,但图像相对模糊。


最后只有一个textureId,即纹理对象句柄值,作为了成员变量,会在具体绘制的时候使用它。

除了纹理,我们还需要构建文理坐标数组,然后纹理图像才能根据坐标贴到模型上。

还是在onSurfaceCreated代码后面加上纹理坐标生成的代码:

	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		
		float[] vertexs = new float[]{
				-5.0f,   5.0f, -30.0f, 
				 5.0f,  -5.0f, -30.0f, 
				 5.0f,   5.0f, -30.0f, 
		};
		vertexBuffer = ByteBuffer.allocateDirect(vertexs.length * 4)
						.order(ByteOrder.nativeOrder()).asFloatBuffer();
		vertexBuffer.put(vertexs);
		vertexBuffer.position(0);
		
		int[] tempArray = new int[1];
		GLES10.glGenTextures(1, tempArray, 0);
		GLES10.glBindTexture(GLES10.GL_TEXTURE_2D, tempArray[0]);
		
		Bitmap image = BitmapFactory.decodeStream(getResources().openRawResource(R.raw.image_test)) ;
		GLUtils.texImage2D(GLES10.GL_TEXTURE_2D, 0, image, 0);
		image.recycle();
		
		GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MAG_FILTER, GLES10.GL_NEAREST);
		GLES10.glTexParameterx(GLES10.GL_TEXTURE_2D, GLES10.GL_TEXTURE_MIN_FILTER, GLES10.GL_NEAREST);
		
		textureId = tempArray[0] ;
		
		
		float[] texcoods = new float[]{
				0,	0,
				1,	1,
				1,	0,
		};
		txtCoodsBuffer = ByteBuffer.allocateDirect(texcoods.length * 4).order(ByteOrder.nativeOrder()).asFloatBuffer();
		txtCoodsBuffer.put(texcoods);
		txtCoodsBuffer.position(0);


		GLES10.glEnable(GLES10.GL_TEXTURE_2D);
	}



三个顶点,对应三个坐标,最后一句是真正开启2D纹理贴图功能。


绘制的代码改成如下形式:

	public void onDrawFrame(GL10 gl) {
		GLES10.glClearColor(0.0f, 0.0f, 0.0f, 0.0f); // 清空场景为黑色。
		GLES10.glClear(GLES10.GL_COLOR_BUFFER_BIT | GLES10.GL_DEPTH_BUFFER_BIT);// 清空相关缓存。


		GLES10.glEnableClientState(GLES10.GL_VERTEX_ARRAY);
		GLES10.glVertexPointer(3, GLES10.GL_FLOAT, 0, vertexBuffer);
		
		GLES10.glEnableClientState(GLES10.GL_TEXTURE_COORD_ARRAY);
		GLES10.glTexCoordPointer(2, GLES10.GL_FLOAT, 0, txtCoodsBuffer);
		
		GLES10.glDrawArrays(GLES10.GL_TRIANGLES, 0, 3);
		
		GLES10.glFlush();
	}


glTexCoordPointer 和 glVertexPointer类似,只是前者一个顶点的坐标数据个数是两个。


运行吧!!骚年,可以看到半个中国心了噢。



继续讲一点额外的事项:
这里面只有一个纹理,一个三角形。如果是多个纹理对象,多个三角形,分别不同的贴图的话
那么前面初始化的过程差不多,只不过会产生多个纹理的句柄

而后面在绘制的时候,就是在glDrawArrays之前,调用一次glBindTexture即可,激活某一个纹理,那么绘制的时候就是用的当前的纹理对象。一个纹理的话,默认就始终是它,所以不需要重复glBindTexture了。


下一章将继续更新,关于光照材质等技术点。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值