您的位置:首页 > 运维架构

OpenGL系统设计-纹理贴图(2)

2009-10-29 22:06 393 查看

1.1 位图纹理

纹理可以映射到平面上,也可以映射到曲面上,并且还可以多层映射。多层映射使用的是多重纹理。
使用纹理映射时,首先要定义一个纹理,然后控制纹理的滤波,说明映射的方式,在绘制顶点时同时给出其纹理坐标即可。

理论上来说,只要是图形文件都可以作为纹理贴图,不过最常用的还是BMP、JPEG、TGA文件。BMP文件处理方便,JPEG文件压缩率高,而TGA文件一贯作为纹理贴图文件用于三维动画设计软件中,如3DS MAX等。QUAKE III是用的是JPEG和TGA文件。

首先我们来制作一个简单的纹理,将一个笑脸符号映射在立方体上。
创建笑脸纹理的函数是HappyTexture,该函数返回一个GLuint的纹理名。在HappyTexture中首先建立一个8X8的数组HappyBitmap,其中取0的元素表示黑色,取1的元素表示黄色。为了转换成RGB类型的数据,再定义一个纹理像素数组TexImage[8][8][3],进行RGB值的转换。然后调用glGenTextures()函数对纹理在程序中注册。glGenTextures()的第一个参数表示需要注册的纹理个数,第二个参数表示纹理指针。接着调用glBindTexture()将纹理绑定,其中第一个参数GL_TEXTURE_2D表示我们将使用二维纹理,如果是GL_TEXTURE_1D则表示使用一维纹理,第二个参数仍然是纹理指针,指向纹理数组的第一个元素的地址。
接下来要调用glTexImage2D生成纹理的数据,glTexImage2D的原型如下:

void glTexImage2D(
GLenum target,
GLint level,
GLint internalformat,
GLsizei width,
GLsizei height,
GLint border,
GLenum format,
GLenum type,
const GLvoid *pixels
);
其中第一个参数表示纹理的维数,一维或者二维,和glBindTexture()的第一个参数一致。第二个参数表示纹理的细节等级,如果使用MIPMAP纹理映射,则level的取值n表示第n级纹理,对于普通纹理,取0即可。第三个参数internalformat表示使用的颜色组合,通常取值为1,2,3,4。1表示只使用红色,2表示只使用红色和绿色,3表示使用红、绿、蓝三种颜色,4表示除了使用红绿蓝三种颜色外,还使用ALPHA。第三、四个参数分别是纹理数据图像的宽度和高度。第5个参数border是纹理的边界宽度,取值为0和1。第六个参数format表示纹理图像的格式,可以取的值如表5-1说明如下:

表5-1 纹理数据格式

取值
含义
GL_COLOR_INDEX
纹理数据中每一个元素是一个颜色的索引值
GL_RED
纹理数据中每一个元素都是红色分量的值,此时OpenGL会将绿色和蓝色置为0.0,Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_GREEN
纹理数据中每一个元素都是绿色分量的值,此时OpenGL会将蓝色和红色置为0.0,Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_BLUE
纹理数据中每一个元素都是蓝色分量的值,此时OpenGL会将绿色和红色置为0.0,Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_ALPHA
纹理数据中每一个元素都是Alpha分量的值,此时OpenGL会将红色、绿色和蓝色置为0.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_RGB
纹理数据中每组元素都是红色、绿色、蓝色的值,此时OpenGL会将Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_RGBA
纹理数据中每组元素都是红色、绿色、蓝色和Alpha的值,此时OpenGL会将Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_BGR_EXT
纹理数据中每组元素都是蓝色、绿色、红色的值,注意这里的三种颜色分量的顺序和上面的不同,这是因为Windows的位图格式用的是这种顺序,OpenGL和Windows位图保持匹配。
GL_BGRA_EXT
纹理数据中每组元素的顺序是蓝色、绿色、红色和Alpha值。
GL_LUMINANCE
纹理数据中每组元素都是一个亮度值,OpenGL会将红色、绿色、蓝色都乘以该值,并将Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
GL_LUMINANCE_ALPHA
纹理数据中每组元素都是一个亮度值和一个Alpha,OpenGL会将红色、绿色、蓝色都乘以该亮度值,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]。
第七个参数type表示纹理数据的类型,有GL_UNSIGNED_BYTE, GL_BYTE, GL_BITMAP, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT和GL_FLOAT八种类型。第九个参数pixels就是纹理数据在内存中的地址指针。
纹理创建成功后,还需要调用glTexParameteri()设置纹理的参数,glTexParameteri()函数的原型如下:
void glTexParameteri(
GLenum target,
GLenum pname,
GLint param
);

它的作用是设置纹理参数,其中第一个参数target表示是一维还是二维纹理,第二个参数pname就是需要设置的参数,第三个参数param就是第二个参数pname的值。
如果一切OK,就返回生成的纹理值。

