如何在OpenGL中创建数组纹理

juzqafwq  于 2023-04-11  发布在  其他
关注(0)|答案(2)|浏览(112)

有这个代码:

#include <iostream>
#include <vector>

#include <glad/glad.h>
#include <GLFW/glfw3.h>

#define STB_IMAGE_IMPLEMENTATION

#include <stb/stb_image.h>

#include <test/main/shader.hpp>

// size of the images has to be the same
// https://stackoverflow.com/questions/75976623/how-to-use-gl-texture-2d-array-for-binding-multiple-textures-as-array/75976728?noredirect=1#comment134006728_75976728
uint32_t BuildTextureArray(const std::vector<std::string>& paths) {
    int width, height;
    stbi_set_flip_vertically_on_load(1);
    uint8_t *data_of_first = stbi_load(paths[0].c_str(), &width, &height, nullptr, 4);

    uint32_t array_id;
    glGenTextures(1, &array_id);
    glBindTexture(GL_TEXTURE_2D_ARRAY, array_id);
    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, width, height, paths.size(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, data_of_first);
    for (int i = 1; i < paths.size(); i++) {
        uint8_t *data = stbi_load(paths[i].c_str(), nullptr, nullptr, nullptr, 4);
        glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);
    }

    return array_id;
}

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();
    uint32_t texture_array = BuildTextureArray({"assets/textures/logo.png", "assets/textures/homer.png"});
    glBindTexture(GL_TEXTURE_2D_ARRAY, texture_array);
    shader.SetInt("u_textures", 0);

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

    while (!glfwWindowShouldClose(window)) {
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        shader.Bind();

        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类(未显示)仅使用顶点和片段部分解析着色器文件。现在两个纹理通过最后一个属性指针进行区分,第一个纹理为浮点0.0f,第二个纹理为1.0f。运行此操作将导致segfault。segfault由BuildTextureArray()函数引起(它可以在没有它的情况下运行,但当然不会有数组纹理)。我已经尝试根据How to use GL_TEXTURE_2D_ARRAY in OpenGL 3.2创建该函数,所以不应该重复,因为我正在使用那里的代码。而且因为我手头没有调试器,我不能通过这个函数。但是因为它是刚刚复制的,什么地方出错了?图像logo.pnghomer.png的大小相同1024x1024
PS:为了完整性,这里是着色器类:

  • 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
  • 来源 *:
#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]);
}
mwkjh3gx

mwkjh3gx1#

很有可能你的纹理根本找不到,或者两个图像的大小不一样。在glTexSubImage中使用图像加载器之前,你并没有真正检查它的返回值,也没有验证图像的大小是否相同。
测试返回值应该总是做的,特别是与文件IO,因为它总是会失败在某些时候...同样的假设图像大小...宽/高的新图像应检查-它是相同的大小作为第一个图像。
因为我手头没有调试器,所以我不能遍历该函数。
如果没有某种方法来查看代码在实际中的作用,你就不能期望编写代码时有效率。

rekjcdws

rekjcdws2#

segfault问题发生在第二次调用stbi_load()(for循环内部)时,我使用nullptr作为width,height和channels指针。我不知道为什么,但必须指定width和hight指针。我为此放置了dummy stack int,它工作了。

uint32_t BuildTextureArray(const std::vector<std::string>& paths) {
    int width, height;
    stbi_set_flip_vertically_on_load(1);
    uint8_t *data_of_first = stbi_load(paths[0].c_str(), &width, &height, nullptr, 4);
    std::cout << "path: " << paths[0] << std::endl;
    if(data_of_first == nullptr){
        std::cerr << "cannot open '" << paths[0] << "'\n";
    }

    uint32_t array_id;
    glGenTextures(1, &array_id);
    glBindTexture(GL_TEXTURE_2D_ARRAY, array_id);
    glTexImage3D(GL_TEXTURE_2D_ARRAY, 0, GL_RGBA, width, height, paths.size(), 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);

    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    int a, b;
    glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, 0, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, data_of_first);
    for (int i = 1; i < paths.size(); i++) {
        uint8_t *data = stbi_load(paths[i].c_str(), &a, &b, nullptr, 4);
        if(data == nullptr){
            std::cerr << "cannot open '" << paths[i] << "'\n";
        }
        glTexSubImage3D(GL_TEXTURE_2D_ARRAY, 0, 0, 0, i, width, height, 1, GL_RGBA, GL_UNSIGNED_BYTE, data);
    }

    return array_id;
}

但是由于stbi_load()来自第三方,我不能控制它的实现,所以我对此无能为力。

相关问题