跳到主要内容

OpenGL纹理

· 阅读需 4 分钟
苏明才
Vulkan驱动填坑狮

简介

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)));

运行效果:

1637478946876

环绕方式

通过设置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

11


GL_MIRRORED_REPEAT

222


GL_CLAMP_TO_EDGE

333


GL_CLAMP_TO_BORDER

444