opengl 纹理知识点

来源:互联网 发布:动画合成软件 编辑:程序博客网 时间:2024/06/05 03:56
 

2、纹理坐标
我们先来回忆一下之前学过的一点内容:
当我们绘制一个三角形时,只需要指定三个顶点的颜色。三角形中其它各点的颜色不需要我们指定,这些点的颜色是OpenGL自己通过计算得到的。
在我们学习OpneGL光照时,法线向量、材质的指定,都是只需要在顶点处指定一下就可以了,其它地方的法线向量和材质都是OpenGL自己通过计算去获得。

纹理的使用方法也与此类似。只要指定每一个顶点在纹理图象中所对应的像素位置,OpenGL就会自动计算顶点以外的其它点在纹理图象中所对应的像素位置。
这听起来比较令人迷惑。我们可以这样类比一下:
在绘制一条线段时,我们设置其中一个端点为红色,另一个端点为绿色,则OpenGL会自动计算线段中其它各像素的颜色,如果是使用glShadeMode(GL_SMOOTH);,则最终会形成一种渐变的效果(例如线段中点,就是红色和绿色的中间色)。
类似的,在绘制一条线段时,我们设置其中一个端点使用“纹理图象中最左下角的颜色”作为它的颜色,另一个端点使用“纹理图象中最右上角的颜色”作为它的颜色,则OpenGL会自动在纹理图象中选择合适位置的颜色,填充到线段的各个像素(例如线段中点,可能就是选择纹理图象中央的那个像素的颜色)。

我们在类比时,使用了“纹理图象中最左下角的颜色”这种说法。但这种说法在很多时候不够精确,我们需要一种精确的方式来表示我们究竟使用纹理中的哪个像素。纹理坐标也就是因为这样的要求而产生的。以二维纹理为例,规定纹理最左下角的坐标为(0, 0),最右上角的坐标为(1, 1),于是纹理中的每一个像素的位置都可以用两个浮点数来表示(三维纹理会用三个浮点数表示,一维纹理则只用一个即可)。
使用glTexCoord*系列函数来指定纹理坐标。这些函数的用法与使用glVertex*系列函数来指定顶点坐标十分相似。例如:glTexCoord2f(0.0f, 0.0f);指定使用(0, 0)纹理坐标。
通常,每个顶点使用不同的纹理,于是下面这样形式的代码是比较常见的。

glBegin( /* ... */ );
     glTexCoord2f( /* ... */ );   glVertex3f( /* ... */ );
     glTexCoord2f( /* ... */ );   glVertex3f( /* ... */ );
     /* ... */
glEnd();



