在了解纹理贴图之前,我们先要对BMP位图文件要有一定的了解。在Windows中,BMP格式是一个很常见的图像文件储存格式。位图文件由4个部分构成:位图文件头,位图信息头,彩色表以及定义位图的字节列阵。24位的真彩色图像不使用彩色表,所以可以暂时不用管它。
我们写个函数
int LoadBitmap(constchar *file)
图像bmp文件的地址是file,返回值int我们等一下再说为什么。
在windows.h中,定义了两个结构体BITMAPFILEHEADER和BITMAPINFOHEADER分别表示位图文件头和位图信息头。然后在定义一个byte数组image用于读入位图的数据信息。Width和height用于接受图像的长宽(单位:像素)。注意:每个像素因为由RGB三种不同光组合而成,所以实际上image数组保存数据的长度是width*height*3。
int width,height,i;
byte *image; //接受图像数据
FILE *fp; //文件指针
BITMAPFILEHEADER FileHeader; //接受位图文件头
BITMAPINFOHEADER InfoHeader; //接受位图信息头
fp=fopen(file,"rb");
if (fp == NULL)
{
perror("LoadBitmap"); //打开文件失败
return -1;
}
fread(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
if(FileHeader.bfType != BITMAP_ID) //确保文件是一个位图文件,效验文件类型
{
printf("Error: This file is not a bmp file!");
fclose(fp);
return -1;
}
fread(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
width=InfoHeader.biWidth;
height=InfoHeader.biHeight;
if (InfoHeader.biSizeImage == 0) //确保图像数据的大小
{
InfoHeader.biSizeImage = width*height*3;
}
fseek(fp, FileHeader.bfOffBits, SEEK_SET); //将文件指针移动到实际图像数据处
image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); //申请空间
if (image == NULL)
{
free(image);
printf("Error: No enough space!");
return -1;
}
fread(image, 1, InfoHeader.biSizeImage, fp);
这样图像文件的数据就算是被读入了,但是还有一个步骤没有完成。因为BMP文件的像素信息是按照BGR的顺序排列的。于是我们需要写一个交换器,把每一个像素的B值和R值交换一下,以保证OpenGL可以顺利地生成纹理。
for(i=0; i<InfoHeader.biSizeImage; i+=3)
{
t=image[i];
image[i]=image[i+2];
image[i+2]=t;
}
fclose(fp);
好的,至此我们已经做好了准备工作。接下来的工作就是要调用OpenGL函数把图像指定为纹理。
首先,我们需要用一个非0的无符号整数作为纹理对象名。为了避免重用对象名,使用void glGenTexture(GLsizei n,GLuint * textureNames)函数来获得未被占用的对象名。这个函数可以通过textureNames数组返回n个未使用的纹理对象名。
unsigned int ID;
glGenTextures(1, &ID);
再调用void glBindTexture(Glenum target,Gluint textureName)函数,创建纹理。其中target说明你创建的是几维的纹理,如:GL_TEXTURE_2D..textureName就是刚刚获取到的对象名。
glBindTexture(GL_TEXTURE_2D, ID);
接下来我们需要告诉OpenGL,当我们的图像映射到多边形表面并转换为屏幕坐标的时候,应该如何让像素一一对应。如图,一个正方形的图像纹理,映射到一个如右图的长方形表面时,应该如何映射。
比如说这两种贴图方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
glTexParameteri这个函数用于指定放大和缩小过滤方法。更多的参数和用法还是参见红书第9章吧,这里鉴于篇幅就不多介绍了。
最后,在调用gluBuild2Dmipmaps函数生成纹理。
glBuildImage2D(
GL_TEXTURE_2D, //申明纹理是2维纹理
3, //像素是有多少个颜色组成,如3个RGB,4个RGBA
width,
height,
GL_RGB, //有哪几个颜色组成
GL_UNSIGNED_BYTE, //数据的数据类型
Image //数据的地址
);
在return ID将ID返回。
LoadBitmap函数就算是写完了。
这个函数的用法很简单,像这样:
int ID;
ID=LoadBitmap(“Photo.bmp”);
if (ID == -1)
printf("图像读取失败!");
OpenGL_Draw_BMP(ID); //这个函数负责接受ID,并绘制图像
接下来的工作就是把OpenGL_Draw_BMP()函数完成,也就是完成绘制部分的工作。
首先我们需要启动纹理贴图功能,调用
glEnable(GL_TEXTURE_2D);
然后是绘制过程,glTexCoord2f指定纹理的坐标,表示把纹理坐标的4个角分配给这个多边形。
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex2f(-1,-1);
glTexCoord2f(1.0f, 0.0f);glVertex2f(1,-1);
glTexCoord2f(1.0f, 1.0f);glVertex2f(1,1);
glTexCoord2f(0.0f, 1.0f);glVertex2f(-1,1);
glEnd();
最后,glutSwapBuffers()就完成了!
总结一下,大体的流程是这样的。先调用LoadBitmap读取和分配纹理对象,再调用display函数把图片显示一下。
附本节代码:
#include<GL/glut.h>
#include<stdio.h>
#include<math.h>
#include<time.h>
#include<stdlib.h>
#include<windows.h>
#include<vector>
#include<iostream>
using std::vector;
using std::cout;
using std::endl;
unsignedint ID;
int LoadBitmap(constchar *file)
{
unsignedint ID; //纹理的id
int width,height,i;
byte *image,t; //接受图像数据
FILE *fp; //文件指针
BITMAPFILEHEADER FileHeader; //接受位图文件头
BITMAPINFOHEADER InfoHeader; //接受位图信息头
fp=fopen(file,"rb");
if (fp == NULL)
{
perror("LoadBitmap"); //打开文件失败
return -1;
}
fread(&FileHeader, sizeof(BITMAPFILEHEADER), 1, fp);
if(FileHeader.bfType != 0x4D42) //确保文件是一个位图文件,效验文件类型
{
printf("Error: This file is not a bmp file!");
fclose(fp);
return -1;
}
fread(&InfoHeader, sizeof(BITMAPINFOHEADER), 1, fp);
width=InfoHeader.biWidth;
height=InfoHeader.biHeight;
if (InfoHeader.biSizeImage == 0) //确保图像数据的大小
{
InfoHeader.biSizeImage = width*height*3;
}
fseek(fp, FileHeader.bfOffBits, SEEK_SET); //将文件指针移动到实际图像数据处
image=(byte *)malloc(sizeof(byte)*InfoHeader.biSizeImage); //申请空间
if (image == NULL)
{
free(image);
printf("Error: No enough space!");
return -1;
}
fread(image, 1, InfoHeader.biSizeImage, fp);
for(i=0; i<InfoHeader.biSizeImage; i+=3)
{
t=image[i];
image[i]=image[i+2];
image[i+2]=t;
}
fclose(fp);
glGenTextures(1, &ID);
glBindTexture(GL_TEXTURE_2D, ID);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width,
height, GL_RGB, GL_UNSIGNED_BYTE,
image);
return ID;
}
void Draw()
{
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, ID);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex2f(-1,-1);
glTexCoord2f(1.0f, 0.0f);glVertex2f(1,-1);
glTexCoord2f(1.0f, 1.0f);glVertex2f(1,1);
glTexCoord2f(0.0f, 1.0f);glVertex2f(-1,1);
glEnd();
glDisable(GL_TEXTURE_2D);
glutSwapBuffers();
}
void Update()
{
glutPostRedisplay();
}
void Reshape(int w,int h)
{
w=w>h?h:w;
glViewport(0,0,(GLsizei)w,(GLsizei)w);
}
void init()
{
ID=LoadBitmap("Hulaoshi.bmp");
if (ID == -1)
exit(0);
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE);
glutInitWindowPosition(100, 100);
glutInitWindowSize(400, 400);
glutCreateWindow("ShowBitmap");
glutReshapeFunc(&Reshape);
glutIdleFunc(&Update);
glutDisplayFunc(&Draw);
init();
glutMainLoop();
return 0;
}
说在后面的话。额...最后还是从Ubuntu换成了win7,比较MSOffice用起来比LibreOffice方便啊。。