Related
Problem
I have a vector of pointers to particles:
struct Particle {
vec3 pos; // just 3 floats, GLM vec3 struct
// ...
}
std::vector<Particle *> particles;
I want to use this vector as the source of data for an array buffer in OpenGL
Like this:
glGenBuffers(1, &particleBuffer);
glBindBuffer(GL_ARRAY_BUFFER, particleBuffer);
int bufferSize = sizeof(Particle) * particles->size();
glBufferData(GL_ARRAY_BUFFER, bufferSize, /* What goes here? */, GL_STATIC_DRAW);
glEnableVertexAttribArray(positionAttribLocation);
glVertexAttribPointer(positionAttribLocation, 3, GL_FLOAT, GL_FALSE, sizeof(Particle), (void *)0);
Where the interesting line is glBufferData( ... )
How do I get OpenGL to get that the data is pointers?
How do I get OpenGL to get that the data is pointers?
You don't.
The whole point of buffer objects is that the data lives in GPU-accessible memory. An pointer is an address, and a pointer to a CPU-accessible object is a pointer to a CPU address. And therefore is not a pointer to GPU-accessible memory.
Furthermore, accessing indirect data structures like that is incredibly inefficient. Having to do two pointer indirection just to access a single value basically destroys all chance of cache coherency on memory accesses. And without that, every independent particle is a cache miss.
That's bad. Which is why OpenGL doesn't let you do that. Or at least, it doesn't let you do it directly.
The correct way to do this is to work with a flat vector<Particle>, and move them around as needed.
glBufferData requires a pointer to an array of the data you wish to use. In your case, a GLfloat[] would be used for the vertices. You could write a function which creates a GLfloat[] from the vector of particles. The code I use creates a GLfloat[] and then passes it as a pointer to a constructor which then sets the buffer data. Here is my code;
Creating the Vertex Array - GlFloat[]
GLfloat vertices[] = { 0, 0, 0,
0, 3, 0,
8, 3, 0,
8, 0, 0 };
After I have created the vertices I then create a buffer object (Which just creates a new buffer and sets it's data);
Buffer* vbo = new Buffer(vertices, 4 * 3, 3);
The constructor for my buffer object is;
Buffer::Buffer(GLfloat* data, GLsizei count, GLuint componentCount) {
m_componentCount = componentCount;
glGenBuffers(1, &m_bufferID);
glBindBuffer(GL_ARRAY_BUFFER, m_bufferID);
glBufferData(GL_ARRAY_BUFFER, count * sizeof(GLfloat), data, GL_STATIC_DRAW); //Set the buffer data to the GLFloat pointer which points to the array of vertices created earlier.
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
After this array has been passed to the buffer, you can delete the object to save memory however it is recommended to hold onto it in case it is reused later.
For more information and better OpenGL practices I recommend you check out the following youtube playlist by TheChernoProject. https://www.youtube.com/watch?v=qTGMXcFLk2E&list=PLlrATfBNZ98fqE45g3jZA_hLGUrD4bo6_&index=12
How do I read back multiple transform feedback buffers when using GL_SEPARATE_ATTRIBS instead of just one like with GL_INTERLEAVED_ATTRIBS? In this example I set up two buffers and setup them for transform feedback.
//gen,bind,buffer GL_ARRAY_BUFFER buffer0
//gen,bind,buffer GL_ARRAY_BUFFER buffer1
//gen,bind GL_TRANSFORM_FEEDBACK feedback
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 0, buffer0);
glBindBufferBase(GL_TRANSFORM_FEEDBACK_BUFFER, 1, buffer1);
const GLchar* _varyings[] = { "pos", "color" };
glTransformFeedbackVaryings(program, 2, _varyings, GL_SEPARATE_ATTRIBS);
glLinkProgram(program);
//bind VAO and draw with TF enabled
glBeginTransformFeedback(GL_TRIANGLES);
glDrawArrays(GL_TRIANGLES, 0, numberOfVertices);
glEndTransformFeedback();
With a single buffer and GL_INTERLEAVED_ATTRIBS I am able to read back a single buffer by calling glGetBufferSubData on GL_TRANSFORM_FEEDBACK_BUFFER:
std::vector<float> fb;
fb.resize(3*numberOfVertices);
glGetBufferSubData(GL_TRANSFORM_FEEDBACK_BUFFER, 0, fb.size() * sizeof(float), fb.data());
However, how do I tell OpenGL that I want to read back both buffers buffer0 and buffer1 indexed (0,1) with glBindBufferBase of GL_TRANSFORM_FEEDBACK_BUFFER?
All tutorials I have found always simplify this to a single buffer with GL_INTERLEAVED_ATTRIBS and just explain that it is possible to use separate buffers to match the TF output according to the VAO input layout or simply don't read back buffer data at all.
There was no example available for the specific question asked as reading back multiple buffers with transform feedback is not different from reading back multiple buffers in general. You bind the buffer - you read it. The transform feedback object and it's buffer bases only tell OpenGL how it should store the captured output.
std::vector<float> _bufferfeedback0;
_bufferfeedback0.resize(3*numberOfVertices);//e.g. x,y,z per vertex
glBindBuffer(GL_ARRAY_BUFFER, buffer0);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, 3*numberOfVertices*sizeof(float), _bufferfeedback0.data());
std::vector<float> _bufferfeedback1;
_bufferfeedback1.resize(3*numberOfVertices);//e.g. r,g,b per vertex
glBindBuffer(GL_ARRAY_BUFFER, buffer1);
glGetBufferSubData(GL_ARRAY_BUFFER, 0, 3*numberOfVertices*sizeof(float), _bufferfeedback1.data());
After giving up on the slow glBegin/glEnd technique, I finally decided to use VBOs. After hours and hours of frustration, I finally got it to compile. But it doesn't mean it works. The function "CreateVBO" executes without errors, but as soon as glutMainLoop() is called, the program crashes, it doesn't even call the Reshape() or Render() functions.
Here is the CreateVBO() function:
(note: the "lines" variable is equal to 4 million, the whole program is just a stress-test for rendering many lines before I can do something that actually makes sense)
void CreateVBO()
{
glGenBuffers = (PFNGLGENBUFFERSPROC)wglGetProcAddress("glGenBuffers");
glBindBuffer=(PFNGLBINDBUFFERPROC)wglGetProcAddress("glBindBuffer");
glBufferData=(PFNGLBUFFERDATAPROC)wglGetProcAddress("glBufferData");
glDeleteBuffers=(PFNGLDELETEBUFFERSPROC)wglGetProcAddress("glDeleteBuffers");
glMapBuffer=(PFNGLMAPBUFFERPROC)wglGetProcAddress("glMapBuffer");
for(float i=0; i<lines*2; i++)
{
t_vertices.push_back(i/9000);
t_vertices.push_back(5 * (((int)i%2)*2-1));
t_vertices.push_back(20);
vert_cols.push_back(0);
vert_cols.push_back(255);
vert_cols.push_back(0);
t_indices.push_back((int)i);
}
glGenBuffers(1, &VertexVBOID);
glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
glBufferData(GL_ARRAY_BUFFER, sizeof(float)*t_vertices.size(), &t_vertices[0], GL_STATIC_DRAW);
glGenBuffers(1, &IndexVBOID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(int)*t_indices.size(), NULL, GL_STATIC_DRAW);
GLvoid * buf = glMapBuffer( GL_ARRAY_BUFFER, GL_WRITE_ONLY );
memcpy( (void*)buf, &vert_cols[0], (size_t)(sizeof(float)*vert_cols.size()));
glBindBuffer( GL_ARRAY_BUFFER, 0 );
}
And here's the Render() function. I don't know what may be wrong with it since the program crashes before even calling that function.
void Display()
{
glRotatef(1, 0, 1, 0);
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
glBindBuffer(GL_ARRAY_BUFFER, VertexVBOID);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(t_vertices.size(), GL_FLOAT, 0, 0); //The starting point of the VBO, for the vertices
glEnableClientState(GL_COLOR_ARRAY);
glColorPointer(vert_cols.size(), GL_FLOAT, 0, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexVBOID);
glDrawElements(GL_LINES, lines, GL_UNSIGNED_INT, 0);
glutSwapBuffers();
//Sleep(16);
glutPostRedisplay();
}
Your code doesn't make sense.
First, you create your vertexVBOID big enough to hold your t_vertices vector, and fill it with exactly that array.
Then, you map that VBO and overwrite the data in it with the data from the vert_color vector. For drawing, you specify both the vertex position and the color attribute to come from the same position in memory - so effectively using the color as position, too.
But that is not the reason of the crash. There are actually two main reasons for crashing:
You create IndexVBOID big enough to hold your index array, but you never initalize that buffer with some data. You instead specify NULL as the data pointer, so the storage is created, but left uninitialized. So you end up with an undefined content in that buffer, and the GL will access arbtrary memory positions when drawing with that.
Your glVertexPointer and glColorPointer calls are wrong. The first parameter, size, is not the size of an array. It is the size of a single vector, which can be 1,2,3 or 4. Your array size is probably something else, so this calls end up generating an GL_INVALID_VALUE error, and not setting the attrib pointers at all. And by default, those pointers are NULL.
So ultimately, you are asking the GL to draw from undefined array indices relative to a NULL pointer. That's very likely to crash. In general, it is just undefined behavior, and the result could be anything.
Furthermore, you also keep the VertexVBOID buffer mapped - you never unmap it. It is invalid to use a buffer as the source or destination for GL commands as long as it is mapped (unless a persistent mapping is created, which is a relatively new feature which doesn't really belong here).
Does anyone know why this error is being thrown?
I thought I am binding to VBO when I use glEnableVertexAttribArray?
com.jogamp.opengl.GLException: array vertex_buffer_object must be bound to call this method
at jogamp.opengl.gl4.GL4bcImpl.checkBufferObject(GL4bcImpl.java:39146)
at jogamp.opengl.gl4.GL4bcImpl.checkArrayVBOBound(GL4bcImpl.java:39178)
at jogamp.opengl.gl4.GL4bcImpl.glVertexAttribPointer(GL4bcImpl.java:37371)
This is my code to draw ..
public void draw(final GL2ES2 gl, Matrix4f projectionMatrix, Matrix4f viewMatrix, int shaderProgram, final Vec3 position, final float angle) {
// enable glsl
gl.glUseProgram(shaderProgram);
// enable alpha
gl.glEnable(GL2ES2.GL_BLEND);
gl.glBlendFunc(GL2ES2.GL_SRC_ALPHA, GL2ES2.GL_ONE_MINUS_SRC_ALPHA);
// get handle to glsl variables
mPositionHandle = gl.glGetAttribLocation(shaderProgram, "vPosition");
setmColorHandle(gl.glGetUniformLocation(shaderProgram, "vColor"));
mProj = gl.glGetUniformLocation(shaderProgram, "mProj");
mView = gl.glGetUniformLocation(shaderProgram, "mView");
mModel = gl.glGetUniformLocation(shaderProgram, "mModel");
// perform translations
getModelMatrix().loadIdentity();
getModelMatrix().translate(new Vec3(position.x * 60.0f, position.y * 60.0f, position.z * 60.0f));
getModelMatrix().rotate(angle, 0, 0, 1);
// set glsl variables
gl.glUniform4fv(getmColorHandle(), 1, getColorArray(), 0);
gl.glUniformMatrix4fv(mProj, 1, true, projectionMatrix.getValues(), 0);
gl.glUniformMatrix4fv(mView, 1, true, viewMatrix.getValues(), 0);
gl.glUniformMatrix4fv(mModel, 1, true, getModelMatrix().getValues(), 0);
// Enable a handle to the triangle vertices
gl.glEnableVertexAttribArray(mPositionHandle);
// Prepare the triangle coordinate data
gl.glVertexAttribPointer(
getmPositionHandle(),
COORDS_PER_VERTEX,
GL2ES2.GL_FLOAT,
false,
vertexStride, 0L); // This is the line that throws error
// Draw the square
gl.glDrawElements(
GL2ES2.GL_TRIANGLES,
drawOrder.length,
GL2ES2.GL_UNSIGNED_SHORT,
0L);
// Disable vertex array
gl.glDisableVertexAttribArray(mPositionHandle);
gl.glDisable(GL2ES2.GL_BLEND);
gl.glUseProgram(0);
}
(I've never used OpenGL with Java, so I'll use C/C++ code, but I hope it will come across well)
You do not create or bind a Vertex Buffer Object.
First, use glGenBuffers to create a buffer, as so:
GLuint bufferID;
glGenBuffers(1, &bufferID);
This allocates a handle and stores it in bufferID.
Then, bind the buffer:
glBindBuffers(GL_ARRAY_BUFFER, bufferID);
This makes it the "current" buffer to use.
Next, fill the buffer with data. Assuming vertices is an array that stores your vertex coordinates, in flat format, with three floats per vertex:
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), &vertices, GL_STATIC_DRAW);
This actually puts the data in GPU memory.
Then enable the attribute array and set the pointer:
glEnableVertexAttribArray(mPositionHandle);
glVertexAttribPointer(mPositionHandle, 3, GL_FLOAT, 0, 0, 0);
This will make the data in vertices available for shader programs under the vertex attribute location of mPositionHandle.
The second-to-last parameter of glVertexAttribPointer is stride. In this example, it is 0, because the buffer contains only vertex position data. If you want to pack both vertex position data and color data in the same buffer, as so:
v1.positionX v1.positionY v1.positionZ v1.colorR v1.colorG v1.colorB
v2.positionX ...
you will need to use a non-zero stride. stride specifies the offset between one attribute and the next of the same type; with stride of 0, they are assumed to be tightly packed. In this case, you'll want to set a stride of sizeof(GLfloat) * 6, so that after reading one vertex's position, it will skip the color data to arrive at the next vertex, and similarily for colors.
// (create, bind and fill vertex buffer here)
glEnableVertexAttribArray(location_handle_of_position_data);
glVertexAttribPointer(location_handle_of_position_data, 3, GL_FLOAT, 0, sizeof(GLfloat) * 6, 0);
glEnableVertexAttribArray(location_handle_of_color_data);
glVertexAttribPointer(location_handle_of_color_data, 3, GL_FLOAT, 0, sizeof(GLfloat) * 6, sizeof(GLfloat) * 3);
The last parameter is the offset to the first attribute - the first color attribute starts after the third float.
Other considerations:
You should look into using Vertex Array Objects. It might or might not work without them, but by standard they are required, and they simplify the code in any case.
For the sake of simplicity, this example code stores color data in floats, but for real use bytes are preferable.
glVertexAttribPointer() specifies that data for the attribute should be pulled from the currently bound vertex buffer, using the parameters specified. So you need to call:
gl.glBindBuffer(GL_VERTEX_ARRAY, ...);
before you call glVertexAttribPointer().
glEnableVertexAttribArray() specifies that an array should be used for the vertex attribute. Otherwise, a constant value, specified with calls like glVertexAttrib4f() is used. But it does not specify that the array is in a buffer. And even more importantly, there's no way glVertexAttribPointer() would know which buffer to use for the attribute unless you bind a specific buffer.
I'm working through the excellent tutorials at arcsynthesis while building a graphics engine and have discovered I don't understand VAOs as much as I thought I had.
From the tutorial Chapter 5. Objects In Depth
Buffer Binding and Attribute Association
You may notice that glBindBuffer(GL_ARRAY_BUFFER) is not on that list, even though it is part of the attribute setup for rendering. The binding to GL_ARRAY_BUFFER is not part of a VAO because the association between a buffer object and a vertex attribute does not happen when you call glBindBuffer(GL_ARRAY_BUFFER). This association happens when you call glVertexAttribPointer.
When you call glVertexAttribPointer, OpenGL takes whatever buffer is at the moment of this call bound to GL_ARRAY_BUFFER and associates it with the given vertex attribute. Think of the GL_ARRAY_BUFFER binding as a global pointer that glVertexAttribPointer reads. So you are free to bind whatever you want or nothing at all to GL_ARRAY_BUFFER after making a glVertexAttribPointer call; it will affect nothing in the final rendering. So VAOs do store which buffer objects are associated with which attributes; but they do not store the GL_ARRAY_BUFFER binding itself.
I initially missed the last line "...but they do not store the GL_ARRAY_BUFFER binding itself". Before I noticed this line I thought that the currently bound buffer was saved once glVertexAttribPointer was called. Missing this knowledge, I built a mesh class and was able to get a scene with a number of meshes rendering properly.
A portion of that code is listed below. Note that I do not call glBindBuffer in the draw function.
// MESH RENDERING
/* ... */
/* SETUP FUNCTION */
/* ... */
// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);
// Setup vertex buffers
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
glBufferData(GL_ARRAY_BUFFER, _vertices.size() * sizeof(vertex), &_vertices[0], GL_STATIC_DRAW);
// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_normal);
glEnableVertexAttribArray(e_aid_color);
glEnableVertexAttribArray(e_aid_tex);
glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, pos));
glVertexAttribPointer(e_aid_normal, 3, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, norm));
glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, col));
glVertexAttribPointer(e_aid_tex, 2, GL_FLOAT, GL_FALSE, sizeof(vertex), (GLvoid*)offsetof(vertex, tex));
/* ... */
/* DRAW FUNCTION */
/* ... */
glBindVertexArray(_vertex_array_object_id);
glDrawArrays(GL_TRIANGLES, 0, _vertices.size());
Now that I'm about to start lighting I wanted to get some debug drawing up so I could verify all my normals are correct. Currently I just store all lines to be rendered for a frame in a vector. Since this data will likely change every frame, I'm using GL_DYNAMIC_DRAW and specify the data right before I render it.
Initially when I did this I would get garbage lines that would just point off into infinity. The offending code is below:
// DEBUG DRAW LINE RENDERING
/* ... */
/* SETUP FUNCTION */
/* ... */
// Setup vertex array object
glGenVertexArrays(1, &_vertex_array_object_id);
glBindVertexArray(_vertex_array_object_id);
// Setup vertex buffers
glGenBuffers(1, &_vertex_buffer_object_id);
glBindBuffer(GL_ARRAY_BUFFER, _vertex_buffer_object_id);
// Note: no buffer data supplied here!!!
// Setup vertex attributes
glEnableVertexAttribArray(e_aid_position);
glEnableVertexAttribArray(e_aid_color);
glVertexAttribPointer(e_aid_position, 3, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, pos));
glVertexAttribPointer(e_aid_color, 4, GL_FLOAT, GL_FALSE, sizeof(line_vertex), (GLvoid*)offsetof(line_vertex, col));
/* ... */
/* DRAW FUNCTION */
/* ... */
glBindVertexArray(_vertex_array_object_id);
// Specifying buffer data here instead!!!
glBufferData(GL_ARRAY_BUFFER, _line_vertices.size() * sizeof(line_vertex), &_line_vertices[0], GL_DYNAMIC_DRAW);
glDrawArrays(GL_LINES, 0, _line_vertices.size());
After a bit of hunting, as well as finding the detail I missed above, I found that if I call glBindBuffer before glBufferData in the draw function, everything works out fine.
I'm confused as to why my mesh rendering worked in the first place given this. Do I only need to call glBindBuffer again if I change the data in the buffer? Or is behavior undefined if you don't bind the buffer and I was just unlucky and had it work?
Note that I am targeting OpenGL version 3.0.
Do I only need to call glBindBuffer again if I change the data in the
buffer?
Yes, The VAO object remembers which buffers were bound each time you called glVertexAttribPointer whilst that VAO was bound, so you don't usually need to call glBindBuffer again. If you want to change the data in the buffer however, OpenGL needs to know which buffer you are changing, so you need to call glBindBuffer before calling glBufferData. It is irrelevant which VAO object is bound at this point.
A Vertex Array object holds the data set by glEnableVertexAttribArray, glDisableVertexAttribArray, glVertexAttribPointer, glVertexAttribIPointer, glVertexAttribDivisor and gl.bindBuffer(GL_ELEMENT_ARRAY_BUFFER)
To put it another way you could define a VAO as
struct VertexAttrib {
GLint size; // set by gVertexAttrib(I)Pointer
GLenum type; // set by gVertexAttrib(I)Pointer
GLboolean normalize; // set by gVertexAttrib(I)Pointer
GLsizei stride; // set by gVertexAttrib(I)Pointer
GLint buffer; // set by gVertexAttrib(I)Pointer (indirectly)
void* pointer; // set by gVertexAttrib(I)Pointer
GLint divisor; // set by gVertexAttribDivisor
GLboolean enabled; // set by gEnable/DisableVertexAttribArray
};
struct VertexArrayObject {
std::vector<VertexAttrib> attribs;
GLuint element_array_buffer; // set by glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ..)
};
How many attributes there are can be queried with
GLint num_attribs;
glGetIntegerv(GL_MAX_VERTEX_ATTRIBS, &num_attribs)
You can think of GL's global state has having a pointer to a VertexArrayObject that is set with glBindVertexArray
struct GLGlobalState {
VertexArrayObject default_vao;
VertexArrayObject* current_vao = &default_vao;
...
GLint current_array_buffer; // set by glBindBuffer(GL_ARRAY_BUFFER, ...)
}
GLGloalState gl_global_state;
void glBindVertexArray(GLint vao) {
gl_global_state.current_vao = vao == 0 ? &default_vao : getVAOById(vao);
}
And you can think of the other functions listed above working like this
void glEnableVertexAttribArray(GLuint index) {
gl_global_state.current_vao->attribs[index].enabled = GL_TRUE;
}
void glEnableVertexAttribArray(GLuint index) {
gl_global_state.current_vao->attribs[index].enabled = GL_FALSE;
}
void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized,
GLsizei stride, const void* pointer) {
VertexAttrib* attrib = &gl_global_state.current_vao->attribs[index];
attrib->size = size;
attrib->type = type;
attrib->normalized = normalized;
attrib->stride = stride;
attrib->pointer = pointer;
attrib->buffer = glGlobalState.current_array_buffer;
}
void glVertexAttribDivisor(GLuint index, GLuint divisor) {
gl_global_state.current_vao->attribs[index].divisor = divisor;
}
It might be easier to see it visually
from this diagram though the diagram above shows offset instead of pointer since it's from WebGL which doesn't allow client side arrays, only vertex buffers, therefore the pointer field is always interpreted as an offset.
In OpenGL pointer is a pointer to user memory if the buffer field for that attribute is 0 (set indirectly, see above). It's an offset into a buffer if the buffer for an attribute is non-zero.
One thing NOT stored in an VAO are the attribute's constant values when it's disabled. If an attribute is disabled (they default to disabled, or you call gl.disableVertexAttribArray) then that attribute will get a constant value. The constant value can be set with glVertexAttrib???. These values are global. They are not part of VAO state.