当我们用一个坐标表示顶点在三维空间的位置时,可以使用glRotate*等函数来对坐标进行转换。纹理坐标也可以进行这种转换。只要使用glMatrixMode(GL_TEXTURE);,就可以切换到纹理矩阵(另外还有透视矩阵GL_PROJECTION和模型视图矩阵GL_MODELVIEW,详细情况在第五课有讲述),然后glRotate*,glScale*,glTranslate*等操作矩阵的函数就可以用来处理“对纹理坐标进行转换”的工作了。在简单应用中,可能不会对矩阵进行任何变换,这样考虑问题会比较简单。
3、纹理参数
到这里,入门所需要掌握的所有难点都被我们掌握了。但是,我们的知识仍然是不够的,如果仅利用现有的知识去使用纹理的话,你可能会发现纹理完全不起作用。这是因为在使用纹理前还有某些参数是必须设置的。
使用glTexParameter*系列函数来设置纹理参数。通常需要设置下面四个参数:
GL_TEXTURE_MAG_FILTER:指当纹理图象被使用到一个大于它的形状上时(即:有可能纹理图象中的一个像素会被应用到实际绘制时的多个像素。例如将一幅256*256的纹理图象应用到一个512*512的正方形),应该如何处理。可选择的设置有GL_NEAREST和GL_LINEAR,前者表示“使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色”,后者表示“使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色”。前者只经过简单比较,需要运算较少,可能速度较快,后者需要经过加权平均计算,其中涉及除法运算,可能速度较慢(但如果有专门的处理硬件,也可能两者速度相同)。从视觉效果上看,前者效果较差,在一些情况下锯齿现象明显,后者效果会较好(但如果纹理图象本身比较大,则两者在视觉效果上就会比较接近)。
GL_TEXTURE_MIN_FILTER:指当纹理图象被使用到一个小于(或等于)它的形状上时(即有可能纹理图象中的多个像素被应用到实际绘制时的一个像素。例如将一幅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中的类似。此参数似乎是必须设置的(在我的计算机上,不设置此参数将得到错误的显示结果,但我目前并没有找到根据)。
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, 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、纹理对象
前面已经提到过,载入一幅纹理所需要的时间是比较多的。因此应该尽量减少载入纹理的次数。如果只有一幅纹理,则应该在第一次绘制前就载入它,以后就不需要再次载入了。这点与glDrawPixels函数很不相同。每次使用glDrawPixels函数,都需要把像素数据重新载入一次,因此用glDrawPixels函数来反复绘制图象的效率是较低的(如果只绘制一次,则不会有此问题),使用纹理来反复绘制图象是可取的做法。
但是,在每次绘制时要使用两幅或更多幅的纹理时,这个办法就行不通了。你可能会编写下面的代码:

glTexImage2D( /* ... */ ); // 载入第一幅纹理
// 使用第一幅纹理
glTexImage2D( /* ... */ ); // 载入第二幅纹理
// 使用第二幅纹理
// 当纹理的数量增加时,这段代码会变得更加复杂。



在绘制动画时,由于每秒钟需要将画面绘制数十次,因此如果使用上面的代码,就会反复载入纹理,这对计算机是非常大的负担,以目前的个人计算机配置来说,根本就无法让动画能够流畅的运行。因此,需要有一种机制,能够在不同的纹理之间进行快速的切换。

纹理对象正是这样一种机制。我们可以把每一幅纹理(包括纹理的像素数据、纹理大小等信息,也包括了前面所讲的纹理参数)放到一个纹理对象中,通过创建多个纹理对象来达到同时保存多幅纹理的目的。这样一来,在第一次使用纹理前,把所有的纹理都载入,然后在绘制时只需要指明究竟使用哪一个纹理对象就可以了。

使用纹理对象和使用显示列表有相似之处:使用一个正整数来作为纹理对象的编号。在使用前,可以调用glGenTextures来分配纹理对象。该函数有两种比较常见的用法:

GLuint texture_ID;
glGenTextures(1, &texture_ID); // 分配一个纹理对象的编号


或者:

GLuint texture_ID_list[5];
glGenTextures(5, texture_ID_list); // 分配5个纹理对象的编号



零是一个特殊的纹理对象编号,表示“默认的纹理对象”,在分配正确的情况下,glGenTextures不会分配这个编号。与glGenTextures对应的是glDeleteTextures,用于销毁一个纹理对象。

在分配了纹理对象编号后,使用glBindTexture函数来指定“当前所使用的纹理对象”。然后就可以使用glTexImage*系列函数来指定纹理像素、使用glTexParameter*系列函数来指定纹理参数、使用glTexCoord*系列函数来指定纹理坐标了。如果不使用glBindTexture函数,那么glTexImage*、glTexParameter*、glTexCoord*系列函数默认在一个编号为0的纹理对象上进行操作。glBindTexture函数有两个参数,第一个参数是需要使用纹理的目标,因为我们现在只学习二维纹理,所以指定为GL_TEXTURE_2D,第二个参数是所使用的纹理的编号。
使用多个纹理对象,就可以使OpenGL同时保存多个纹理。在使用时只需要调用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_ID_2); // 指定第二幅纹理
// 使用第二幅纹理



提示:纹理对象是从OpenGL 1.1版开始才有的,最旧版本的OpenGL 1.0并没有处理纹理对象的功能。不过,我想各位的机器不会是比OpenGL 1.1更低的版本(Windows 95就自带了OpenGL 1.1版本,遗憾的是,Microsoft对OpenGL的支持并不积极,Windows XP也还采用1.1版本。据说Vista使用的是OpenGL 1.4版。当然了,如果安装显卡驱动的话,现在的主流显卡一般都附带了适用于该显卡的OpenGL 1.4版或更高版本),所以这个问题也就不算是问题了。

0 0