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

来源:互联网 发布:开通淘宝客要求 编辑:程序博客网 时间:2024/04/30 00:13

 

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轴旋转,效果如图5-6所示。

 

图5-6   简单位图纹理