GLuint HappyTexture()
{
GLuint tex;
GLubyte HappyBitmap[8][8] =
{0,0,0,0,0,0,0,0,
0,0,1,1,1,1,0,0,
0,1,0,1,1,0,1,0,
0,1,1,1,1,1,1,0,
0,1,0,1,1,0,1,0,
0,1,1,0,0,1,1,0,
0,0,1,1,1,1,0,0,
0,0,0,0,0,0,0,0};
GLubyte TexImage[8][8][3];

for(int i=0; i<8; i++)
for(int j=0; j<8; j++)
{
TexImage[i][j][0] = HappyBitmap[j][i]*255;
TexImage[i][j][1] = HappyBitmap[j][i]*255;
TexImage[i][j][2] = 0;
}

glGenTextures( //为纹理分配内存
1, //纹理个数
&tex); //把其标识赋值与tex

glBindTexture( //绑定纹理
GL_TEXTURE_2D, //二维纹理
tex); //使用纹理

glTexImage2D( //为当前纹理设置像素数据
GL_TEXTURE_2D, //是二维纹理还是一维纹理
0, //纹理的细节等级
3, //纹理的每个像素包含几种颜色成份
pBitmap->sizeX, //图像的宽度
pBitmap->sizeY, //图像的高度
0, //纹理的边界宽度
GL_RGB, //图像的格式
GL_UNSIGNED_BYTE, //图像的数据类型
pBitmap->data); //图像的像素数据

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线性滤波
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线性滤波

return tex;
}

为了使用纹理,必须在初始化OpenGL时使用启动glEnable(GL_TEXTURE_2D)纹理映射,否则不论后面怎样操作都无法实现纹理。启动纹理映射在glInit()中完成。
在前面的程序中,我们都使用了白色的背景,看起来有些刺眼。为了使背景的颜色更加柔和一些,我们在glInit()中改变glClearColor的参数,使用深灰色的背景。
程序创建纹理时一次性的,所以也放在glInit()中。这里选择的纹理图像文件的大小是有要求的,其宽和高都必须是2的n次方,否则无法正常显示。Quake III中的纹理图形的宽和高通常是32、64、128、256、512。当图像太大时,对程序的效率会有影响。

int glInit()
{

// 启用纹理映射
glEnable(GL_TEXTURE_2D);

//启用阴影平滑(Smooth Shading)。
glShadeModel(GL_SMOOTH);

//使用深灰色背景,颜色比较柔和一些
glClearColor(0.5f,0.5f,0.5f,0.0f);

//设置深度缓冲
glClearDepth(1.0f);

//启动深度测试
glEnable(GL_DEPTH_TEST);

//深度测试的类型
glDepthFunc(GL_LEQUAL);

//进行透视修正
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);

//创建一个纹理
g_Texture[0] = HappyTexture(); //GLuint g_Texture[1]全局变量在文件头定义
if(!g_Texture[0]) //创建失败则返回
{
MessageBox(g_hWnd, "创建纹理失败!", "提示", MB_OK);
return FALSE;
}

return TRUE;
}

glMain()仍然比较简单,最重要的是增加的glBindTexture(GL_TEXTURE_2D, g_Texture[0])一行代码,它在glBegin之前设置当前纹理为g_Texture[0]。然后调用DrawCube()绘制立方体。

void glMain()
{

static int angle =0;

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); //加载单位矩阵

glTranslatef(0.0f, 0.0f, -60.0f);
glRotated(angle, 1, 1, 0);

//和下面的glBegin~glEnd块间的图形绑定纹理
glBindTexture(GL_TEXTURE_2D, g_Texture[0]); // 选择纹理
DrawCube();
angle++;

SwapBuffers(g_hDC);
}

不可忽视的是,在DrawCube中必须使用glTexCoord2f设置每一个顶点的纹理坐标值,如下所示:

void DrawCube()
{
glBegin(GL_QUADS); // 开始绘制立方体
glTexCoord2f(0.0f, 0.0f);glVertex3f( 1.0f, 1.0f,-1.0f); //顶面
glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f, 1.0f,-1.0f);
glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);glVertex3f( 1.0f, 1.0f, 1.0f);

glTexCoord2f(1.0f, 0.0f);glVertex3f( 1.0f,-1.0f, 1.0f); // 底面
glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f,-1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f);glVertex3f( 1.0f,-1.0f,-1.0f);

glTexCoord2f(0.0f, 1.0f);glVertex3f( 1.0f, 1.0f, 1.0f); // 前面
glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f);glVertex3f( 1.0f,-1.0f, 1.0f);

glTexCoord2f(1.0f, 1.0f);glVertex3f( 1.0f,-1.0f,-1.0f); //后面
glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f, 1.0f,-1.0f);
glTexCoord2f(1.0f, 0.0f);glVertex3f( 1.0f, 1.0f,-1.0f);

glTexCoord2f(1.0f, 0.0f);glVertex3f(-1.0f, 1.0f, 1.0f); //左面
glTexCoord2f(1.0f, 1.0f);glVertex3f(-1.0f, 1.0f,-1.0f);
glTexCoord2f(0.0f, 1.0f);glVertex3f(-1.0f,-1.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f);glVertex3f(-1.0f,-1.0f, 1.0f);

glTexCoord2f(0.0f, 0.0f);glVertex3f( 1.0f, 1.0f,-1.0f); //右面
glTexCoord2f(1.0f, 0.0f);glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(1.0f, 1.0f);glVertex3f( 1.0f,-1.0f, 1.0f);
glTexCoord2f(0.0f, 1.0f);glVertex3f( 1.0f,-1.0f,-1.0f);
glEnd(); // 立方体绘制结束
}

运行程序后,我们可以看到立方体的六个面都贴上了笑脸,围绕着X、Y轴旋转,效果如图5-6所示。

图5-6 简单位图纹理
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: