OpenGL纹理
· 阅读需 4 分钟
简介
opengl纹理简单来说就是贴图,像2D游戏,就是用大量的贴图拼起来组成丰富多彩的画面。下面我们详细地描述一下使用纹理的代码方式。
示例代码
顶点着色器输入两组坐标,顶点坐标和纹理坐标,纹理坐标直接传递给片段着色器
//02.Texture.vs
#version 330 core
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;
out vec2 TexCoord;
void main()
{
gl_Position = vec4(aPos, 1.0);
TexCoord = aTexCoord;
}
片段着色器定义一个纹理采样器text1,它与代码中绑定的TEXTURE0关联,使用传入的纹理坐标采样
//02.Texture.fs
#version 330 core
in vec2 TexCoord;
uniform sampler2D tex1;
out vec4 FragColor;
void main()
{
FragColor = texture(tex1, TexCoord);
}
实现代码逻辑
GLuint programID = LoadShaders( "data/shader/02.Texture.vs", "data/shader/02.Texture.fs" );
glUseProgram(programID);
// 顶点、纹理坐标
const GLfloat g_vertex_buffer_data[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // 左下角
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // 右下角
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f, // 左上角
0.5f, 0.5f, 0.0f, 1.0f, 1.0f // 右上角
};
// 顶点索引
unsigned int indices[] = {0, 1, 2,
1, 2, 3};
// VAO1、VBO1、EBO1的数据
GLuint VAO1;
GLuint VBO1;
GLuint EBO1;
glGenVertexArrays(1, &VAO1);
glBindVertexArray(VAO1);
glGenBuffers(1, &VBO1);
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);
glGenBuffers(1, &EBO1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO1);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)0);
// 纹理数据
GLuint 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, channels;
// 图像坐标系与纹理坐标系相反,翻转y轴
stbi_set_flip_vertically_on_load(true);
unsigned char* data = stbi_load("data/img/test.jpg", &width, &height, &channels, 0);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
stbi_image_free(data);
// 纹理坐标
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(GLfloat), (void*)(3 * sizeof(GLfloat)));
运行效果:
环绕方式
通过设置GL_TEXTURE_WRAP_
参数来设置环绕方式
-
GL_REPEAT
: 超出纹理范围的坐标整数部分被忽略,形成重复效果。 -
GL_MIRRORED_REPEAT
: 超出纹理范围的坐标整数部分被忽略,但当整数部分为奇数时进行取反,形成镜像效果。 -
GL_CLAMP_TO_EDGE
: 超出纹理范围的坐标被截取成0和1,形成纹理边缘延伸的效果。 -
GL_CLAMP_TO_BORDER
: 超出纹理范围的部分被设置为边缘色。
如果将纹理坐标改成如下:
const GLfloat g_vertex_buffer_data[] = {
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // 左下角
0.5f, -0.5f, 0.0f, 2.0f, 0.0f, // 右下角
-0.5f, 0.5f, 0.0f, 0.0f, 2.0f, // 左上角
0.5f, 0.5f, 0.0f, 2.0f, 2.0f // 右上角
};
不同环绕方式对应的效果:
GL_REPEAT
GL_MIRRORED_REPEAT
GL_CLAMP_TO_EDGE
GL_CLAMP_TO_BORDER