OpenGL - 重新绑定 glBindVertexArray 不会在屏幕上绘制任何内容

OpenGL - rebinding glBindVertexArray doesn't draw anything to the screen

我刚开始尝试学习 opengl,在将代码的各个部分解耦为 classes 的过程中,我 运行 遇到了问题。

这是一个非常简单的问题。在下面的代码中,如果 Mesh 构造函数底部的 glBindVertexArray(0) 被注释掉,它会将图像绘制到屏幕上,但是如果我把它放在

中,则不会绘制图像。

Mesh.draw() 函数中调用 glDrawElements 之前有一个 glBindVertexArray(vao) 命令,所以看起来应该可以,但实际上没有。

主要代码:

int main(int argc, char*argv[]) {

    if (SDL_Init(SDL_INIT_VIDEO) < 0)
        std::cout << "SDL did not intizlize.  SDL Error: " << SDL_GetError() << std::endl;

    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);

    SDL_Window* window = SDL_CreateWindow("OpenGL", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, 800, 600, SDL_WINDOW_OPENGL);
    SDL_GLContext context = SDL_GL_CreateContext(window);


    glewExperimental = GL_TRUE;
    GLenum glewTest = glewInit();
    if (glewTest != GLEW_OK)
        std::cout << glewGetErrorString(glewTest) << std::endl;
    std::cout << glGetError() << std::endl;

    Mesh mesh;
    Shaders shaders("basicShader");
    Texture texture("kitten.png");

    //Loop stuff
    bool quit = false;
    SDL_Event e;
    double frameCounter = 0;
    double time = SDL_GetTicks();

    while (!quit) {
        while (SDL_PollEvent(&e)) {
            if (e.type == SDL_QUIT) {
                quit = true;
            }
            if (e.type == SDL_KEYDOWN) {
                if (e.key.keysym.sym == SDLK_ESCAPE)
                    quit = true;
            }
        }

        ++frameCounter;
        if (SDL_GetTicks() - time >= 500) {
            std::cout << "FPS: " << frameCounter / ((SDL_GetTicks() - time) / 1000) << std::endl;
            frameCounter = 0;
            time = SDL_GetTicks();
        }

        // Clear the screen to black
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT);

        mesh.draw();
        //glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        // Swap buffers
        SDL_GL_SwapWindow(window);
    }

    mesh.~Mesh();
    SDL_GL_DeleteContext(context);
    SDL_Quit();
    return 0;
}

网格class代码:

Mesh::Mesh() {
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);

    glGenBuffers(1, &vbo);
    GLfloat vertices[] = {
        -0.5f, 0.5f, 0.0, 0.0,
        0.5f, 0.5f, 1.0, 0.0,
        0.5f, -0.5f, 1.0, 1.0,
        -0.5f, -0.5f, 0.0, 1.0
    };
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glGenBuffers(1, &ebo);
    GLuint elements[] = {
        0, 1, 2,
        2, 3, 0
    };
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(elements), elements, GL_STATIC_DRAW);

    //glBindVertexArray(0);
}

void Mesh::draw() {
    glBindVertexArray(vao);

    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

    //glBindVertexArray(0);
}

Mesh::~Mesh() {
    glDeleteVertexArrays(1, &vao);
}

我还有着色器设置代码未在此处发布,它调用 glVertexArrayPointer()glEnableVertexAttribArray() 来设置顶点属性。

问题在于您在设置着色器时进行的 glVertexAttribPointer()glEnableVertexAttribArray() 调用。这两个调用都修改了当前 VAO 的状态。

如果您致电:

glBindVertexArray(0);

在网格设置结束时,0 现在是您当前的 VAO(在兼容性配置文件中是合法的 VAO,但在核心配置文件中不是)。因此,您在此之后对 glVertexAttribPointer()glEnableVertexAttribArray() 的调用现在将修改 VAO 0 中的状态。

当您在 draw() 方法开始时调用绑定 VAO 时:

glBindVertexArray(vao);

您正在使用网格 VAO 中的所有状态。这反过来意味着您在 VAO 0 中设置的状态未被使用。由于在绑定网格 VAO 时您从未进行过 glVertexAttribPointer()glEnableVertexAttribArray() 调用,因此现在根本没有设置顶点属性。

你真的很幸运,当你没有在构造函数的末尾解除绑定 VAO 时它起作用了。由于在设置着色器时网格 VAO 仍处于绑定状态,因此 glVertexAttribPointer()glEnableVertexAttribArray() 调用修改了网格 VAO 状态,从而产生了预期的结果。但是,如果您使用多个 mesh/shader.

,这很可能会惨败

您确实需要在设置网格时设置顶点状态,同时绑定特定网格的 VAO。以下调用都修改 VAO 状态,并且必须在绑定正确的 VAO 时进行:

  • glEnableVertexAttribArray(...)
  • glDisableVertexAttribArray(...)
  • glVertexAttribPointer(...)
  • glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ...)

在设置着色器程序时修改顶点设置状态在概念上也是有问题的。顶点设置状态不是着色器程序状态的一部分。