欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

OpenGL学习——glut/ 纹理贴图一

程序员文章站 2022-03-25 20:26:13
...

本篇文章为纹理贴图的第一次学习,没太多时间去深入的学,把每个函数都搞得明明白白,所以这里自己的理解写的比较少。别人总结的还蛮好的,所以我主要是总结了几篇其他文章的内容,如果不去深究每个函数,我觉得足以让你明白纹理贴图的一个大致操作流程。

一、创建纹理对象,并为它指定一个纹理

启用纹理贴图

glEnable(GL_TEXTURE_2D);

定义一个纹理对象编号

static GLuint texName;

创建一个纹理对象,&texName指向纹理索引

glGenTextures(1, &texName);

绑定纹理:改变OpenGL状态,使得后续的纹理操作都对texName指向的2D纹理生效

glBindTexture(GL_TEXTURE_2D, texName);

二、载入纹理,设置纹理参数

  1. 纹理过滤函数

glTexParameteri()

图象从纹理图象空间映射到帧缓冲图象空间(映射需要重新构造纹理图像,这样就会造成应用到多边形上的图像失真),这时就可用glTexParmeteri()函数来确定如何把纹理象素映射成像素。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

这些参数先不用管,就是说贴图时的各种限制和模式。

  1. 指定纹理贴图与材质的混合模式

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

材质这个不是太懂。

  1. 为纹理对象指定一个纹理

glTexImage2D (
GLenum target, //指定的目标,可以使用GL_TEXTURE_2D
GLint level, //“多重细节层次”,不考虑多重纹理的话设置为零。
GLint internalformat,//RGB数据存储的格式,比如GL_RGB
GLsizei width,
GLsizei height, //二维纹理像素的宽度和高度。
GLint border, //纹理边框的大小,不使用纹理边框设置为零。
GLenum format, //
GLenum type, //数据格式和数据保存形式。
const GLvoid *pixels //保存了纹理图像的内参块地址。
);

此函数的参数可以像下面这样设置:

glTexImage2D(
GL_TEXTURE_2D,        //target,目标纹理
0, 			          //分辨率级数参数,默认为0
GL_RGBA, 	       	  //纹理单元格式
checkImageWidth,      //纹理图像的宽
checkImageHeight,     //纹理图像的高
0, 			          //纹理图像边框的宽度,0或1
GL_RGBA,              //纹理像素数据的格式
GL_UNSIGNED_BYTE, 	  //像素数据类型
checkImage	          //内存中指向纹理图像的指针
);

三、纹理映射,绘制纹理图像

纹理映射跟颜色的绘制一样,需要指定每一个顶点在纹理图像中所对应的位置,OpenGL会自动计算出顶点之间的其他点在纹理图像中应该对应的位置。这里注意纹理图像的坐标范围是从(0,0)到(1,1),左下角为(0,0),右上角为(1,1)。比如,将图像映射到多个四边形上:

    // 绘制底面以及纹理
    glBindTexture(GL_TEXTURE_2D, texSea);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);   //纹理坐标配置函数
    glVertex3f(-8.0f, -8.0f, 0.0f);
    glTexCoord2f(0.0f, 3.0f);
    glVertex3f(-8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 3.0f);
    glVertex3f(8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 0.0f);
    glVertex3f(8.0f, -8.0f, 0.0f);
    glEnd();

此代码显示3行3列个位图(共9个)(如果坐标是小于1的,那就是正常的映射),坐标可以这样映射:(0.0f, 0.0f)、(0.0f, 3.0f)、(3.0f, 3.0f)、(3.0f, 0.0f)。

glTexCoord2f()主要与glVertex3f()配合使用,glTexCoord2f()是配置纹理坐标,glVertex3f()是配置图形坐标。

四、其他补充

下面这个代码的原文章有大致这样说明,读取纹理图片到内存时,位图的大小有规定的大小,宽度什么的有些要求,如果长宽有超过当前版本所支持最大长宽数值,还需要对图像进行缩放。这里我们先不管这个格式问题了,给的位图是标准的。

还有代码中关于读取位图的操作,我觉得有点麻烦,我看有些直接使用stb_image.h来加载图像,这个我还没有研究,但肯定比这篇文章的示例代码简单些。

他这个代码是手动分配内存的。
OpenGL学习——glut/ 纹理贴图一

#define WindowWidth  400
#define WindowHeight 400
#define WindowTitle  "纹理贴图"

#include <gl/glut.h>
#include <stdio.h>
#include <stdlib.h>
#define GL_BGR_EXT 0x80E0   //我这个版本找不到这个宏,需要自己定义

//定义两个纹理对象编号
GLuint texSea;
GLuint texSky;

#define BMP_Header_Length 54  //图像数据在内存块中的偏移量

static GLfloat angle = 0.0f;   //旋转角度

//读取一个BMP文件作为纹理。如果失败,返回0,如果成功,返回纹理编号.
//这个代码的处理位图的操作太麻烦了,可以先不管,知道他把位图保存到了一个字节变量pixel中就好
GLuint load_texture(const char* file_name)
{
    GLint width, height, total_bytes;
    GLubyte* pixels = 0;
    GLuint last_texture_ID=0, texture_ID = 0;

    // 打开文件,如果失败,返回
    FILE* pFile = fopen(file_name, "rb");
    if( pFile == 0 )
    {
        printf("Wrong!!!!\n");
        return 0;
    }

    // 读取文件中图象的宽度和高度
    fseek(pFile, 0x0012, SEEK_SET);
    fread(&width, 4, 1, pFile);
    fread(&height, 4, 1, pFile);
    fseek(pFile, BMP_Header_Length, SEEK_SET);

    // 计算每行像素所占字节数,并根据此数据计算总像素字节数
    GLint line_bytes = width * 3;
    while( line_bytes % 4 != 0 )
        ++line_bytes;
    total_bytes = line_bytes * height;

    // 根据总像素字节数分配内存
    pixels = (GLubyte*)malloc(total_bytes);
    if( pixels == 0 )
    {
        fclose(pFile);
        return 0;
    }

    // 读取像素数据
    if( fread(pixels, total_bytes, 1, pFile) <= 0 )
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }


    // 分配一个新的纹理编号
    glGenTextures(1, &texture_ID);
    if( texture_ID == 0 )
    {
        free(pixels);
        fclose(pFile);
        return 0;
    }

    // 绑定新的纹理,载入纹理并设置纹理参数
    // 在绑定前,先获得原来绑定的纹理编号,以便在最后进行恢复
    GLint lastTextureID = last_texture_ID;
    glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID);

    glBindTexture(GL_TEXTURE_2D, texture_ID);   //绑定纹理

    //设置4个常用的纹理参数。。如何把纹理像素映射成像素
    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_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);

    glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); //指定纹理贴图与材质的混合模式

    //参数: 目标纹理,多重细节层次(不考虑多重纹理为0),RGB数据存储格式,二维纹理像素宽高,纹理边框大小
    //       纹理像素数据的格式,数据保存形式(像素数据类型,字节),内存中指向纹理图像的指针(纹理图像的内参快地址)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
                 GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); //为纹理对象指定一个纹理

    glBindTexture(GL_TEXTURE_2D, lastTextureID);  //恢复之前的纹理绑定

    free(pixels);
    return texture_ID;
}


void display(void)
{
    // 清除屏幕
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    // 设置视角和观察点
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluPerspective(75, 1, 1, 21);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(-4, 7,7, 0, 0, 0, 0, 0, 1);

    glRotatef(angle, 0.0f, 0.0f, 1.0f); //旋转矩形,实现物体的旋转


    // 绘制底面以及纹理
    glBindTexture(GL_TEXTURE_2D, texSea);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);   //纹理坐标配置函数
    glVertex3f(-8.0f, -8.0f, 0.0f);
    glTexCoord2f(0.0f, 3.0f);
    glVertex3f(-8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 3.0f);
    glVertex3f(8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 0.0f);
    glVertex3f(8.0f, -8.0f, 0.0f);
    glEnd();
    //比如显示3行2列个位图(共6个),坐标可以这样:(0.0f, 0.0f)、(0.0f, 3.0f)、(2.0f, 3.0f)、(2.0f, 0.0f)。
    //该函数主要与glVertex3f()配合使用,glTexCoord2f()是配置纹理坐标,glVertex3f()是配置图形坐标。

    // 绘制立面
    glBindTexture(GL_TEXTURE_2D, texSky);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-6.0f, -3.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-6.0f, -3.0f, 5.0f);
    glTexCoord2f(2.0f, 1.0f);
    glVertex3f(6.0f, -3.0f, 5.0f);
    glTexCoord2f(2.0f, 0.0f);
    glVertex3f(6.0f, -3.0f, 0.0f);
    glEnd();

    //绘制另外一个立面
    glBegin(GL_QUADS);
    glTexCoord2f(2.0f, 0.0f);
    glVertex3f(6.0f, -3.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(6.0f, 9.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(6.0f, 9.0f, 5.0f);
    glTexCoord2f(2.0f, 1.0f);
    glVertex3f(6.0f, -3.0f, 5.0f);
    glEnd();

    glutSwapBuffers();
}

//旋转角度
void myIdle(void)
{
    angle += 0.01f; //1.8f
    if( angle >= 360.0f )
        angle = 0.0f;
    display();
}

int main(int argc, char* argv[])
{
    // GLUT初始化
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA);
    glutInitWindowPosition(100, 100);
    glutInitWindowSize(WindowWidth, WindowHeight);
    glutCreateWindow(WindowTitle);

    glEnable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);    // 启用2D纹理功能

    texSea = load_texture("C:\\Users\\user\\Desktop\\贴图1_方块\\sea.bmp");
    texSky = load_texture("C:\\Users\\user\\Desktop\\贴图1_方块\\sky.bmp");  //加载纹理

    //绘图
    glutDisplayFunc(&display);
    glutIdleFunc(&myIdle);
    glutMainLoop();
    return 0;
}