12.1 基本步骤
计算机三维图形通过给面贴纹理来表现表面细节。OpenGL默认设置是关闭贴纹理的,所以必须先用命令打开纹理计算。OpenGL体系内有一块纹理内存,在有硬件加速的情况下,可能是位于显卡的VRAM里,否则会是OpenGL库管理的一块内存。在这个纹理内存里图片是以特定的内部格式保存的,有的显卡还支持压缩纹理技术,所以将纹理像素从应用程序内存传到纹理内存需要进行格式转换。这在OpenGL中是通过分别描述像素在应用程序内存和纹理内存的格式来完成的,真正转换工作OpenGL会在内部完成。OpenGL术语称应用程序内存读出像素的过程为解码(UNPACK),而向纹理内存写像素的过程为编码(PACK)。用glPixelStore*(GL_[UN]PACK_*,参数值);命令设定编码[解码]格式 。对于贴纹理过程我们只需关心解码过程。如今的显卡通常都有比较大的显存,其中有一部份是专门的纹理存储区,有的卡还可以将最多64M系统内存映射为纹理内存,所以我们有可能把经常要用的纹理就保留在纹理内存里以提高程序性能。
纹理映射是一个相当复杂的过程,这节只简单地叙述一下最基本的执行纹理映射所需的步骤。基本步骤如下:
1)定义纹理(glGenTextures()、glBindTexture())、2)控制纹理(glTexParameteri())、3)说明纹理映射方式(glTexEnv)、4)绘制场景,给出顶点的纹理坐标和几何坐标。
注意:纹理映射只能在RGBA方式下执行,不能运用于颜色表方式。下面举出一个最简单的纹理映射应用例子:
注意:纹理图像必须是2的N次幂,纹理图像的宽高比必须是(2的n次幂:1)。因为在mipmap技术中,小的纹理图通常是通过对最大的纹理图进行滤波处理得到的,每个纹素的值时一个纹理图中4个相应纹素的平均值。但是让美工做出2次方纹理,并且整张图的空间利用率要高,是很难的。
例12-1 简单纹理映射应用例程(texsmpl.c)
图12-1 简单纹理映射 |
以上程序运行结果是将一块纹理映射到两个正方形上去。这两个正方形都是按透视投影方式绘制,其中一个正对观察者,另一个向后倾斜45度角。图形的纹理是由函数makeImage()产生的,并且所有纹理映射的初始化工作都在程序myinit()中进行。由glTexImage2d()说明了一个全分辨率的图像,其参数指出了图像的尺寸、图像类型、图像位置和图像的其它特性;下面连续调用函数glTexParameter*()说明纹理怎样缠绕物体和当象素与纹理数组中的单个元素(texel,暂称纹素)不能精确匹配时如何过滤颜色;接着用函数glTexEnv*()设置画图方式为GL_DECAL,即多边形完全用纹理图像中的颜色来画,不考虑多边形在未被纹理映射前本身的颜色;最后,调用函数glEnable()启动纹理映射。子程序display()画了两个正方形,其中纹理坐标与几何坐标一起说明,glTexCoord*()函数类似于glNormal*()函数,不过它是设置纹理坐标,之后任何顶点都把这个纹理坐标与顶点坐标联系起来,直到再次调用 glTexCoord*()改变当前纹理坐标。
12.2、纹理定义
12.2.1 二维纹理定义的函数
void glTexImage2D(GLenum target,GLint level,GLint internalFormat,
GLsizei width, glsizei height,GLint border,
GLenum format,GLenum type, const GLvoid *pixels);
参数 target 是 GL_TEXTURE_2D (二维纹理) 或 GL_PROXY_TEXTURE_2D (二维代理纹理), 代理纹理暂且不提。
参数 level 指定了纹理映射细节的级别,用在mipmap中。 基本的纹理图像级别为0, 在后面的mipmap部分讲解
参数 internalFormat 指定了纹理存储在显存中的内部格式,取值在下表。为兼容 OpenGL1.0 internalFormat 可以取值 1,2,3,4 分别对应常量 LUMINANCE, LUMINANCE_ALPHA, RGB, RGBA。
格式 |
注解 |
GL_ALPHA |
Alpha 值 |
GL_DEPTH_COMPONENT |
深度值 |
GL_LUMINCE |
灰度值 |
GL_LUMINANCE_ALPHA |
灰度值和 Alpha 值 |
GL_INTENSITY |
亮度值 |
GL_RGB |
Red, Green, Blue三原色值 |
GL_RGBA |
Red, Green, Blue 和Alpha 值 |
格式 |
注解 |
GL_COLOR_INDEX |
颜色索引值 |
GL_DEPTH_COMPONENT |
深度值 |
GL_RED |
红色像素值 |
GL_GREEN |
绿色像素值 |
GL_BLUE |
蓝色像素值 |
GL_ALPHA |
Alpha 值 |
GL_RGB |
Red, Green, Blue 三原色值 |
GL_RGBA |
Red, Green, Blue 和Alpha 值 |
GL_BGR |
Blue, Green, Red 值 |
GL_BGRA |
Blue, Green, Red 和Alpha 值 |
GL_LUMINANCE |
灰度值 |
GL_LUMINANCE_ALPHA |
灰度值和 Alpha 值 |
数据类型 |
注解 |
GL_BITMAP |
一位(0或1) |
GL_BYTE |
带符号8位整形值(一个字节) |
GL_UNSIGNED_BYTE |
不带符号8位整形值(一个字节) |
GL_SHORT |
带符号16位整形值(2个字节) |
GL_UNSIGNED_SHORT |
不带符号16未整形值(2个字节) |
GL_INT |
带符号32位整形值(4个字节) |
GL_UNSIGNED_INT |
不带符号32位整形值(4个字节) |
GL_FLOAT |
单精度浮点型(4个字节) |
GL_UNSIGNED_BYTE_3_3_2 |
压缩到不带符号8位整形:R3,G3,B2 |
GL_UNSIGNED_BYTE_2__3_REV |
压缩到不带符号8位整形:B2,G3,R3 |
GL_UNSIGNED_SHORT_5_6_5 |
压缩到不带符号16位整形:R5,G6,B5 |
GL_UNSIGNED_SHORT_5_6_5_REV |
压缩到不带符号16位整形:B5,G6,R5 |
GL_UNSIGNED_SHORT_4_4_4_4 |
压缩到不带符号16位整形:R4,G4,B4,A4 |
GL_UNSIGNED_SHORT_4_4_4_4_REV |
压缩到不带符号16位整形:A4,B4,G4,R4 |
GL_UNSIGNED_SHORT_5_5_5_1 |
压缩到不带符号16位整形:R5,G5,B5,A1 |
GL_UNSIGNED_SHORT_1_5_5_5_REV |
压缩到不带符号16位整形:A1,B5,G5,R5 |
GL_UNSIGNED_INT_8_8_8_8 |
压缩到不带符号32位整形:R8,G8,B8,A8 |
GL_UNSIGNED_INT_8_8_8_8_REV |
压缩到不带符号32位整形:A8,B8,G8,R8 |
GL_UNSIGNED_INT_10_10_10_2 |
压缩到32位整形:R10,G10,B10,A2 |
GL_UNSIGNED_INT_2_10_10_10_REV |
压缩到32位整形:A2,B10,G10,R10 |
最后一个参数pixels包含了纹理图像数据,这个数据描述了纹理图像本身和它的边界。
12.2 一维纹理定义的函数
void glTexImage1D(GLenum target,GLint level,GLint components,GLsizei width,
GLint border,GLenum format,GLenum type,const GLvoid *pixels);
定义一个一维纹理映射。除了第一个参数target应设置为GL_TEXTURE_1D外,其余所有的参数与函数TexImage2D()的一致,不过纹理图像是一维纹素数组,其宽度值必须是2的幂,若有边界则为2m+2。
12.3、纹理控制
纹理图像在映射到物体时会产生许多问题。这些问题主要有纹理图像的纹理怎样对应到屏幕上的像素、怎样通过纹理贴图实现纹理缩放和纹理重复等。OpenGL中的纹理控制函数是
void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param);
控制纹素映射到片元(fragment)时怎样对待纹理。第一个参数target可以是GL_TEXTURE_1D或GL_TEXTURE_2D,它指出是为一维或二维纹理说明参数;后两个参数的可能值见表12-1所示。
参数 | 值 |
GL_TEXTURE_WRAP_S | GL_CLAMP GL_REPEAT |
GL_TEXTURE_WRAP_T | GL_CLAMP GL_REPEAT |
GL_TEXTURE_MAG_FILTER | GL_NEAREST GL_LINEAR |
GL_TEXTURE_MIN_FILTER | GL_NEAREST GL_LINEAR GL_NEAREST_MIPMAP_NEAREST GL_NEAREST_MIPMAP_LINEAR GL_LINEAR_MIPMAP_NEAREST GL_LINEAR_MIPMAP_LINEAR |
表12-1 放大和缩小滤波方式 |
12.3.1 滤波
一般来说,纹理图像为正方形或长方形。但当它映射到一个多边形或曲面上并变换到屏幕坐标时,纹理的单个纹素很少对应于屏幕图像上的象素。根据所用变换和所用纹理映射,屏幕上单个象素可以对应于一个纹素的一小部分(即放大)或一大批纹素(即缩小)。下面用函数glTexParameter*()说明放大和缩小的方法:
glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameter*(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
实际上,第一个参数可以是GL_TEXTURE_1D或GL_TEXTURE_2D,即表明所用的纹理是一维的还是二维的;第二个参数指定滤波方法,其中参数值GL_TEXTURE_MAG_FILTER指定为放大滤波方法,GL_TEXTURE_MIN_FILTER指定为缩小滤波方法;第三个参数说明滤波方式,其值见表12-1所示。
若选择GL_NEAREST则采用坐标最靠近象素中心的纹素,这有可能使图像走样;若选择GL_LINEAR则采用最靠近象素中心的四个象素的加权平均值。GL_NEAREST所需计算比GL_LINEAR要少,因而执行得更快,但GL_LINEAR提供了比较光滑的效果。GL_LINEAR_MIPMAP_LINEAR使用线性均和计算两个纹理的值。GL_LINEAR_MIPMAP_NEAREST线性地改写临近的纹理单元值。GL_NEAREST_MIPMAP_LINEAR在两个纹理中选择最临近的纹理,并取它们之间的线性均和值。GL_NEAREST_MIPMAP_NEAREST选择最邻近的纹理单元值。
12.3.2 重复与约简
纹理坐标可以超出(0, 1)范围,并且在纹理映射过程中可以重复映射或约简映射。在重复映射的情况下,纹理可以在s,t方向上重复,即:
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameterfv(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
若将参数GL_REPEAT改为GL_CLAMP,则所有大于1的纹素值都置为1,所有小于0的值都置为0。参数设置参见表12-1。
12.4、映射方式
在本章的第一个例程中,纹理图像是直接作为画到多边形上的颜色。实际上,可以用纹理中的值来调整多边形(曲面)原来的颜色,或用纹理图像中的颜色与多边形(曲面)原来的颜色进行混合。因此,OpenGL提供了三种纹理映射的方式,这个函数是:
void glTexEnv{if}[v](GLenum target,GLenum pname,TYPE param);
设置纹理映射方式。参数target必须是GL_TEXTURE_ENV;若参数pname是GL_TEXTURE_ENV_MODE,则参数param可以是GL_DECAL(贴花)、GL_MODULATE(调整)、GL_REPLACE(替换、覆盖)、GL_ADD(添加)或GL_BLEND(混合),以说明纹理值怎样与原来表面颜色的处理方式;若参数pname是GL_TEXTURE_ENV_COLOR,则参数param是包含四个浮点数(分别是R、G、B、A分量)的数组,这些值只在采用GL_BLEND纹理函数时才有用。
12.5、纹理坐标
12.5.1 坐标定义
在绘制纹理映射场景时,不仅要给每个顶点定义几何坐标,而且也要定义纹理坐标。经过多种变换后,几何坐标决定顶点在屏幕上绘制的位置,而纹理坐标决定纹理图像中的哪一个纹素赋予该顶点。并且顶点之间的纹理坐标插值与基础篇中所讲的平滑着色插值方法相同。
纹理图像是方形数组,纹理坐标通常可定义成一、二、三或四维形式,称为s,t,r和q坐标,以区别于物体坐标(x, y, z, w)和其他坐标。一维纹理常用s坐标表示,二维纹理常用(s, t)坐标表示,目前忽略r坐标,q坐标象w一样,一半值为1,主要用于建立齐次坐标。OpenGL坐标定义的函数是:
void gltexCoord{1234}{sifd}[v](TYPE coords);
设置当前纹理坐标,此后调用glVertex*()所产生的顶点都赋予当前的纹理坐标。对于gltexCoord1*(),s坐标被设置成给定值,t和r设置为0,q设置为1;用gltexCoord2*()可以设置s和t坐标值,r设置为0,q设置为1;对于gltexCoord3*(),q设置为1,其它坐标按给定值设置;用gltexCoord4*()可以给定所有的坐标。使用适当的后缀(s,i,f或d)和TYPE的相应值(GLshort、GLint、glfloat或GLdouble)来说明坐标的类型。注意:整型纹理坐标可以直接应用,而不是象普通坐标那样被映射到[-1, 1]之间。
12.5.2 坐标自动产生
在某些场合(环境映射等)下,为获得特殊效果需要自动产生纹理坐标,并不要求为用函数gltexCoord*()为每个物体顶点赋予纹理坐标值。OpenGL提供了自动产生纹理坐标的函数,其如下:
void glTexGen{if}[v](GLenum coord,GLenum pname,TYPE param);
自动产生纹理坐标。第一个参数必须是GL_S、GL_T、GL_R或GL_Q,它指出纹理坐标s,t,r,q中的哪一个要自动产生;第二个参数值为GL_TEXTURE_GEN_MODE、GL_OBJECT_PLANE或 GL_EYE_PLANE;第三个参数param是一个定义纹理产生参数的指针,其值取决于第二个参数pname的设置,当pname为GL_TEXTURE_GEN_MODE时,param是一个常量,即GL_OBJECT_LINEAR、GL_EYE_LINEAR或GL_SPHERE_MAP,它们决定用哪一个函数来产生纹理坐标。对于pname的其它可能值,param是一个指向参数数组的指针。下面是一个运用自动产生纹理坐标函数的实例:
例12-1 纹理坐标自动产生例程(texpot.c)
图12-2 贴纹理的茶壶 |
转载地址:http://blog.csdn.net/hgl868/article/details/40375615