《二维纹理的操作.doc》由会员分享,可在线阅读,更多相关《二维纹理的操作.doc(3页珍藏版)》请在金锄头文库上搜索。
1、二维纹理的操作 1、启用纹理和载入纹理glEnable(GL_TEXTURE_2D); /启用二维纹理glDisable(GL_TEXTURE_2D); /禁用二维纹理/载入2D纹理glTexImage2D( 1GLenum target, 2GLint level, 3GLint internalFormat,4GLsize width, 5GLsizei height, 6GLint border, 7GLenum format, 8GLenum type, 9const GLvoid *texels );1、指定目标:一维纹理、二维纹理、三维纹理等等。2、多重细节层次(多种分辨率的纹理图
2、像)。3、指定颜色成分:为1到4之间的整数,3/GL_RGB,4/GL_RGBA。注意:虽然我们使用Windows的BMP文件作为纹理时,一般是蓝色的像素在最前,其真实的格式为GL_BGR而不是GL_RGB,在数据的顺序上有所不同,但因为同样是红、绿、蓝三种颜色,因此这里仍然使用GL_RGB。(如果使用GL_BGR,OpenGL将无法识别这个参数,造成错误)。4、5宽度高度。注意:OpenGL在以前的很多版本中,限制纹理的大小必须是2的整数次方,即纹理的宽度和高度只能是16, 32, 64, 128, 256等值,直到最近的新版本才取消了这个限制。而且,一些OpenGL实现(例如,某些PC机上
3、板载显卡的驱动程序附带的OpenGL)并没有支持到如此高的OpenGL版本。因此在使用纹理时要特别注意其大小。尽量使用大小为2的整数次方的纹理,当这个要求无法满足时,使用gluScaleImage函数把图象缩放至所指定的大小(在后面的例子中有用到)。另外,无论旧版本还是新版本,都限制了纹理大小的最大值,例如,某OpenGL实现可能要求纹理最大不能超过1024*1024。可以使用如下的代码来获得OpenGL所支持的最大纹理:GLint max; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max);6、纹理边框的大小:没有使用边框,则设置为零。7、所读取的像素元素的数
4、据成分:索引值,或R,G,B,A成分值(多个成分值的组合),深度,模板等。8、所获取的像素元素的数据类型。9、读取的像素,保存在texels指针中。注意,载入纹理的过程可能比较慢,原因是纹理数据通常比较大,例如一幅512*512的BGR格式的图象,大小为0.75M。把这些像素数据从主内存传送到专门的图形硬件,这个过程中还可能需要把程序中所指定的像素格式转化为图形硬件所能识别的格式(或最能发挥图形硬件性能的格式),这些操作都需要较多时间。2、纹理坐标:只要指定每一个顶点在纹理图象中所对应的像素位置,OpenGL就会自动计算顶点以外的其它点在纹理图象中所对应的像素位置。就好像:在绘制一条线段时,我
5、们设置其中一个端点为红色,另一个端点为绿色,则OpenGL会自动计算线段中其它各像素的颜色,如果是使用glShadeMode(GL_SMOOTH);,则最终会形成一种渐变的效果(例如线段中点,就是红色和绿色的中间色)。类似的,在绘制一条线段时,我们设置其中一个端点使用“纹理图象中最左下角的颜色”作为它的颜色,另一个端点使用“纹理图象中最右上角的颜色”作为它的颜色,则OpenGL会自动在纹理图象中选择合适位置的颜色,填充到线段的各个像素(例如线段中点,可能就是选择纹理图象中央的那个像素的颜色)。使用glTexCoord*系列函数来指定纹理坐标。这些函数的用法与使用glVertex*系列函数来指定
6、顶点坐标十分相似。例如:glTexCoord2f(0.0f, 0.0f);指定使用(0, 0)纹理坐标。通常,每个顶点使用不同的纹理,于是下面这样形式的代码是比较常见的glBegin( /* . */ ); glTexCoord2f( /* . */ ); glVertex3f( /* . */ ); glTexCoord2f( /* . */ ); glVertex3f( /* . */ ); /* . */glEnd();当我们用一个坐标表示顶点在三维空间的位置时,可以使用glRotate*等函数来对坐标进行转换。纹理坐标也可以进行这种转换。只要使用glMatrixMode(GL_TEXT
7、URE);,就可以切换到纹理矩阵(另外还有透视矩阵GL_PROJECTION和模型视图矩阵GL_MODELVIEW,详细情况在第五课有讲述),然后glRotate*,glScale*,glTranslate*等操作矩阵的函数就可以用来处理“对纹理坐标进行转换”的工作了。3、纹理参数使用glTexParameter*系列函数来设置纹理参数。通常需要设置下面四个参数:GL_TEXTURE_MAG_FILTER:指当纹理图象被使用到一个大于它的形状上时(即:有可能纹理图象中的一个像素会被应用到实际绘制时的多个像素。例如将一幅256*256的纹理图象应用到一个512*512的正方形),应该如何处理。可
8、选择的设置有GL_NEAREST和GL_LINEAR,前者表示“使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色”,后者表示“使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色”。前者只经过简单比较,需要运算较少,可能速度较快,后者需要经过加权平均计算,其中涉及除法运算,可能速度较慢(但如果有专门的处理硬件,也可能两者速度相同)。从视觉效果上看,前者效果较差,在一些情况下锯齿现象明显,后者效果会较好(但如果纹理图象本身比较大,则两者在视觉效果上就会比较接近)。GL_TEXTURE_MIN_FILTER:指当纹理图象被使用到一个小于(或等于)它的形状上时(即有可能
9、纹理图象中的多个像素被应用到实际绘制时的一个像素。例如将一幅256*256的纹理图象应用到一个128*128的正方形),应该如何处理。可选择的设置有GL_NEAREST,GL_LINEAR,GL_NEAREST_MIPMAP_NEAREST,GL_NEAREST_MIPMAP_LINEAR,GL_LINEAR_MIPMAP_NEAREST和GL_LINEAR_MIPMAP_LINEAR。其中后四个涉及到mipmap,现在暂时不需要了解。前两个选项则和GL_TEXTURE_MAG_FILTER中的类似。此参数似乎是必须设置的(在我的计算机上,不设置此参数将得到错误的显示结果,但我目前并没有找到根
10、据)。GL_TEXTURE_WRAP_S:指当纹理坐标的第一维坐标值大于1.0或小于0.0时,应该如何处理。基本的选项有GL_CLAMP和GL_REPEAT,前者表示“截断”,即超过1.0的按1.0处理,不足0.0的按0.0处理。后者表示“重复”,即对坐标值加上一个合适的整数(可以是正数或负数),得到一个在0.0, 1.0范围内的值,然后用这个值作为新的纹理坐标。例如:某二维纹理,在绘制某形状时,一像素需要得到纹理中坐标为(3.5, 0.5)的像素的颜色,其中第一维的坐标值3.5超过了1.0,则在GL_CLAMP方式中将被转化为(1.0, 0.5),在GL_REPEAT方式中将被转化为(0.5
11、, 0.5)。在后来的OpenGL版本中,又增加了新的处理方式,这里不做介绍。如果不指定这个参数,则默认为GL_REPEAT。GL_TEXTURE_WRAP_T:指当纹理坐标的第二维坐标值大于1.0或小于0.0时,应该如何处理。选项与GL_TEXTURE_WRAP_S类似,不再重复。如果不指定这个参数,则默认为GL_REPEAT。设置参数的代码如下所示:glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);4、纹理对象前面已经提到过,载入一幅纹理所需要的时间是比较多的。因此应该尽量减少载入纹理的次数。如果只有一幅纹理,则应该在
12、第一次绘制前就载入它,以后就不需要再次载入了。这点与glDrawPixels函数很不相同。每次使用glDrawPixels函数,都需要把像素数据重新载入一次,因此用glDrawPixels函数来反复绘制图象的效率是较低的(如果只绘制一次,则不会有此问题),使用纹理来反复绘制图象是可取的做法。但是,在每次绘制时要使用两幅或更多幅的纹理时,这个办法就行不通了。你可能会编写下面的代码:glTexImage2D( /* . */ ); /载入第一幅纹理/使用第一幅纹理glTexImage2D( /* . */ ); /载入第二幅纹理/使用第二幅纹理/当纹理的数量增加时,这段代码会变得更加复杂。在绘制动
13、画时,由于每秒钟需要将画面绘制数十次,因此如果使用上面的代码,就会反复载入纹理,这对计算机是非常大的负担,以目前的个人计算机配置来说,根本就无法让动画能够流畅的运行。因此,需要有一种机制,能够在不同的纹理之间进行快速的切换。纹理对象正是这样一种机制。我们可以把每一幅纹理(包括纹理的像素数据、纹理大小等信息,也包括了前面所讲的纹理参数)放到一个纹理对象中,通过创建多个纹理对象来达到同时保存多幅纹理的目的。这样一来,在第一次使用纹理前,把所有的纹理都载入,然后在绘制时只需要指明究竟使用哪一个纹理对象就可以了。使用纹理对象和使用显示列表有相似之处:使用一个正整数来作为纹理对象的编号。在使用前,可以调
14、用glGenTextures来分配纹理对象。该函数有两种比较常见的用法:GLuint texture_ID;glGenTextures(1, &texture_ID); /分配一个纹理对象的编号或者:GLuint texture_ID_list5;glGenTextures(5, texture_ID_list); /分配5个纹理对象的编号零是一个特殊的纹理对象编号,表示“默认的纹理对象”,在分配正确的情况下,glGenTextures不会分配这个编号。与glGenTextures对应的是glDeleteTextures,用于销毁一个纹理对象。在分配了纹理对象编号后,使用glBindTextu
15、re函数来指定“当前所使用的纹理对象”。然后就可以使用glTexImage*系列函数来指定纹理像素、使用glTexParameter*系列函数来指定纹理参数、使用glTexCoord*系列函数来指定纹理坐标了。如果不使用glBindTexture函数,那么glTexImage*、glTexParameter*、glTexCoord*系列函数默认在一个编号为0的纹理对象上进行操作。glBindTexture函数有两个参数,第一个参数是需要使用纹理的目标,因为我们现在只学习二维纹理,所以指定为GL_TEXTURE_2D,第二个参数是所使用的纹理的编号。使用多个纹理对象,就可以使OpenGL同时保存
16、多个纹理。在使用时只需要调用glBindTexture函数,在不同纹理之间进行切换,而不需要反复载入纹理,因此动画的绘制速度会有非常明显的提升。/在程序开始时:分配好纹理编号,并载入纹理glGenTextures( /* . */ );glBindTexture(GL_TEXTURE_2D, texture_ID_1);/载入第一幅纹理glBindTexture(GL_TEXTURE_2D, texture_ID_2);/载入第二幅纹理/在绘制时,切换并使用纹理,不需要再进行载入glBindTexture(GL_TEXTURE_2D, texture_ID_1); /指定第一幅纹理/使用第一幅纹理glBindTexture(GL_TEXTURE_2D, texture