I'm confused on the concept of buffers. I understand the very basics with glBufferData and glBufferSubData. Using glBufferSubData before a render function in your main loop you can use a offset and size parameter to store multiple model objects in a vertex buffer and a index buffer. Then at render you bind the single vertex buffer and call glDrawElements with the correct offsets and sizes to render multiple objects.
Is this the case with glMapBuffer? Or am I suppose to call glMapBuffer to link vertex and index data at render, then draw?
GLuint vertexArrayId;
GLuint verticesBufferId;
GLuint indicesBufferId;
void setupBuffers() {
glGenVertexArrays(1, &vertexArrayId);
glBindVertexArray(vertexArrayId);
glGenBuffers(1, &verticesBufferId);
glGenBuffers(1, &indicesBufferId);
glBindBuffer(GL_ARRAY_BUFFER, verticesBufferId);
glBufferData(GL_ARRAY_BUFFER, VERTICES_SIZE, NULL, GL_STATIC_DRAW);
// glMapBuffer stuff here
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indicesBufferId);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, INDICES_SIZE, NULL, GL_STATIC_DRAW);
// glMapBuffer stuff here
}
void render() {
glBindBuffer(GL_ARRAY_BUFFER, verticesBufferId);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)((sizeof(GLfloat) * 0)));
glEnableVertexAttribArray(0);
// glDrawElements stuff here
glDisableVertexAttribArray(0);
// Model, View, Projection transformations here
// glUniformMatrix4fv
}
You can use glMapBuffer to upload data from ram to the GPU:
glBindBuffer(GL_ARRAY_BUFFER, verticesBufferId);
void *data = glMapBuffer( GL_ARRAY_BUFFER, ... );
// copy vertex data from instance
::memcpy( data, vertices, vertexSize );
...
glUnmapBuffer( ... );
For instance when you want to upload vertex- and index-data from a model. You can use the pointer returned from glMapBuffer like a raw-c-pointer ( just for convenience )
Keep in mind: glMapBuffer is not fast, so try to avoid using it during your render loop. After calling the glUnMap the data will be transfered to the GPU.
There are much better ways when you have to upload data from the CPU to the GPU periodically like using using Uniform-Blocks.
I found this blog-post, where the mapping is explained really well ( with all its drawbacks ): Mapping in OpenGL
Related
I am trying to render multiple models sharing the same VAO and vertex format (following top answer on this post Render one VAO containing two VBOs), however I cannot get it to work with GL_ELEMENT_ARRAY_BUFFER nor can I find any resource/example to help me. Is it even possible to do that or does the element array buffer work in a way that is incompatible with glVertexAttribFormat/glBindVertexBuffer and sharing VAOs? or am I missing the ELEMENT_ARRAY_BUFFER equivalent of glBindVertexBuffer?
My VAO is first created this way:
glCreateVertexArrays(1, &sharedVao);
glBindVertexArray(sharedVao);
glEnableVertexAttribArray(0);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(0, 0);
// (just 1 for the example but there is more)
glBindVertexArray(0);
Then my model buffers are created as follow:
glBindVertexArray(sharedVao); // tried with and without binding vao first, no success
glCreateBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(vertex), vertices.data(), GL_STATIC_DRAW);
// (just 1 for the example but there is more)
glCreateBuffers(1, &ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, triangles.size() * sizeof(triangle), triangles.data(), GL_STATIC_DRAW);
glBindVertexArray(0);
And finally I render as follow:
glBindVertexArray(sharedVao);
for (auto const& model : models)
{
glBindVertexBuffer(0, model.vbo, sizeof(vertex));
// (just 1 for the example but there is more)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, model.ebo);
// also tried glVertexArrayElementBuffer(sharedVao, model.ebo);
glDrawElements(GL_TRIANGLES, model.triangleCount * 3, GL_UNSIGNED_INT, nullptr);
}
Note that it does work if I start rendering the same VAO with glDrawArray (so without element array buffer).
This C++ GLSL Multiple IBO in VAO may be of valuable use, but still not sure what it means for sharing VAO formats for multiple models... (also realized that calling it IBO gives me more results than EBO...).
EDIT: this question was originally closed as supposedly a duplicate of Rendering meshes with multiple indices but it is not. Unlike this other question I am not talking about having different indices for different data (ex: indices for positions, indices for normals, indices for texture coords, etc.) but to have different indices per draw calls, while still using the same VAO format (the same way it is done with VBO and glBindVertexBuffer in Render one VAO containing two VBOs).
Multiple draw calls with shared Vertex Array Object
The purpose of this method is to avoid the cost of changing VAO format (see glVertexAttribPointer and glVertexAttribFormat: What's the difference? or https://www.youtube.com/watch?v=-bCeNzgiJ8I&t=1860) by sharing VAO and only rebinding buffers for every draw.
A clear example that doesn't make use of Element Buffer Array can be seen here: Render one VAO containing two VBOs
Create shared VAO (could be only once per program):
GLuint sharedVao = 0;
glCreateVertexArray(1, &sharedVao);
glBindVertexArray(sharedVao);
glEnableVertexAttribArray(0);
glVertexAttribFormat(0, 3, GL_FLOAT, GL_FALSE, 0);
// binding each attribute from its own buffer so attrib_index == buffer_index
glVertexAttribBinding(0, 0);
glEnableVertexAttribArray(1);
glVertexAttribFormat(1, 2, GL_FLOAT, GL_FALSE, 0);
glVertexAttribBinding(1, 1);
Create mesh buffers (would be only once per mesh):
struct Mesh
{
GLuint m_ebo = 0;
std::array<GLuint, 2> m_vbos = 0;
GLuint m_triangleCount = 0;
};
// Binding shared VAO here is mandatory as operations accessing or modifying EBO's
// state are not guaranteed to succeed if it wasn't bound to a VAO.
// However, for every new model, binding the EBO will unbind the previous, and we
// will need to rebind EBO to shared VAO for every draw call.
glBindVertexArray(sharedVao);
glCreateBuffer(1, &mesh.m_ebo);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.m_ebo);
glBufferData(
GL_ELEMENT_ARRAY_BUFFER,
triangles.size() * sizeof(Triangle),
triangles.data(),
GL_STATIC_DRAW);
glBindVertexArray(0);
mesh.m_triangleCount = triangles.size();
glCreateBuffers(2, mesh.m_vbos.data());
glBindBuffer(GL_ARRAY_BUFFER, mesh.m_vbos[0]);
glBufferData(
GL_ARRAY_BUFFER,
positions.size() * sizeof(glm::vec3),
positions.data(),
GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, mesh.m_vbos[1]);
glBufferData(
GL_ARRAY_BUFFER,
textureCoords.size() * sizeof(glm::vec2),
textureCoords.data(),
GL_STATIC_DRAW);
Render loop:
// Bind shared VAO only once
// If drawing with different set of vertex data bound, use glEnableVertexAttribArray
// or glDisableVertexAttribArray before draw calls
glBindVertexArray(sharedVao);
for (auto const& mesh : meshes)
{
glBindVertexBuffer(0, mesh.m_vbos[0], 0, sizeof(glm::vec3));
glBindVertexBuffer(1, mesh.m_vbos[1], 0, sizeof(glm::vec2));
// This is the key difference with existing example on sharing VAO:
// EBO must be rebound for every draw (unless 2 draws share the same primitive indices)
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.m_ebo);
glDrawElements(GL_TRIANGLES, mesh.m_triangleCount * 3, GL_UNSIGNED_INT, nullptr);
}
Quite simply, my question is this. My code is below for a C++ header file that keeps track of and handles a vertex buffer object in OpenGL. In order to comply with core standards, it must use vertex arrays (VAO). Since glGenVertexArrays() and other related methods are called, is this rendering method still have information stored on the GPU or in RAM?
class VertexBuffer
{
private:
int vertCount;
GLuint vertArray;
GLuint vertBuffer;
public:
VertexBuffer();
void renderInterleaved();
void createInterleaved(GLfloat*, int);
void destroy();
GLuint* getVboId();
};
VertexBuffer::VertexBuffer()
{
glGenVertexArrays(1, &vertArray);
glGenBuffers(1, &vertBuffer);
}
void VertexBuffer::renderInterleaved()
{
glBindVertexArray(vertArray);
glBindBuffer(GL_ARRAY_BUFFER, vertBuffer);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
int stride = 7 * sizeof(GLfloat);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, stride, (void*) 0);
glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, stride, (void*)(3 * sizeof(GLfloat)));
glDrawArrays(GL_TRIANGLES, 0, vertCount);
glDisableVertexAttribArray(1);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
}
void VertexBuffer::createInterleaved(GLfloat* data, int vertices)
{
vertCount = vertices;
glBindBuffer(GL_ARRAY_BUFFER, vertBuffer);
glBufferData(GL_ARRAY_BUFFER, vertices * 21 * sizeof(GLfloat), data, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void VertexBuffer::destroy()
{
glDeleteBuffers(1, &vertBuffer);
glDeleteVertexArrays(1, &vertArray);
}
GLuint* VertexBuffer::getVboId()
{
return &vertBuffer;
}
The term "vertex buffer object" is, and has always been, a misnomer, created by the unfortunate naming of the extension that introduced buffer objects.
There is no "vertex buffer object." There is only a buffer object, which represents a linear array of OpenGL-managed memory. Buffer objects can be used to store vertex data for rendering operations, but there is nothing intrinsic about any particular buffer object that lets it do that. It is only a question of how you use it.
If your object encapsulates the parameters you would use for glVertexAttribPointer, then it is not encapsulating just a buffer object. A buffer object does not know how to render itself or what data it contains; it's just a buffer. The closest OpenGL concept it represents is a vertex array object.
But your code isn't really using VAOs either. Every time you call renderInterleaved, you're setting state that's already stored as part of the VAO. All of those glVertexAttribPointer and glEnableVertexAttribArray calls are recorded into the VAO. That's something you ought to set up once, then just bind the VAO later and render with it. So yes, it is a less-efficient VAO.
But your class encapsulates more than a VAO. It knows how to render itself. So the best name for your class would be "mesh" or something of that effect.
When I first add some vertices to the buffer, these are the relevant functions I'm calling
// Create and bind the object's Vertex Array Object:
glGenVertexArrays(1, &_vao);
glBindVertexArray(_vao);
// Create and load vertex data into a Vertex Buffer Object:
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(float), &vertices[0], GL_STATIC_DRAW);
// Tells OpenGL that there is vertex data in this buffer object and what form that vertex data takes:
// Obtain attribute handles:
_posAttrib = glGetAttribLocation(program, "position");
glEnableVertexAttribArray(_posAttrib);
glVertexAttribPointer(_posAttrib, // attribute handle
4, // number of scalars per vertex
GL_FLOAT, // scalar type
GL_FALSE,
0,
0);
// Unbind vertex array:
glBindVertexArray(0);
But later on in my program, I want to add some more vertices.
I do this by the following (within a separate function:
add_vertices(x,y); //adds the necessary vertices to the vector.
glGenBuffers(1, &_vbo);
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, (TRIANGLE_AMOUNT+1)*4*_number_of_circles * sizeof(float), &vertices[0], GL_STATIC_DRAW);
Assuming the funky size in the 2nd argument of glBufferData is fine, am I missing anything? Are there any other OpenGL functions that need calling?
I'm not getting any errors, but when I'm trying to draw the extra shapes with the new vertices by looping over glDrawArrays with different subsets of the vertices, nothing happens. Only the first shape gets drawn.
I hope that this is semi-coherent...let me know if there's any info I haven't provided.
Cheers.
In OpenGL, changing the buffers and exchanging the buffers data are two different things which require different actions to be taken afterwards:
Exchanging the data
In this case a previously generated vbo is required. To upload new data, it is only required to bind the buffer and to buffer new data:
glBindBuffer(GL_ARRAY_BUFFER, _vbo);
glBufferData(GL_ARRAY_BUFFER, (TRIANGLE_AMOUNT+1)*4*_number_of_circles * sizeof(float),
&vertices[0], GL_STATIC_DRAW);
Creating a new buffer
In this case a new buffer is generated by glGenerateBuffers and (in addition to uploading the data) all VAO bindings have to be updated.
Sidenote: In the code shown above, you create a new buffer without deleting the previous buffer.
I've been trying to use Vertex Buffer Objects to save vertex data on the GPU and reduce the overhead, but I cannot get it to work. The code is below.
From what I understand you generate the buffer with glGenBuffers, then you bind the buffer with glBindBuffer so it's ready to be used, then you write data to it with glBufferData and its done and can be unbinded and ready for use later with simply binding it again.
However the last part is what I'm having trouble with, when I bind it after I have created and loaded data to it and try to draw using it, it gives me lots of GL Error: Out of Memory.
I doubt that I am running out of memory for my simple mesh, so I must be doing something very wrong.
Thanks.
EDIT 1: I call glGetError after every frame, but since this is the only OpenGL I do in the entire program it shouldn't be a problem
//when loading the mesh we create the VBO
void createBuffer()
{
GLuint buf;
glGenBuffers(1, &buf);
glBindBuffer(GL_ARRAY_BUFFER, buf);
glBufferData(GL_ARRAY_BUFFER, vertNormalBuffer->size() * sizeof(GLfloat), (GLvoid*) bufferData, GL_STATIC_DRAW);
//EDIT 1: forgot to show how I handle the buffer
model->vertexNormalBuffer = &buf;
//Unbinds it
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Fighter::doRedraw(GLuint shaderProgram)
{
glm::mat4 transformationMatrix = getTransform();
GLuint loc = glGetUniformLocation(shaderProgram,"modelviewMatrix");
glUniformMatrix4fv(loc, 1, GL_FALSE, (GLfloat*) &transformationMatrix);
glBindBuffer(GL_ARRAY_BUFFER, *model->vertexNormalBuffer);
//If I uncomment this line below all works wonderfully, but isnt the purpose of VBO of not uploading the same data again and again?
//glBufferData(GL_ARRAY_BUFFER, model->vertAndNormalArraySize * sizeof(GLfloat), model->vertAndNormalArray, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(2);
renderChild(model, model);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Fighter::renderChild(ModelObject* model, ModelObject* parent)
{
//Recursively render the mesh children
for(int i = 0; i < model->nChildren; i++)
{
renderChild( dynamic_cast<ModelObject*>(model->children[i]), parent);
}
//Vertex and normal data are interlieved
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat),(void*)(model- >vertexDataPosition*sizeof(GLfloat)));
glVertexAttribPointer(2, 4, GL_FLOAT, GL_FALSE, 8*sizeof(GLfloat), (void*)((model->vertexDataPosition + 4)*sizeof(GLfloat)));
//Draws using two sets of indices
glDrawElements(GL_QUADS, model->nQuads * 4, GL_UNSIGNED_INT,(void*) model->quadsIndices);
glDrawElements(GL_TRIANGLES, model->nTriangles * 3, GL_UNSIGNED_INT, (void*) model->trisIndices);
}
This is your problem:
model->vertexNormalBuffer = &buf;
/* ... */
glBindBuffer(GL_ARRAY_BUFFER, *model->vertexNormalBuffer);
You're storing the address of your buf variable, rather than its contents, and then it falls out of scope when createBuffer returns, and is most likely overwritten with other data, so when you're later rendering, you're using an uninitialized buffer. Just store the contents of buf in your vertexNormalBuffer field instead.
I'll admit I don't know why OpenGL thinks it proper to say that it's "out of memory" just because of that, but perhaps you're just invoking undefined behavior. It does explain, however, why it starts working when you re-fill the buffer with data after you rebind it, because you then implicitly initialize the buffer that you just bound.
I am transferring over my vertex arrays functions to VBOs to increase the speed of my application.
Here was my original working vertex array rendering function:
void BSP::render()
{
glFrontFace(GL_CCW);
// Set up rendering states
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, sizeof(Vertex), &vertices[0].x);
glTexCoordPointer(2, GL_FLOAT, sizeof(Vertex), &vertices[0].u);
// Draw
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, indices);
// End of rendering - disable states
glDisableClientState(GL_VERTEX_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
Worked great!
Now I am moving them into VBOs and my program actually caused my graphics card to stop responding. The setup on my vertices and indices are exactly the same.
New setup:
vboId is setup in the bsp.h like so: GLuint vboId[2];
I get no error when I just run the createVBO() function!
void BSP::createVBO()
{
// Generate buffers
glGenBuffers(2, vboId);
// Bind the first buffer (vertices)
glBindBuffer(GL_ARRAY_BUFFER, vboId[0]);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Now save indices data in buffer
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
}
And the rendering code for the VBOS. I am pretty sure it's in here. Just want to render whats in the VBO like I did in the vertex array.
Render:
void BSP::renderVBO()
{
glBindBuffer(GL_ARRAY_BUFFER, vboId[0]); // for vertex coordinates
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, vboId[1]); // for indices
// do same as vertex array except pointer
glEnableClientState(GL_VERTEX_ARRAY); // activate vertex coords array
glVertexPointer(3, GL_FLOAT, 0, 0); // last param is offset, not ptr
// draw the bsp area
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_BYTE, BUFFER_OFFSET(0));
glDisableClientState(GL_VERTEX_ARRAY); // deactivate vertex array
// bind with 0, so, switch back to normal pointer operation
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
}
Not sure what the error is but I am pretty sure I have my rendering function wrong. Wish there was a more unified tutorial on this as there are a bunch online but they are often contradicting eachother.
In addition what Miro said (the GL_UNSIGNED_BYTE should be GL_UNSIGNED_SHORT), I don't think you want to use numVertices but numIndices, like in your non-VBO call.
glDrawElements(GL_TRIANGLES, numIndices, GL_UNSIGNED_SHORT, 0);
Otherwise your code looks quite valid and if this doesn't fix your problem, maybe the error is somewhere else.
And by the way the BUFFER_OFFSET(i) thing is usuaully just a define for ((char*)0+(i)), so you can also just pass in the byte offset directly, especially when it's 0.
EDIT: Just spotted another one. If you use the exact data structures you use for the non-VBO version (which I assumed above), then you of course need to use sizeof(Vertex) as stride parameter in glVertexPointer.
If you are passing same data to glDrawElements when you aren't using VBO and same data to VBO buffer. Then parameters little differs, without FBO you've used GL_UNSIGNED_SHORT and with FBO you've used GL_UNSIGNED_BYTE. So i think VBO call should look like that:
glDrawElements(GL_TRIANGLES, numVertices, GL_UNSIGNED_SHORT, 0);
Also look at this tutorial, there are VBO buffers explained very well.
How do you declare vertices and indices?
The size parameter to glBufferData should be the size of the buffer in bytes and if you pass sizeof(vertices) it will return the total size of the declared array (not just what is allocated).
Try something like sizeof(Vertex)*numVertices and sizeof(indices[0])*numIndices instead.