无法使用 VAO 和 EBO (openGL) 绘制多个对象

Posted

技术标签:

【中文标题】无法使用 VAO 和 EBO (openGL) 绘制多个对象【英文标题】:Unable to draw multiple objects using VAOs and EBOs (openGL) 【发布时间】:2020-02-17 18:42:37 【问题描述】:

我正在尝试使用顶点数组对象和元素缓冲区对象。我设法使用带有 EBO 边界的单个 VAO 在屏幕上绘制字母“H”,因为我的所有顶点和索引都在一个数组中。虽然我想将每个矩形分割成不同的对象并赋予它们不同的颜色,但只有其中一个在屏幕上绘制。

代码如下:

#include <glew.h>
#include <glfw3.h>
#include <iostream>

const char* vertexShaderSource = "#version 330 core\n"
    "layout (location = 0) in vec3 aPos;\n"
    "void main()\n"
    "\n"
    "gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0);\n"
    "\0";

const char* fragmentShaderSource = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "\n"
    "FragColor = vec4(0.2f, 0.6f, 0.7f, 1.0f);\n"
    "\0";

const char* fragmentShaderSource2 = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "\n"
    "FragColor = vec4(0.7f, 0.4f, 0.3f, 1.0f);\n"
    "\0";

const char* fragmentShaderSource3 = "#version 330 core\n"
    "out vec4 FragColor;\n"
    "void main()\n"
    "\n"
    "FragColor = vec4(0.1f, 0.2f, 0.6f, 1.0f);\n"
    "\0";

int main(void)

    GLFWwindow* window;

    glewInit();
    glfwInit();
    glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
    glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
    glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);

    /* Initialize the library */
    if (!glfwInit())
        return -1;

    /* Create a windowed mode window and its OpenGL context */
    window = glfwCreateWindow(640, 480, "Hello World", NULL, NULL);
    if (!window)
    
        glfwTerminate();
        return -1;
    

    /* Make the window's context current */
    glfwMakeContextCurrent(window);

    if (glewInit() != GLEW_OK)
        std::cout << "Error!" << std::endl;

    float vertices[] = 
         0.2f, 0.7f, 0.0f,
         0.3f, 0.7f, 0.0f,
         0.3f, 0.2f, 0.0f,
         0.2f, 0.2f, 0.0f
    ;

    float vertices2[] = 
         0.6f, 0.7f, 0.0f,
         0.7f, 0.7f, 0.0f,
         0.7f, 0.2f, 0.0f,
         0.6f, 0.2f, 0.0f
    ;

    float vertices3[] = 
        0.3f, 0.4f, 0.0f,
        0.3f, 0.5f, 0.0f,
        0.6f, 0.5f, 0.0f,
        0.6f, 0.4f, 0.0f
    ;

    unsigned int indices[] =     
        1, 0, 2,
        2, 3, 0
    ;  

    unsigned int indices2[] = 
        5, 4, 6,
        6, 7, 4
    ;

    unsigned int indices3[] = 
        8, 9, 11,
        11, 10, 9
    ;

    unsigned int vertexShader = glCreateShader(GL_VERTEX_SHADER);

    glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);
    glCompileShader(vertexShader);

    int  success;
    char infoLog[512];
    glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);

    if (!success)
    
        glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    

    unsigned int fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);

    glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);
    glCompileShader(fragmentShader);

    glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);

    if (!success)
    
        glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    

    unsigned int fragmentShader2 = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader2, 1, &fragmentShaderSource2, NULL);
    glCompileShader(fragmentShader2);

    unsigned int fragmentShader3 = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragmentShader3, 1, &fragmentShaderSource3, NULL);
    glCompileShader(fragmentShader3);

    unsigned int shaderProgram;
    shaderProgram = glCreateProgram();

    unsigned int shaderProgram2;
    shaderProgram2 = glCreateProgram();

    unsigned int shaderProgram3;
    shaderProgram3 = glCreateProgram();

    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragmentShader);
    glLinkProgram(shaderProgram);
    glAttachShader(shaderProgram2, fragmentShader2);
    glAttachShader(shaderProgram2, vertexShader);
    glLinkProgram(shaderProgram2);
    glAttachShader(shaderProgram3, fragmentShader3);
    glAttachShader(shaderProgram3, vertexShader);
    glLinkProgram(shaderProgram3);

    glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success);

    if (!success)
    
        glGetShaderInfoLog(shaderProgram, 512, NULL, infoLog);
        std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;
    

    glDeleteShader(vertexShader);
    glDeleteShader(fragmentShader);
    glDeleteShader(fragmentShader2);
    glDeleteShader(fragmentShader3);

    unsigned int VBOs[3], VAOs[3], EBOs[3];

    glGenVertexArrays(3, VAOs);
    glGenBuffers(3, VBOs);
    glGenBuffers(3, EBOs);

    glBindVertexArray(VAOs[0]);
    glBindBuffer(GL_ARRAY_BUFFER, VBOs[0]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[0]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAOs[1]);
    glBindBuffer(GL_ARRAY_BUFFER, VBOs[1]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices2), vertices2, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[1]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices2), indices2, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindVertexArray(VAOs[2]);
    glBindBuffer(GL_ARRAY_BUFFER, VBOs[2]);
    glBufferData(GL_ARRAY_BUFFER, sizeof(vertices3), vertices3, GL_STATIC_DRAW);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBOs[2]);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices3), indices3, GL_STATIC_DRAW);

    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(float), (void*)0);
    glEnableVertexAttribArray(0);

    glBindBuffer(GL_ARRAY_BUFFER, 0);
    glBindVertexArray(0);

    /* Loop until the user closes the window */
    while (!glfwWindowShouldClose(window))
    

        /* Render here */
        glClear(GL_COLOR_BUFFER_BIT);

        glUseProgram(shaderProgram);
        glBindVertexArray(VAOs[0]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glUseProgram(shaderProgram2);
        glBindVertexArray(VAOs[1]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        glUseProgram(shaderProgram3);
        glBindVertexArray(VAOs[2]);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);

        /* Swap front and back buffers */
        glfwSwapBuffers(window);

        /* Poll for and process events */
        glfwPollEvents();
    
    glDeleteVertexArrays(3, VAOs);
    glDeleteBuffers(3, VBOs);
    glDeleteBuffers(3, EBOs);

    glfwTerminate();
    return 0;

【问题讨论】:

【参考方案1】:

你会在这里:

float vertices2[] = 
     0.6f, 0.7f, 0.0f,
     0.7f, 0.7f, 0.0f,
     0.7f, 0.2f, 0.0f,
     0.6f, 0.2f, 0.0f
;

与:

unsigned int indices2[] = 
    5, 4, 6,
    6, 7, 4
;

这没有意义。您只需将 4 个顶点复制到 VBO[1],并将属性指针设置为该缓冲区中的偏移量 0,因此唯一有效的索引是 0123

您的索引被设置为好像所有顶点都像以前一样在一个大数组中,实际上,这将是一个更好的策略:保留一个大顶点数组、一个大元素索引数组和一个 VAO,并且只需通过更改 glDrawElements() 调用中的 indices 参数来绘制该数组的各个部分,如下所示:

glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // first 6 indices
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, (void*)(6*sizeof(GLuint))); // second 6 indices
// ...

也不是说在这里使用 3 个不同的着色器效率很低。如果您只是将颜色添加为另一个属性,并且对所有内容使用单个绘制调用(意味着单个着色器、单个 VAO),那将是最好的选择。

【讨论】:

天哪!我不敢相信我错过了这么简单的事情。当然它没有工作。谢谢!我知道这也不是很有效。只是想尝试一下。

以上是关于无法使用 VAO 和 EBO (openGL) 绘制多个对象的主要内容,如果未能解决你的问题,请参考以下文章

VAO VBO EBO

OpenGL ES之VBOEBO与VAO的说明和使用

Android OpenGL ES 学习 – 使用 VBOVAO 和 EBO/IBO 优化程序

OpenGL入门之渲染管线pipeline,VAOVBO和EBO

我的OpenGL学习进阶之旅介绍 顶点数组对象VAO并实战一下

[转]OpenGL图形渲染管线VBOVAOEBO概念及用例