根据这个答案:OpenGL sampler2D array
您不能使用片段着色器输入变量来索引纹理采样器数组。您必须使用 sampler2DArray (GL_TEXTURE_2D_ARRAY) 而不是 sampler2D (GL_TEXTURE_2D) 数组
所以我试图做到这一点 - 生成 GL_TEXTURE_2D_ARRAY:
#include <iostream>
#include <glad/glad.h>
#include <GLFW/glfw3.h>
#define STB_IMAGE_IMPLEMENTATION
#include <stb/stb_image.h>
#include <test/main/shader.hpp>
uint32_t LoadTexture(const std::string& filepath) {
int w, h, bits;
stbi_set_flip_vertically_on_load(1);
uint8_t *pixels = stbi_load(filepath.c_str(), &w, &h, &bits, 4);
uint32_t texture_id;
glGenTextures(1, &texture_id);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture_id);
glTexImage2D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
// glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
stbi_image_free(pixels);
return texture_id;
}
// https://stackoverflow.com/questions/60513272/alternative-for-glbindtextureunit-for-opengl-versions-less-than-4-5
void Bind2DArrayTexture(int unit, uint32_t texture) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
}
int main() {
glfwInit();
GLFWwindow *window = glfwCreateWindow(960, 540, "test", nullptr, nullptr);
if (window == nullptr) {
std::cerr << "failed to create GLFW window\n";
}
glfwMakeContextCurrent(window);
glfwSetFramebufferSizeCallback(window, [](GLFWwindow *_, int width, int height) {
glViewport(0, 0, width, height);
});
if (!gladLoadGLLoader((GLADloadproc) glfwGetProcAddress)) {
std::cerr << "failed to initialize GLAD\n";
}
glEnable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
Shader shader("assets/GLSL/test.glsl");
shader.Bind();
int samplers[2] = {0, 1};
shader.SetIntArray("u_textures", samplers, 2);
uint32_t quad_va, quad_vb, quad_ib;
glGenVertexArrays(1, &quad_va);
glBindVertexArray(quad_va);
float vertices[] = {
// first texture - logo.png
-1.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f,
-1.5f, 0.5f, 0.0f, 0.0f, 1.0f, 0.0f,
// second texture - chessboard.png
0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f,
1.5f, -0.5f, 0.0f, 1.0f, 0.0f, 1.0f,
1.5f, 0.5f, 0.0f, 1.0f, 1.0f, 1.0f,
0.5f, 0.5f, 0.0f, 0.0f, 1.0f, 1.0f
};
glGenBuffers(1, &quad_vb);
glBindBuffer(GL_ARRAY_BUFFER, quad_vb);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 6, nullptr);
glEnableVertexAttribArray(0);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 6,
reinterpret_cast<const void *>(sizeof(float) * 3));
glEnableVertexAttribArray(1);
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, sizeof(float) * 6,
reinterpret_cast<const void *>(sizeof(float) * 5));
glEnableVertexAttribArray(2);
uint32_t indices[] = {
0, 1, 2, 2, 3, 0,
4, 5, 6, 6, 7, 4
};
glGenBuffers(1, &quad_ib);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quad_ib);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
uint32_t logo_id = LoadTexture("assets/textures/logo.png");
uint32_t chessboard_id = LoadTexture("assets/textures/chessboard.png");
while (!glfwWindowShouldClose(window)) {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader.Bind();
Bind2DArrayTexture(0, logo_id);
Bind2DArrayTexture(1, chessboard_id);
glBindVertexArray(quad_va);
glDrawElements(GL_TRIANGLES, 12, GL_UNSIGNED_INT, nullptr);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
和着色器:
#type vertex
#version 450 core
layout(location = 0) in vec3 a_pos;
layout(location = 1) in vec2 a_texcoord;
layout(location = 0) in float a_texindex;
out vec2 v_texcoord;
out float v_texindex;
void main()
{
v_texcoord = a_texcoord;
v_texindex = a_texindex;
gl_Position = vec4(a_pos, 1.0);
}
#type fragment
#version 450 core
in vec2 v_texcoord;
in float v_texindex;
uniform sampler2DArray u_textures;
out vec4 color;
void main()
{
color = texture(u_textures, vec3(v_texcoord.xy, v_texindex));
}
未显示的 Shader 类简单地解析包含顶点和片段部分的着色器文件。现在两个纹理都通过最后一个属性指针来区分,一个 float 0.0f 用于第一个纹理,1.0f 用于第二个纹理。但是在输出中,我只看到 2 个黑色方块(里面应该有纹理),所以如何使用
GL_TEXTURE_2D_ARRAY
加载两个纹理(因此调用 2 次 Bind2DArrayTexture
,因为这是目标,我无法制作两种纹理的实际阵列)。
现在请不要将此标记为与参考答案重复(在此问题开头提供)。因为只显示了着色器的用法(我做了),但没有实际使用 C++ 端(使用
GL_TEXTURE_2D_ARRAY
用于 2 个不同的纹理)。所以它不会回答我的问题,因此我的问题不能重复
PS:为了完整起见,这里是着色器类:
标题:
#ifndef TEST_SHADER_HPP
#define TEST_SHADER_HPP
#include <string>
#include <glm/glm.hpp>
class Shader {
public:
explicit Shader(const std::string& filepath);
void Bind() const;
void SetInt(const std::string& name, int value) const;
void SetIntArray(const std::string& name, int *values, int count);
void SetFloat(const std::string& name, float value) const;
void SetVec3(const std::string& name, const glm::vec3& vec) const;
void SetVec4(const std::string& name, const glm::vec4& vec) const;
void SetMat4(const std::string& name, const glm::mat4& mat) const;
private:
static void CheckCompileErrors(uint32_t shader_id, const std::string& type, const std::string& filepath);
uint32_t id_;
};
#endif //TEST_SHADER_HP
来源:
#include <test/main/shader.hpp>
#include <iostream>
#include <fstream>
#include <sstream>
#include <glad/glad.h>
void Shader::CheckCompileErrors(uint32_t shader_id, const std::string& type, const std::string& filepath) {
int success;
char infoLog[1024];
if (type != "PROGRAM") {
glGetShaderiv(shader_id, GL_COMPILE_STATUS, &success);
if (!success) {
glGetShaderInfoLog(shader_id, 1024, nullptr, infoLog);
std::cout << "ERROR::SHADER::COMPILATION_ERROR of type: " << type << "\n" << infoLog << "filepath: "
<< filepath;
}
} else {
glGetProgramiv(shader_id, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader_id, 1024, nullptr, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n" << infoLog << "filepath: "
<< filepath;
}
}
}
Shader::Shader(const std::string& filepath) {
// 1. divide vertex and fragment part
enum class ShaderType {
NONE = -1, VERTEX = 0, FRAGMENT = 1
};
std::ifstream stream(filepath);
std::string line;
std::stringstream ss[2];
ShaderType type = ShaderType::NONE;
while (getline(stream, line)) {
if (line.find("#type") != std::string::npos) {
if (line.find("vertex") != std::string::npos) {
type = ShaderType::VERTEX;
} else if (line.find("fragment") != std::string::npos) {
type = ShaderType::FRAGMENT;
} else {
std::cout << "Unknown shader: " << line << '\n';
}
} else {
if (type == ShaderType::NONE) {
std::cout << "No shader defined" << '\n';
} else {
ss[(int) type] << line << '\n';
}
}
}
std::string vertex_code = ss[(int) ShaderType::VERTEX].str();
std::string fragment_code = ss[(int) ShaderType::FRAGMENT].str();
// 2. convert, compile and link sources to shader program
// 2.1 convert string to c str
const char *vertex_str = vertex_code.c_str();
const char *fragment_str = fragment_code.c_str();
// 2.2.1 vertex shader
uint32_t vertex_id = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vertex_id, 1, &vertex_str, nullptr);
glCompileShader(vertex_id);
CheckCompileErrors(vertex_id, "VERTEX", filepath);
// 2.2.2 fragment shader
uint32_t fragment_id = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(fragment_id, 1, &fragment_str, nullptr);
glCompileShader(fragment_id);
CheckCompileErrors(fragment_id, "FRAGMENT", filepath);
// 2.3 shader program
id_ = glCreateProgram();
glAttachShader(id_, vertex_id);
glAttachShader(id_, fragment_id);
glLinkProgram(id_);
CheckCompileErrors(id_, "PROGRAM", filepath);
// 2.4 delete the shaders as they're linked into program and no longer necessary
glDeleteShader(vertex_id);
glDeleteShader(fragment_id);
}
void Shader::Bind() const {
glUseProgram(id_);
}
void Shader::SetInt(const std::string& name, int value) const {
glUniform1i(glGetUniformLocation(id_, name.c_str()), value);
}
void Shader::SetIntArray(const std::string& name, int *values, int count) {
glUniform1iv(glGetUniformLocation(id_, name.c_str()), count, values);
}
void Shader::SetFloat(const std::string& name, float value) const {
glUniform1f(glGetUniformLocation(id_, name.c_str()), value);
}
void Shader::SetVec4(const std::string& name, const glm::vec4& vec) const {
glUniform4f(glGetUniformLocation(id_, name.c_str()), vec.x, vec.y, vec.z, vec.w);
}
void Shader::SetVec3(const std::string& name, const glm::vec3& vec) const {
glUniform3f(glGetUniformLocation(id_, name.c_str()), vec.x, vec.y, vec.z);
}
void Shader::SetMat4(const std::string& name, const glm::mat4& mat) const {
glUniformMatrix4fv(glGetUniformLocation(id_, name.c_str()), 1, GL_FALSE, &mat[0][0]);
}
glTexImage2D
和 GL_TEXTURE_2D_ARRAY
生成“GL_INVALID_ENUM”错误。实际上一个GL_TEXTURE_2D_ARRAY
是多个GL_TEXTURE_2D
纹理。您必须指定 2D 纹理的大小和数组中的纹理数量。因此纹理需要用glTexImage3D
指定:
glTexImage2D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
还需要设置
GL_TEXTURE_MAG_FILTER
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
对于不止一个 2D 纹理,我建议为所需数量的 2D 纹理创建 3D 纹理图像:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, noOfTextures, GL_RGBA, GL_UNSIGNED_BYTE, null);
glTexSubImage3D
指定单个 2D 纹理:
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureIndex, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)