OpenGL入门学习[十一 03]bmp纹理

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.0Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]

GL_GREEN

纹理数据中每一个元素都是绿色分量的值,此时OpenGL会将蓝色和红色置为0.0Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]

GL_BLUE

纹理数据中每一个元素都是蓝色分量的值,此时OpenGL会将绿色和红色置为0.0Alpha置为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的位图格式用的是这种顺序,OpenGLWindows位图保持匹配。

GL_BGRA_EXT

纹理数据中每组元素的顺序是蓝色、绿色、红色和Alpha值。

GL_LUMINANCE

纹理数据中每组元素都是一个亮度值,OpenGL会将红色、绿色、蓝色都乘以该值,并将Alpha置为1.0,然后将每个分量乘以一个比例因子GL_c_SCALE,再加上GL_c_BIAS,最后圆整到 [0,1]

GL_LUMINANCE_ALPHA

纹理数据中每组元素都是一个亮度值和一个AlphaOpenGL会将红色、绿色、蓝色都乘以该亮度值,然后将每个分量乘以一个比例因子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_INTGL_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轴旋转

你可能感兴趣的:(windows,3D,图形,OpenGL,Studio,Visual)