hyde
路人甲
路人甲
  • 注册日期2003-09-24
  • 发帖数555
  • QQ
  • 铜币1457枚
  • 威望0点
  • 贡献值0点
  • 银元0个
阅读:1223回复:0

OpenGL系列讲座(16)

楼主#
更多 发布于:2004-03-21 11:03
第二章  纹理映射
 

 

在三维图形中,纹理映射(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。

 

喜欢0 评分0
夜落了,风静了,我喜欢一本书,一杯茶,一粒摇曳的烛光...
游客

返回顶部