【OpenGL学习】(七)纹理单元
OpenGL的纹理单元(Texture Unit)是GPU中用于管理和组织纹理资源的逻辑单元,它允许开发者在渲染过程中同时使用多个纹理,并通过采样器(Sampler)在着色器中访问这些纹理。每个纹理单元可以独立绑定不同的纹理对象(如2D纹理、立方体贴图等),并配置各自的纹理参数(如过滤模式、环绕方式等),从而在着色器中通过指定的纹理单元索引(如GL_TEXTURE0、GL_TEXTURE1等)访问对应的纹理数据。
默认情况下,第一个激活的纹理单元是 0(即 GL_TEXTURE0),因此在只使用单个纹理时,无需显式指定其他纹理单元。通过激活和配置多个纹理单元,可在着色器中实现多纹理的应用。
给定两个纹理图像:
纹理图像1 | 纹理图像2 |
---|---|
![]() | ![]() |
本项目实现将两个纹理图像进行线性插值,并映射到正方形上。
项目链接:https://github.com/BinaryAI-1024/OpenGLPorjects/tree/master/textures_combined
顶点着色器texture.vs:
#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 = vec2(aTexCoord.x, aTexCoord.y);
}
片段着色器texture.fs:
#version 330 core
out vec4 FragColor;in vec3 ourColor;
in vec2 TexCoord;// texture samplers
uniform sampler2D texture1;
uniform sampler2D texture2;void main()
{// 在两个纹理之间进行线性插值 (80% container, 20% awesomeface)FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
textures_combined.cpp:
#define STB_IMAGE_IMPLEMENTATION
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#include "stb_image.h"
#include <filesystem.h>
#include <shader_s.h>#include <iostream>void framebuffer_size_callback(GLFWwindow* window, int width, int height);
void processInput(GLFWwindow* window);const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;int main()
{// 初始化GLFWglfwInit();// 配置GLFWglfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); // OpenGL主版本号3.3glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 使用核心模式#ifdef __APPLE__glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 苹果系统兼容性设置
#endif// 创建窗口GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "LearnOpenGL", NULL, NULL);if (window == NULL){std::cout << "Failed to create GLFW window" << std::endl;glfwTerminate();return -1;}glfwMakeContextCurrent(window);glfwSetFramebufferSizeCallback(window, framebuffer_size_callback);// 初始化GLADif (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress)){std::cout << "Failed to initialize GLAD" << std::endl;return -1;}// 创建着色器程序Shader ourShader("texture.vs", "texture.fs");// 设置顶点数据和属性指针float vertices[] = {// 位置 // 颜色 // 纹理坐标0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上角0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下角-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下角-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上角 };unsigned int indices[] = {0, 1, 3, // 第一个三角形1, 2, 3 // 第二个三角形};unsigned int VBO, VAO, EBO;glGenVertexArrays(1, &VAO);glGenBuffers(1, &VBO);glGenBuffers(1, &EBO);glBindVertexArray(VAO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);// 位置属性glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);// 颜色属性glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(3 * sizeof(float)));glEnableVertexAttribArray(1);// 纹理坐标属性glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float)));glEnableVertexAttribArray(2);// 加载和创建纹理unsigned int texture1, texture2;stbi_set_flip_vertically_on_load(true); // 垂直翻转图像// ========== 纹理1配置 ==========glGenTextures(1, &texture1);glBindTexture(GL_TEXTURE_2D, texture1);// 设置纹理环绕参数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(FileSystem::getPath("resources/container.jpg").c_str(),&width, &height, &nrChannels, 0);if (data){// 将图像数据上传到GPUglTexImage2D(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); // 释放图像内存// ========== 纹理2配置 ==========glGenTextures(1, &texture2);glBindTexture(GL_TEXTURE_2D, texture2);// 设置纹理环绕参数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);// 加载带有透明通道的图像data = stbi_load(FileSystem::getPath("resources/awesomeface.png").c_str(),&width, &height, &nrChannels, 0);if (data){// 注意PNG图像有透明通道,所以使用GL_RGBA格式glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D);}else{std::cout << "Failed to load texture" << std::endl;}stbi_image_free(data);ourShader.use();// 为着色器中的uniform采样器变量texSampler1,texSampler2指定对应的纹理单元索引glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler1"), 0); // 纹理单元0:GL_TEXTURE0glUniform1i(glGetUniformLocation(ourShader.ID, "texSampler2"), 1); // 纹理单元1:GL_TEXTURE1// 渲染循环 while (!glfwWindowShouldClose(window)){glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);// 绑定纹理到对应的纹理单元glActiveTexture(GL_TEXTURE0); // 激活纹理单元0glBindTexture(GL_TEXTURE_2D, texture1);glActiveTexture(GL_TEXTURE1); // 激活纹理单元1glBindTexture(GL_TEXTURE_2D, texture2);// 绘制图形ourShader.use();glBindVertexArray(VAO);glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);glfwSwapBuffers(window);glfwPollEvents();}glDeleteVertexArrays(1, &VAO);glDeleteBuffers(1, &VBO);glDeleteBuffers(1, &EBO);glfwTerminate();return 0;
}void processInput(GLFWwindow* window)
{if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS)glfwSetWindowShouldClose(window, true);
}void framebuffer_size_callback(GLFWwindow* window, int width, int height)
{glViewport(0, 0, width, height);
}
项目运行结果: