英文:
How to use GL_TEXTURE_2D_ARRAY for binding multiple textures (as array)?
问题
根据您提供的代码,这部分涉及OpenGL和C++,我将为您提供相关的翻译:
// 加载纹理的函数
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);
stbi_image_free(pixels);
return texture_id;
}
// 绑定2D数组纹理的函数
void Bind2DArrayTexture(int unit, uint32_t texture) {
glActiveTexture(GL_TEXTURE0 + unit);
glBindTexture(GL_TEXTURE_2D_ARRAY, texture);
}
// 主函数
int main() {
// ... 初始化OpenGL 窗口等 ...
uint32_t logo_id = LoadTexture("assets/textures/logo.png");
uint32_t chessboard_id = LoadTexture("assets/textures/chessboard.png");
while (!glfwWindowShouldClose(window)) {
// ... 渲染循环 ...
shader.Bind();
Bind2DArrayTexture(0, logo_id);
Bind2DArrayTexture(1, chessboard_id);
// ... 绘制代码 ...
}
}
这部分代码涉及OpenGL纹理加载和使用,用于创建和绑定GL_TEXTURE_2D_ARRAY
类型的纹理以供着色器使用。shader部分的代码用于加载和设置着色器程序。
您可以使用这些翻译来理解代码的功能和执行步骤。如果您有任何问题或需要进一步的翻译,请告诉我。
英文:
According to this answer: https://stackoverflow.com/questions/72648980/opengl-sampler2d-array
> You cannot use a fragment shader input variable to index an array of texture samplers. You have to use a sampler2DArray (GL_TEXTURE_2D_ARRAY
) instead of an array of sampler2D (GL_TEXTURE_2D
)
So I tried to do exactly that - generate 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();
}
}
and shader:
#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));
}
Where the Shader class, which is not shown simply parses the shader file with both, vertex and fragment part. Now both textures are distinguished via last attrib pointer, one float 0.0f for first texture, 1.0f for second one. But in the output, I only see 2 black squares (which should have the textures in it), so how to use the GL_TEXTURE_2D_ARRAY
to load both textures (and thus calling 2 times Bind2DArrayTexture
, since that is the target, I have no way how to make an actual array of both textures).
Now please do not mark this as duplicate to the referenced answer (provided at the beginning of this question). Because there is only shown the usage of the shader (which I did), but no actual usage of C++ side (using the GL_TEXTURE_2D_ARRAY
for 2 distinct textures). So it won't answer my question and thus my question cannot be duplicate
PS: for completness, here is the shader class:
header:
#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
source:
#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]);
}
答案1
得分: 2
glTexImage2D
使用 GL_TEXTURE_2D_ARRAY
目标生成 INVALID_ENUM
错误。实际上,GL_TEXTURE_2D_ARRAY
是多个 GL_TEXTURE_2D
纹理。您需要指定 2D 纹理的尺寸和数组中的纹理数量。因此,纹理需要使用 glTexImage3D
来指定。例如:对于数组大小为 1:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
您还需要设置 GL_TEXTURE_MAG_FILTER
,因为 GL_TEXTURE_MIN_FILTER
的初始值是 GL_NEAREST_MIPMAP_LINEAR
。如果不更改它并且不创建 Mipmap,纹理将不会是“完整的”并且不会被“显示”。
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
对于多个 2D 纹理,我建议为所需数量的 2D 纹理(noOfTextures
)创建 3D 纹理图像:
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, noOfTextures, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
并使用 glTexSubImage3D
指定单独的 2D 纹理。您必须为每个单独的 2D 图像调用 glTexSubImage3D
,zoffset(textureIndex
)是数组中纹理的索引,对于每个 2D 纹理必须不同。
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureIndex, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
当然,单独的 2D 图像必须具有相同的大小。
英文:
glTexImage2D
with target GL_TEXTURE_2D_ARRAY
generates a INVALID_ENUM
error. Actually a GL_TEXTURE_2D_ARRAY
are multiple GL_TEXTURE_2D
textures. You have to specify the size of the 2D texture and the number of textures in the array. Therefore the texture needs to be specified with glTexImage3D
. e.g.: for array size 1:
<s>glTexImage2D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
</s>
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels);
You also need to set GL_TEXTURE_MAG_FILTER
because the initial value of GL_TEXTURE_MIN_FILTER
is GL_NEAREST_MIPMAP_LINEAR
. If you don't change it and don't create mipmaps, the texture will not be "complete" and will not be "shown".
glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
For more than one 2D texture, I suggest creating the 3D texture image for the desired number of 2D textures (noOfTextures
)
glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, w, h, noOfTextures, 0, GL_RGBA, GL_UNSIGNED_BYTE, null);
and specify the individual 2D textures with glTexSubImage3D
. You have to call glTexSubImage3D
for each individual 2D image, the zoffset (textureIndex
) is the index of the texture in the array and has to be different for each 2D texture.
glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, textureIndex, w, h, 1, GL_RGBA, GL_UNSIGNED_BYTE, pixels)
Of course, the individual 2D images must be of the same size.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论