OpenGL:绑定统一缓冲区时出现异常

OpenGL: Exception when binding uniform buffer

我有以下 opengl 代码来绑定创建的缓冲区并用一些数据填充它。

#include <glad/glad.h> 
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <array>
#include <iostream>

template <size_t max_lights = 100>
class SceneLights
{
private:
    struct Light
    {
        glm::vec4 position;
        glm::vec4 color;
    };

    std::array<Light, max_lights> lights;

public:
    SceneLights()
    {
        lights.fill(Light(glm::vec4(0.0), glm::vec4(0.0)));
    }

    constexpr size_t size() const noexcept
    {
        return lights.size();
    }

    constexpr size_t size_bytes() const noexcept
    {
        return size() * 2 * sizeof(glm::mat4);
    }

    constexpr Light const* data() const noexcept
    {
        return lights.data();
    }
};

int main()
{
    GLFWwindow* window;

    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 5);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
    glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, GL_TRUE);

    window = glfwCreateWindow(800, 600, "The Window", NULL, NULL);
    if (window == nullptr)
    {
        glfwTerminate();
        throw "Failed to create GLFW window";
    }

    glfwMakeContextCurrent(window);
    glfwSwapInterval(0);

    if (!gladLoadGLLoader((GLADloadproc)glfwGetProcAddress))
    {
        throw "Failed to initialize GLAD";
    }

    std::cout << "OpenGL Info:" << std::endl;
    std::cout << "  Vendor: " << glGetString(GL_VENDOR) << std::endl;
    std::cout << "  Renderer: " << glGetString(GL_RENDERER) << std::endl;
    std::cout << "  Version: " << glGetString(GL_VERSION) << std::endl;

    SceneLights sl = SceneLights();

    unsigned int ubo_id;

    glGenBuffers(1, &ubo_id);

    glBindBuffer(GL_UNIFORM_BUFFER, ubo_id);
    glBufferData(GL_UNIFORM_BUFFER, sl.size_bytes(), NULL, GL_DYNAMIC_DRAW);
    glBindBuffer(GL_UNIFORM_BUFFER, 0);

    glBindBufferRange(GL_UNIFORM_BUFFER, 0, ubo_id, 0, sl.size_bytes());

    glBindBuffer(GL_UNIFORM_BUFFER, ubo_id);
    int buf;
    int size;
    glGetIntegerv(GL_UNIFORM_BUFFER_BINDING, &buf);
    glGetBufferParameteriv(GL_UNIFORM_BUFFER, GL_BUFFER_SIZE, &size);
    std::cout << sl.size_bytes() << std::endl;
    std::cout << sl.data() << std::endl;
    std::cout << buf << std::endl;
    std::cout << size << std::endl;
    glBufferSubData(GL_UNIFORM_BUFFER, 0, sl.size_bytes(), sl.data());

    return 0;
}

输出是:

OpenGL Info:
  Vendor: NVIDIA Corporation
  Renderer: NVIDIA GeForce GTX 950/PCIe/SSE2
  Version: 4.5.0 NVIDIA 466.27
12800
00000080860FE830
1
12800

而且我一直收到“在 main.exe 中的 0x0000000000000000 抛出的异常:0xC0000005:访问冲突执行位置 0x0000000000000000。”调用 glBufferSubData(GL_UNIFORM_BUFFER, 0, sl.size_bytes(), sl.data());

不太明白为什么?缓冲区似乎已绑定,我的输入数据不是 nullptr,并且大小匹配?任何帮助或调试建议表示赞赏。

为 64 位编译时在我的机器上得到了重现。没有在 32 位上崩溃,但我怀疑我只是幸运地访问了 sizes/layouts.

注意到这个:

constexpr size_t size_bytes() const noexcept
{
    return size() * 2 * sizeof(glm::mat4);
                                    ^^^^ not a vec4?
}

更改 mat4 以匹配 Light 结构中的 vec4 解决了我这边的崩溃问题。

sizeof(lights)size() * sizeof(Light) 也可以,如果 lights 的类型有可能在将来某个时候更改为动态容器,则优先选择后者。

一般来说,不要告诉 OpenGL 您要求它读取的内存缓冲区比实际长度要长。否则 OpenGL 会愉快地读取结束缓冲区,要么是抓取垃圾,要么是段错误。