Why does glDrawArrays fail if glDrawElements is called first?

  c++, opengl

I have some code which is supposed to draw a set of points on the screen. At the same time, I have some code which should draw lines on the screen. These both use the same buffer. The lines are drawn with glDrawElements, while the points are drawn with glDrawArrays. When glDrawElements is called, the glDrawArrays function does nothing. The order doesn’t matter, if I call glDrawElements before glDrawArrays or not. If I don’t call glDrawElements, glDrawArrays successfully draws the points to the screen. Here is my code.

static int render_loop(GLFWwindow *window) {
    
    // Basic opengl setup (do not delete)
    // Make VAOS
    // Make VBOS
    // Shaders
    // Programs
    // attach shaders, then link, then use
    // AttribArrays
    // after program link and use
    // do not forget to enable them (before the pointer call)
    // Draw
    // Swap buffer (do not forget)
    
    int errored = 0;
    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
    
    GLuint vao;
    glGenVertexArrays(1, &vao);
    
    glBindVertexArray(vao);
    
    // These are constants required for the perfect tetrahedron.
    const float FOUR_FIFTHS = 0.8f,
        TWO_SQRT_SIX = 2.0f * std::sqrt(6.0f),
        FOUR_SQRT_THREE = 4.0f * std::sqrt(3.0f),
        FIFTEEN = 15.0f,
        FOUR_SQRT_THREE_OVER_FIFTEEN = -FOUR_SQRT_THREE / FIFTEEN,
        TWO_SQRT_SIX_OVER_FIFTEEN = -TWO_SQRT_SIX / FIFTEEN;
    
    const uint NUM_POINTS = 50000;
    
    // Declare the tetrahedron
    // This is a perfect tetrahedron
    // whose center lies at the origin
    // and whose side length is 1.6
    // This is computed ahead of time, because computing it later would be a pain.
    #define vec33(X, Y, Z) vec3{X, -Y, Z}
    Tetrahedron main_tetra{
        vec33(FOUR_FIFTHS, -FOUR_SQRT_THREE_OVER_FIFTEEN, -TWO_SQRT_SIX_OVER_FIFTEEN),
        vec33(-FOUR_FIFTHS, -FOUR_SQRT_THREE_OVER_FIFTEEN, -TWO_SQRT_SIX_OVER_FIFTEEN),
        vec33(0.0f, 2.0f * FOUR_SQRT_THREE_OVER_FIFTEEN, -TWO_SQRT_SIX_OVER_FIFTEEN),
        vec33(0.0f, 0.0f, TWO_SQRT_SIX / 5.0f)
    };
    #undef vec33
    
    vec3 *vertices = (vec3 *)&main_tetra;
    
    vec3 points[NUM_POINTS + 4];
    
    points[0] = vertices[0];
    points[1] = vertices[1];
    points[2] = vertices[2];
    points[3] = vertices[3];
    
    {
        // Set the other points. This is redacted. This does work,
        // and each point has a different value.
    }
    
    // These are the elements that are displayed with glDrawElements later.
    uint elements[] = {
        0, 1, 0, 2, 1, 2,
        3, 0, 3, 1, 3, 2
    };
    
    GLuint vbo;
    glGenBuffers(1, &vbo);
    
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(points), (const void *)points, GL_STATIC_DRAW);
    
    // These load the shaders from files. The contents of these files are included later.
    Shader vertex_shader = Shader::load_shader("shaders/vertex.glsl", GL_VERTEX_SHADER),
        fragment_shader = Shader::load_shader("shaders/fragment.glsl", GL_FRAGMENT_SHADER);
    
    if (!vertex_shader.is_valid()) {
        // The DPRINTF_NA macro will print to the console.
        DPRINTF_NA("Invalid vertex shader.");
    }
    if (!fragment_shader.is_valid()) {
        DPRINTF_NA("Invalid fragment shader.");
    }
    
    Program program{};
    
    if (!program.is_valid()) {
        DPRINTF_NA("Error: failed to create program.");
    }
    
    program.attach(vertex_shader);
    program.attach(fragment_shader);
    
    program.bind_frag_data_location(0, "out_color");
    
    program.link();
    program.use();
    
    program.get_attrib("position").enable_vertex_attrib_array().vertex_attrib_pointer(3, GL_FLOAT, GL_FALSE, 0, (void *)0);
    
    glfwShowWindow(window);
    while (!glfwWindowShouldClose(window)) {
        glClear(GL_COLOR_BUFFER_BIT);
        // This is the glDrawElements call. It will draw 6 lines (12 vertices).
        // When this is called, the following glDrawArrays call does nothing.
        // Putting the glDrawArrays call before the glDrawElements call does nothing.
        glDrawElements(GL_LINES, 12, GL_UNSIGNED_INT, elements);
        // When glDrawElements is not called, this call works perfectly.
        glDrawArrays(GL_POINTS, 4, NUM_POINTS);
        glFlush();
        // ABSOLUTELY DO NOT FORGET TO DO THIS. DO NOT FORGET.
        // IT IS IMPERATIVE TO REMEMBER TO SWAP THE BUFFERS!
        glfwSwapBuffers(window);
        glfwPollEvents();
        GLenum error;
        while ((error = glGetError()) != GL_NO_ERROR) {
            DPRINTF("GL Error %d: %sn", error, glErrorString(error));
        }
    }
    
    // Delete stuff.
    glDeleteBuffers(1, &vbo);
    glDeleteVertexArrays(1, &vao);
    
    // The program's and shaders' destructors will be called at the end of the function.
    
    return errored;
}

Here are the shaders:

// shaders/fragment.glsl
#version 330 core

out vec4 out_color;
in vec4 color;

void main(void) {
    out_color = color;
}


// shaders/vertex.glsl
#version 330 core

in vec3 position;
out vec4 color;

void main(void) {
    gl_Position = vec4(position, 1.0);
    color = vec4((1.0 + position)/2.0, 1.0);
}

How can I make it so both the glDrawArrays and the glDrawElements functions successfully draw to the screen.

Source: Windows Questions C++

LEAVE A COMMENT