简体   繁体   中英

Vbo how to draw primitivies

Hi I am trying to learn OpenGl using this tutorial http://www.opengl-tutorial.org/beginners-tutorials/tutorial-2-the-first-triangle/ I would say that this tutorial is not good for person who start. They show a lot of code which works for 1 triangle but there is no more examples. Atm I am trying to write function which draw. Lines or Rectangles and I have problem because it is hard to hard for me. I want to wrote reusable function. But I don't understand VBO :/ I want to write function draw which will be executed from main loop.

class lines{
    public: lines(){
    }
    static void draw(GLuint ve){

        float vertices[] = {-0.5f, -0.5f, 0.5f, 0.5f};
        unsigned int indices[] = {0, 1};

        glEnableClientState(GL_VERTEX_ARRAY);
        glVertexPointer(2, GL_FLOAT, 0, vertices);
        glDrawElements(GL_LINES, 2, GL_UNSIGNED_INT, indices);
    }
};


    // Ensure we can capture the escape key being pressed below
glfwEnable( GLFW_STICKY_KEYS );

// Dark blue background
glClearColor(0.0f, 0.0f, 0.4f, 0.0f);

GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);

// Create and compile our GLSL program from the shaders
GLuint programID = LoadShaders( "SimpleVertexShader.vertexshader", "SimpleFragmentShader.fragmentshader" );


static const GLfloat g_vertex_buffer_data[] = { 
    -0.8f, -1.0f,0.0f,
    0.8f,  -1.0f, 0.0f,
    -0.8f,   1.0f, 0.0f,
    -0.8f, 1.0f, 0.0f,
    0.8f, 1.0f, 0.0f,
    0.8, -1.0f, 0.0f,
};

GLuint vertexbuffer;
glGenBuffers(1, &vertexbuffer);
glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
glBufferData(GL_ARRAY_BUFFER, sizeof(g_vertex_buffer_data), g_vertex_buffer_data, GL_STATIC_DRAW);

do{

    // Clear the screen
    glClear( GL_COLOR_BUFFER_BIT );

    // Use our shader
    glUseProgram(programID);

    // 1rst attribute buffer : vertices
    glEnableVertexAttribArray(0);
    glBindBuffer(GL_ARRAY_BUFFER, vertexbuffer);
    glVertexAttribPointer(
        0,                  // attribute 0. No particular reason for 0, but must match the layout in the shader.
        3,                  // size
        GL_FLOAT,           // type
        GL_FALSE,           // normalized?
        0,                  // stride
        (void*)0            // array buffer offset
    );

    glDrawArrays(GL_TRIANGLES, 0, 6); 

    glDisableVertexAttribArray(0);
    lines::draw(vertexbuffer);
    // Swap buffers
    glfwSwapBuffers();

} // Check if the ESC key was pressed or the window was closed
while( glfwGetKey( GLFW_KEY_ESC ) != GLFW_PRESS &&
       glfwGetWindowParam( GLFW_OPENED ) );

To draw primitives, you give OpenGL a list of vertices and the pattern in which to connect them (eg to form lines, triangles etc). The simplest way to do this is immediate mode ( glBegin / glVertex3f / glEnd ). Immediate mode is quite slow and it's much faster to pass all vertices at once via "vertex arrays" (not vertex array objects (VAO), they're different), where they're laid out sequentially in a 1D array just like your g_vertex_buffer_data . Vertex arrays are when you pass a pointer to your array in main memory to glVertexPointer . This is faster but sends the entire array to the GPU every time you draw. A vertex buffer object (VBO) allows you to send and store the array in GPU memory. With a VBO, only a draw call is needed to be sent and the data is already on the GPU so there's no transfer and it's much faster. VAOs group the glVertexPointer arguments to make binding faster and glDraw*Indirect allow drawing using parameters already in GPU memory, but I'd leave those for later.

Most tutorials start off with hard coded vertices, but of course for an end application this isn't practical. You want to store many bits of arbitrary geometry. A mesh class, similar to your lines is quite common. The key point is that a mesh has a list of vertices. You can add an array of indices to re-reference existing vertices to form primitives, saving memory and computation of duplicate vertices.

But I'd start with immediate mode rendering for now - less things can go wrong.

To start you off,

struct vec3f {
    float x, y, z;
    vec3f(float nx, float ny, float nz) : x(nx), y(ny), z(nz) {}
};

class Mesh {
    std::vector<vec3f> vertices;
public:
    void add(float x, float y, float z)
    {
        vertices.push_back(vec3f(x, y, z));
    }
    void draw()
    {
        glBegin(GL_LINE_STRIP);
        for (size_t i = 0; i < vertices.size(); ++i)
            glVertex3f(vertices[i].x, vertices[i].y, vertices[i].z);
        glEnd();
    }
};

and to use it,

Mesh square;
...
//Lots of add() and push_back is slow. Much faster to use
//dynamic allocation yourself and copy data in bigger blocks,
//but this will do for an example.
square.add(-1,-1,0);
square.add(-1,1,0);
square.add(1,1,0);
square.add(1,-1,0);
square.add(-1,-1,0); //these could also be read from a file
...
line.draw();

This idea allows you to have a global std::list<Mesh> lines; for example. You can add lines to it once at initialization, from a file, or at runtime. Then your drawing code just has to call draw on each element. Later you could extend this idea to support triangles as well (eg. make GL_LINE_STRIP a primitive member). Later you can add indices, normals (here you'll want to look up interleaving vertex/normal data using the stride parameter of gl*Pointer functions), colours/materials etc.

On to your question about using VBOs. glGenBuffers will gives you a unique handle to reference your VBO - simply an integer. When ever you modify or use that VBO you need to bind it.

glBufferData(GL_ARRAY_BUFFER... does the actual initialization/resize of the currently bound GL_ARRAY_BUFFER (if not already) and transfers data if the data argument is non-NULL.

glVertexAttribPointer will assume you're passing it an array in main memory for "vertex arrays" mentioned above unless you glBindBuffer(GL_ARRAY_BUFFER, vbo) , in which case the final argument becomes an offset into that VBO. After you've called glVertexAttribPointer .

So,

1. glGenBuffers
2. glBufferData (while the buffer is bound)

that's initialization taken care of. On to drawing...

3. glVertexAttribPointer (while the buffer is bound)

then

4. glDrawArrays

or

4. glDrawElements (while an element array buffer is bound, containing the order in which to draw the vertices)

So to extend the mesh class, you'd want at least a GLuint vertexBuffer; . Maybe add a function upload() to generate a handle and buffer the current data. You could then call vertices.clear() (actually, for std::vector use this ) assuming you don't need the vertex data in main memory anymore. Then change the draw function to bind the vbo, call glVertexPointer , unbind and glDrawArrays (from a numVertices member as vertices.size() might be zero now).

Here's some related posts:

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM