OpenGL学习——glut/ 纹理贴图一
本篇文章为纹理贴图的第一次学习,没太多时间去深入的学,把每个函数都搞得明明白白,所以这里自己的理解写的比较少。别人总结的还蛮好的,所以我主要是总结了几篇其他文章的内容,如果不去深究每个函数,我觉得足以让你明白纹理贴图的一个大致操作流程。
一、创建纹理对象,并为它指定一个纹理
启用纹理贴图
glEnable(GL_TEXTURE_2D);
定义一个纹理对象编号
static GLuint texName;
创建一个纹理对象,&texName指向纹理索引
glGenTextures(1, &texName);
绑定纹理:改变OpenGL状态,使得后续的纹理操作都对texName指向的2D纹理生效
glBindTexture(GL_TEXTURE_2D, texName);
二、载入纹理,设置纹理参数
- 纹理过滤函数
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);
这些参数先不用管,就是说贴图时的各种限制和模式。
- 指定纹理贴图与材质的混合模式
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
材质这个不是太懂。
- 为纹理对象指定一个纹理
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来加载图像,这个我还没有研究,但肯定比这篇文章的示例代码简单些。
他这个代码是手动分配内存的。
#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;
}