OpenGL织梦之旅【第三章】第1节.纹理贴图

本文详细介绍了如何在OpenGL中使用纹理贴图,从理解BMP文件结构开始,通过读取BMP文件,进行颜色通道交换,然后生成纹理对象,设置纹理参数,最终实现纹理映射到多边形上。文章提供了LoadBitmap函数的实现,并展示了简单的使用方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

       在了解纹理贴图之前,我们先要对BMP位图文件要有一定的了解。在Windows中,BMP格式是一个很常见的图像文件储存格式。位图文件由4个部分构成:位图文件头,位图信息头,彩色表以及定义位图的字节列阵。24位的真彩色图像不使用彩色表,所以可以暂时不用管它。

   我们写个函数

int LoadBitmap(constchar *file)


      图像bmp文件的地址是file,返回值int我们等一下再说为什么。

      在windows.h中,定义了两个结构体BITMAPFILEHEADERBITMAPINFOHEADER分别表示位图文件头和位图信息头。然后在定义一个byte数组image用于读入位图的数据信息。Widthheight用于接受图像的长宽(单位:像素)。注意:每个像素因为由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 IDID返回。

      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方便啊。。

 

日期:2012/12/7

作者:plusplus7

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值