阅读:1346回复:0
OpenGL系列讲座(16)
第二章 纹理映射
在三维图形中,纹理映射(Texture Mapping)的方法运用得很广,尤其描述具有真实感的 物体。比如绘制一面砖墙,就可以用一幅真实的砖墙图像或照片作为纹理贴到一个矩形上, 这样,一面逼真的砖墙就画好了。如果不用纹理映射的方法,则墙上的每一块砖都必须作为 一个独立的多边形来画。另外,纹理映射能够保证在变换多边形时,多边形上的纹理图案也 随之变化。例如,以透视投影方式观察墙面时,离视点远的砖块的尺寸就会缩小,而离视点 较近的就会大些。此外,纹理映射也常常运用在其他一些领域,如飞行仿真中常把一大片植 被的图像映射到一些大多边形上用以表示地面,或用大理石、木材、布匹等自然物质的图像 作为纹理映射到多边形上表示相应的物体。 纹理映射有许多种情况。例如,任意一块纹理可以映射到平面或曲面上,且对光亮的物 体进行纹理映射,其表面可以映射出周围环境的景象;纹理还可按不同的方式映射到曲面上, 一是可以直接画上去(或称移画印花法),二是可以调整曲面颜色或把纹理颜色与曲面颜色混 合;纹理不仅可以是二维的,也可以是一维或其它维的。 本章将详细介绍OpenGL纹理映射有关的内容:基本步骤、纹理定义、纹理控制、映射方 式和纹理坐标等。 2.1 基本步骤 纹理映射是一个相当复杂的过程,这节只简单地叙述一下最基本的执行纹理映射所需的 步骤。基本步骤如下: 一、定义纹理; 二、控制滤波; 三、说明映射方式; 四、绘制场景,给出顶点的纹理坐标和几何坐标。 注意,纹理映射只能在RGBA方式下执行,不能运用于颜色表方式。 下面举出一个最简单的纹理映射应用例子texsmpl.c: 例 3-3 简单纹理映射应用例程 texsmpl.c #include "glos.h" #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h> void myinit(void); void makeImage(void); void CALLBACK myReshape(GLsizei w, GLsizei h); void CALLBACK display(void); /* 创建纹理 */ #define ImageWidth 64 #define ImageHeight 64 GLubyte Image[ImageWidth][ImageHeight][3]; void makeImage(void) { int i, j, r,g,b; for (i = 0; i < ImageWidth; i++) { for (j = 0; j < ImageHeight; j++) { r=(i*j)%255; g=(4*i)%255; b=(4*j)%255; Image[j][0] = (GLubyte) r; Image[j][1] = (GLubyte) g; Image[j][2] = (GLubyte) b; } } } void myinit(void) { glClearColor (0.0, 0.0, 0.0, 0.0); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LESS); makeImage(); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); /* 定义纹理 */ glTexImage2D(GL_TEXTURE_2D, 0, 3, ImageWidth, ImageHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, &Image[0][0][0]); /* 控制滤波 */ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); /* 说明映射方式*/ glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL); /* 启动纹理映射 */ glEnable(GL_TEXTURE_2D); glShadeModel(GL_FLAT); } void CALLBACK display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); /* 设置纹理坐标和物体几何坐标 */ glBegin(GL_QUADS); glTexCoord2f(0.0, 0.0); glVertex3f(-2.0, -1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(-2.0, 1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(0.0, 1.0, 0.0); glTexCoord2f(1.0, 0.0); glVertex3f(0.0, -1.0, 0.0); glTexCoord2f(0.0, 0.0); glVertex3f(1.0, -1.0, 0.0); glTexCoord2f(0.0, 1.0); glVertex3f(1.0, 1.0, 0.0); glTexCoord2f(1.0, 1.0); glVertex3f(2.41421, 1.0, -1.41421); glTexCoord2f(1.0, 0.0); glVertex3f(2.41421, -1.0, -1.41421); glEnd(); glFlush(); } void CALLBACK myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0, 1.0*(GLfloat)w/(GLfloat)h, 1.0, 30.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -3.6); } void main(void) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA); auxInitPosition (0, 0, 500, 500); auxInitWindow ("Simple Texture Map"); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); } 图 3-2-1 简单纹理映射 以上程序运行结果是将一块纹理映射到两个正方形上去。这两个正方形都是按透视投影 方式绘制,其中一个正对观察者,另一个向后倾斜45度角。图形的纹理是由函数makeImage() 产生的,并且所有纹理映射的初始化工作都在程序myinit()中进行。由glTexImage2d()说明 了一个全分辨率的图像,其参数指出了图像的尺寸、图像类型、图像位置和图像的其它特性; 下面连续调用函数glTexParameter*()说明纹理怎样缠绕物体和当象素与纹理数组中的单个 元素(texel,本书暂称纹素)不能精确匹配时如何过滤颜色;接着用函数glTexEnv*()设置 画图方式为GL_DECAL,即多边形完全用纹理图像中的颜色来画,不考虑多边形在未被纹理映 射前本身的颜色;最后,调用函数glEnable()启动纹理映射。子程序display()画了两个正 方形,其中纹理坐标与几何坐标一起说明,glTexCoord*()函数类似于glNormal*()函数,不 过它是设置纹理坐标,之后任何顶点都把这个纹理坐标与顶点坐标联系起来,直到再次调用 glTexCoord*()改变当前纹理坐标。 2.2 纹理定义 二维纹理定义的函数是: void glTexImage2D(GLenum target,GLint level,GLint components,GLsizei width, glsizei height,GLint border,GLenum format,GLenum type, const GLvoid *pixels); 定义一个二维纹理映射。 参数target是常数GL_TEXTURE_2D。 参数level表示多级分辨率的纹理图像的级数,若只有一种分辨率,则level设为0。 参数components是一个从1到4的整数,指出选择了R、G、B、A中的哪些分量用于调 整和混合,1表示选择了R分量,2表示选择了R和A两个分量,3表示选择了R、G、B三个 分量,4表示选择了R、G、B、A四个分量。 参数width和height给出了纹理图像的长度和宽度,参数border为纹理边界宽度,它 通常为0,width和height必须是2m+2b,这里m是整数,长和宽可以有不同的值,b是border 的值。纹理映射的最大尺寸依赖于OpenGL,但它至少必须是使用64x64(若带边界为66x66), 若width和height设置为0,则纹理映射有效地关闭。 参数format和type描述了纹理映射的格式和数据类型,它们在这里的意义与在函数 glDrawPixels()中的意义相同,事实上,纹理数据与glDrawPixels()所用的数据有同样的格 式。参数format可以是GL_COLOR_INDEX、GL_RGB、GL_RGBA、GL_RED、GL_GREEN、GL_BLUE、 GL_ALPHA、GL_LUMINANCE或GL_LUMINANCE_ALPHA(注意,不能用GL_STENCIL_INDEX和 GL_DEPTH_COMPONENT)。类似地,参数type是GL_BYPE、GL_UNSIGNED_BYTE、GL_SHORT、 GL_UNSIGNED_SHORT、GL_INT、GL_UNSIGNED_INT、GL_FLOAT或GL_BITMAP。 参数pixels包含了纹理图像数据,这个数据描述了纹理图像本身和它的边界。 一维纹理定义的函数是: 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。 2.3 纹理控制 OpenGL中的纹理控制函数是: void glTexParameter{if}[v](GLenum target,GLenum pname,TYPE param); 控制纹素映射到片元(fragment)时怎样对待纹理。第一个参数target可以是 GL_TEXTURE_1D或GL_TEXTURE_2D,它指出是为一维或二维纹理说明参数;后两个参数的可能 值见表3-2-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 __________________________________________________________________________ 表3-2-1 放大和缩小滤波方式 2.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指定为缩小滤波方法;第三个参数说明滤波方式,其 值见表3-2-1所示。 若选择GL_NEAREST则采用坐标最靠近象素中心的纹素,这有可能使图像走样;若选择 GL_LINEAR则采用最靠近象素中 心的四个象素的加权平均值。GL_NEAREST所需计算比 GL_LINEAR要少,因而执行得更快,但GL_LINEAR提供了比较光滑的效果。 2.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。参数设置参见表3-2-1。 |
|
|