如何使用GL_TEXTURE_2D_ARRAY来绑定多个纹理(作为数组)?

huangapple go评论60阅读模式
英文:

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 图像调用 glTexSubImage3DzoffsettextureIndex)是数组中纹理的索引,对于每个 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.

huangapple
  • 本文由 发表于 2023年4月10日 19:19:58
  • 转载请务必保留本文链接:https://go.coder-hub.com/75976623.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定