纹理的作用:可以用来呈现图形,从而减少渲染的顶点数目。也可以用来保存着色器所需的数据信息,起到数据传输媒介作用。
纹理的属性:常用接口如下:
1.void glTexParamteri(GLenum target, GLenum pname, GLint param)
:void glTexParamteriv(GLenum target, GLenum pname, const GLint* param)
:void glTexParamterf(GLenum target, GLenum pname, GLfloat param)
:void glTexParamterfv(GLenum target, GLenum pname, const GLfloat* param)
:设置纹理属性参数。
.target表示纹理目标。通常是GL_TEXTURE_1D(一维纹理),GL_TEXTURE_2D(二维纹理),GL_TEXTURE_3D(三维纹理)等当中的一种。
.pname表示属性名。通常是GL_TEXTURE_WRAP_S(x轴环绕方式),GL_TEXTURE_WRAP_T(y轴环绕方式),GL_TEXTURE_WRAP_R(z轴环绕方式),GL_TEXTURE_BORDER_COLOR(边界颜色),GL_TEXTURE_MIN_FILTER(最小过滤),GL_TEXTURE_MAG_FILTER(最大过滤)等当中的一种。
.param表示属性参数值。
常用属性如下:
1.环绕方式:也就是超过纹理大小区域所采用的处理方式。
常见的环绕方式如下所示:
环绕方式 | 描述 |
GL_REPEAT | 对纹理的默认行为。重复纹理图像 |
GL_MIRRORED_REPEAT | 和GL_REPEAT一样,但每次重复图片是镜像放置的。 |
GL_CLAMP_TO_EDGE | 纹理坐标会被约束在0到1之间,超出的部分会重复纹理坐标的边缘,产生一种边缘被拉伸的效果。 |
GL_CLAMP_TO_BORDER | 超出的坐标为用户指定的边缘颜色。 |
环绕方式表现图如下所示:
环绕方式设置代码如下所示:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
2.过滤方式:就是在指定纹理坐标处,获取纹理像素的处理方式。
常见的过滤方式如下所示:
过滤方式 | 描述 |
GL_NEAREST | 临近过滤:就是得到纹理像素中心点距离纹理坐标最近的纹理像素 |
GL_LINEAR | 线性过滤:就是得到纹理坐标附近的纹理像素进行混合后的纹理像素 |
GL_NEAREST_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理来匹配像素大小,并使用邻近插值进行纹理采样 |
GL_LINEAR_MIPMAP_NEAREST | 使用最邻近的多级渐远纹理级别,并使用线性插值进行采样 |
GL_NEAREST_MIPMAP_LINEAR | 在两个最匹配像素大小的多级渐远纹理之间进行线性插值,使用邻近插值进行采样 |
GL_LINEAR_MIPMAP_LINEAR | 在两个邻近的多级渐远纹理之间使用线性插值,并使用线性插值进行采样 |
过滤方式表现图如下所示:
过滤方式设置代码如下所示:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
多级渐远纹理:就是一系列的纹理图像,后一个纹理图像是前一个的二分之一。当观察者的距离超过一定的阈值,OpenGL会使用不同的多级渐远纹理,即最适合物体的距离的那个纹理。当距离远的时候,读取解析度不高的纹理也不会被用户注意到,这样性能就会提高,但是内存会相对的变大。
纹理单元:就是一个纹理位置,方便在着色器中使用多个纹理。目前OpenGL保证有16个纹理单元可以使用,分别是GL_TEXTURE0~GL_TEXTURE15,且默认是激活GL_TEXTURE0这个纹理单元。
纹理的使用:使用的流程和相关接口如下所示:
1.void glGenTextures(GLsizei n, GLuint* textures)
:创建纹理对象。
.n表示需要创建的纹理对象个数。
.textures表示存储纹理对象唯一标志数组。数组长度跟第一个参数n相对应。
2.void glActiveTexture(GLenum texture)
:激活纹理单元。
.texture表示纹理单元。
3.void glBindTexture(GLenum target, GLuint texture)
:纹理对象绑定到指定纹理目标的纹理单元上。
.target表示纹理目标。通常是GL_TEXTURE_1D(一维纹理),GL_TEXTURE_2D(二维纹理),GL_TEXTURE_3D(三维纹理)等当中的一种。
.texture表示纹理对象标志。
4.unsigned char * stbi_load(char const* filename, int* x, int* y, int* channels_in_file, int desired_channels)
:Sean Barrett的stb_image.h文件中的接口,用来加载图像并得到数据参数信息的接口。
.filename表示图像文件名。
.x表示保存图像宽度。
.y表示保存图像高度。
.channels_in_file表示图像颜色通道的个数。
.desired_channels表示期望的图像颜色通道,一般传入0。
5.void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const void* pixels)
:使用图像数据填充纹理。
.target表示纹理目标。此时跟该目标绑定的纹理对象标志一一对应。
.level指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
.internalformat表示纹理储存为何种格式,如:GL_RGB。
.width表示纹理的宽度,通常如加载图像时获取的宽度保持一致。
.height表示纹理的高度,通常如加载图像时获取的高度保持一致。
.border表示纹理边界,一般传入0就可以了。
.format表示加载图像的数据格式。
.type表示加载图像的数据类型。
.pixels表示加载图像的数据源。
6.void glGenerateMipmap(GLenum target)
:生成多级渐远纹理。
.target表示纹理目标,通常跟绑定的纹理对象标志一一对应。
7.void stbi_image_free(const char* data)
:stb_image.h库中用来回收图像数据的接口。
.data表示stb_load加载得到的图像数据。
8.void glUniform1i(GLint texIndex, GLenum texUnit)
:对着色器中指定的纹理采样对象关联到指定的纹理单元上。
.texIndex表示着色器中纹理采样对象的索引位置。
.texUnit表示纹理单元。
9.vec3 texture(sampler2D tex2D, vec2 texCoord)
:GLSL接口。用来对指定纹理单元关联的纹理对象中的指定纹理坐标处,以指定的环绕方式和过滤方式来进行采样,从而得到纹理像素。
.tex2D表示指定纹理单元的纹理采样对象。
.texCoord表示指定的纹理坐标。
使用纹理相关参考代码如下:
1.应用程序部分:
unsigned int texture;
glGenTextures(1, &texture);
glActiveTexture(GL_TEXTURE0); // 在绑定纹理之前先激活纹理单元
glBindTexture(GL_TEXTURE_2D, texture);
// 为当前绑定的纹理对象设置环绕、过滤方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 加载并生成纹理
int width, height, nrChannels;
unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);
if (data)
{
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
{
std::cout << "Failed to load texture" << std::endl;
}
stbi_image_free(data);
// 设置纹理采样对象的纹理单元为0,此时片元着色器中的ourTexture和纹理单元0处的纹理对象相关联
glUniform1i(glGetUniformLocation(ourShader.ID, "ourTexture"), 0);
2.顶点着色器部分:
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aColor;
layout (location = 2) in vec2 aTexCoord;
out vec3 ourColor;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
ourColor = aColor;
TexCoord = aTexCoord;
}
3.片段着色器部分:
#version 330 core
out vec4 FragColor;
in vec3 ourColor;
in vec2 TexCoord;
uniform sampler2D ourTexture;
void main()
{
FragColor = texture(ourTexture, TexCoord);
}