OpenGLut开发入门系列教程7:纹理贴图

系列教程索引:OpenGLut开发入门系列教程索引

上一篇:OpenGLut开发入门系列教程6:3D化图像

上一篇中的金字塔四个面使用的是颜色覆盖,本篇介绍使用图片覆盖面,这个操作称为纹理贴图(Texture Mapping)。

首先函数基本的架构是:

flow

在main函数中glut*Func()的参数为回调函数,需要在main函数外独立实现。

重要的是四个回调函数:

  • initGL,初始化函数
  • reshapeGL,尺寸调整函数,当窗口的尺寸发生变化时调用此函数重新绘图
  • keyboard,捕获键盘输入并处理
  • displayGL,将想要显示的图像绘制出来的函数

其他函数说明见OpenGL函数功能说明系列

贴图需要图片,有图片就需要读取图片来使用。

我们使用现有的图片库stb_image来读取不同格式的图片(支持JPG, PNG, TGA, BMP, PSD, GIF, HDR, PIC)。

1、包含stb头文件

1
2
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

2、加载图片

1
STBIDEF stbi_uc *stbi_load(char const *filename, int *x, int *y, int *channels_in_file, int desired_channels);

只需要文件路径准确,x/y为图片宽高,剩下两个参数保持默认就可以了。

3、释放图片

1
stbi_image_free(data);

4、创建纹理

1
void glGenTextures( GLsizei n, GLuint *textures );

第一个参数需要输入生成纹理的数量,然后把它们储存在第二个参数的unsigned int数组中。

5、绑定纹理

1
glBindTexture(GL_TEXTURE_2D, texture);

texture就是创建纹理中的数组。

6、生成纹理

1
2
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);

纹理坐标系

下图为普通图像坐标系:

axis

坐标原点在左上角。

下图为原始opengl纹理坐标:

axis

坐标原点在右下角。

对于一副图片,结果纹理坐标映射后就是:

1
2
3
4
5
6
12345678 56789123
23456789 45678912
34567891 34567891
45678912 23456789
56789123 12345678
... ...

可以看出图像是反的。

那么就需要变换一下坐标系:

这样纹理映射之后就是正确的图片了。

映射方法为:图片左上角的点坐标对应(0,0),以此类推。(本文)映射顺序为左下角开始,顺时针方向。

opengl三维世界坐标系为:

3d

  • OpenGL坐标系(物体、世界、照相机坐标系)属于右手坐标系
  • 设备坐标系使用的是左手坐标系

先用二维图测试一下:

二维矩形贴图

从阿里图库盗了一只狗:

dog

随便什么图片都行,但是要能看出上下是不是反的。

先加载图片数据,生成纹理数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

GLuint texture[1];//存储一个纹理

int width,height,nrChannels;

//加载图片并转换为纹理
void loadGLTextures(){
unsigned char *data = stbi_load("dog.png",&width,&height,&nrChannels,0);
printf("channels %d.\n",nrChannels);

//创建纹理
glGenTextures(1,&texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);//2维纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if(nrChannels==3){
glTexImage2D(GL_TEXTURE_2D, 0, 3,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(nrChannels==4){
glTexImage2D(GL_TEXTURE_2D, 0, 3,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
}
}

然后进行纹理坐标映射

1
2
3
4
5
6
7
glBegin(GL_POLYGON);//图像绘制部分
glTexCoord2f(0.0f, 1.0-0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0-1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(1.0f, 1.0-1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0-0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glEnd();
}

贴图效果为:

rect result

二维三角形贴图

贴个三角形:

将四边形的纹理

1
2
3
4
5
6
glBegin(GL_POLYGON);//图像绘制部分
glTexCoord2f(0.0f, 1.0-0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 1.0-1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(1.0f, 1.0-1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0-0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glEnd();

替换为

1
2
3
4
5
glBegin(GL_TRIANGLES);//图像绘制部分
glTexCoord2f(0.0f,1.0f);glVertex3f(-1.0f,-1.0f,0.0f);//点B
glTexCoord2f(0.5f,0.0f);glVertex3f(0.0f,1.0f,0.0f);//点A(x,y,z)
glTexCoord2f(1.0f,1.0f);glVertex3f(1.0f,-1.0f,0.0f);//点C
glEnd();

效果为:

triangle

立方体贴图

先测试一下立方体效果:

测试代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
#define STB_IMAGE_IMPLEMENTATION
#include "stb_image.h"

GLuint texture[1]; //存储一个纹理

int width, height, nrChannels;

//加载图片并转换为纹理
void loadGLTextures()
{
unsigned char *data = stbi_load("brick.jpg", &width, &height, &nrChannels, 0);

//创建纹理
glGenTextures(1, &texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]); //2维纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if (nrChannels == 3)
{
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
}
else if (nrChannels == 4)
{
glTexImage2D(GL_TEXTURE_2D, 0, 3, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
}
}

void display()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();

glTranslatef(0.0f, 0.0f, -6.0f); //平移

glRotatef(xrot, 1.0f, 0.0f, 0.0f); //旋转函数
glRotatef(yrot, 0.0f, 1.0f, 0.0f); //旋转函数
glRotatef(zrot, 0.0f, 0.0f, 1.0f); //旋转函数

glBegin(GL_QUADS); //图像绘制部分

// Front Face (note that the texture's corners have to match the quad's corners)
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f); // Top Left Of The Texture and Quad

// Right face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, -1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad

// Back Face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(1.0f, 1.0f, -1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad

// Left Face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, 1.0f); // Top Left Of The Texture and Quad

// Top Face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, 1.0f, -1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, 1.0f, -1.0f); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, 1.0f, 1.0f); // Top Right Of The Texture and Quad

// Bottom Face
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-1.0f, -1.0f, -1.0f); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 1.0f); // Top Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex3f(1.0f, -1.0f, 1.0f); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex3f(1.0f, -1.0f, -1.0f); // Bottom Right Of The Texture and Quad

glEnd();

xrot += 0.1f;
yrot += 0.1f;
zrot += 0.1f;

glutSwapBuffers();
}

测试效果:

dog

接下来我们给立方体贴砖。

准备一张砖(同样是盗的):

brick

只需要把代码里面的图片位置更换一下就可以了。

效果:

brick

纹理加载的流程为:
在initGL函数中创建、绑定、生成纹理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//创建纹理
glGenTextures(1,&texture[0]);
glBindTexture(GL_TEXTURE_2D, texture[0]);//2维纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if(nrChannels==3){
glTexImage2D(GL_TEXTURE_2D, 0, 3,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(nrChannels==4){
glTexImage2D(GL_TEXTURE_2D, 0, 3,width,height,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
}

在绘图函数paintGL中使用之前先绑定纹理表示使用的是哪一个纹理

1
glBindTexture(GL_TEXTURE_2D,texture[0]);//选择纹理

当然,我们也可以使用其他的纹理过滤方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
//创建纹理
glGenTextures(3,&texture[0]);

//纹理1-低质量缩放
glBindTexture(GL_TEXTURE_2D,texture[0]);//绑定纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if(c==3){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(c==4){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
}

//纹理2-线性缩放
glBindTexture(GL_TEXTURE_2D,texture[1]);//绑定纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if(c==3){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(c==4){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
}

//纹理3-映射缩放
glBindTexture(GL_TEXTURE_2D,texture[2]);//绑定纹理

//纹理过滤
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);

//二位纹理,细节等级,RGB3组件,宽,高,边框,rgb数据,无符号整数,数据
if(c==3){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(c==4){
glTexImage2D(GL_TEXTURE_2D, 0, 3,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
}

//二位纹理,RGB3颜色,宽,高,Rgb边框值,rgb数据,无符号整数,数据
if(c==3){
gluBuild2DMipmaps(GL_TEXTURE_2D, 3,w,h,GL_RGB,GL_UNSIGNED_BYTE,data);
}else if(c==4){
gluBuild2DMipmaps(GL_TEXTURE_2D, 3,w,h,GL_RGBA,GL_UNSIGNED_BYTE,data);
}

绘图之前决定启用那种模式

1
glBindTexture(GL_TEXTURE_2D,texture[filter]);//选择纹理

通过键盘进行切换

1
2
3
4
5
6
7
8
9
case 70:
case 102:
std::cout<<"Filter is: "<<filter<<std::endl;
filter+=1;
if(filter>2){
filter=0;
}
std::cout<<"Filter is: "<<filter<<" now."<<std::endl;
break;

三种模式我也没看出来什么区别

textures

可以调整旋转角度,距离窗口的远近

z

完整源码在OpenGL_Freshman下的OpenGLut的7中。

下一篇:OpenGLut开发入门系列教程8:光照