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
Related
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.
so I have this class:
class BRENDERDLL_API Vertex
{
private:
glm::vec3 pos;
public:
Vertex(glm::vec3 pos);
~Vertex();
void setPos(glm::vec3 pos);
glm::vec3 getPos();
};
I would want to change:
glm::vec3 pos;
to:
glm::vec3* pos;
Then I need to change:
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
To point somehow where I storage data. There is obviously no problem with first one case, but what to do with second version?
Can I even do that or should I copy data to some array before?
Changing this to glm::vec3* will not benefit you at all, especially in modern GL. What you need to do is create an array of instances of Vertex.
You have to store this stuff in a Vertex Buffer and that means it needs to be laid out "flatly" in memory (e.g. no pointer dereferencing). If you have a contiguous array of Vertex, then you can take the address of the first instance and use glBufferData (...) to upload it to GL-managed memory.
The following code illustrates this:
std::vector <Vertex> verts;
const size_t size = verts.size () * sizeof (Vertex);
// Generate, allocate and upload your array to an OpenGL Buffer Object
GLuint vbo;
glGenBuffers (1, &vbo);
glBindBuffer (GL_ARRAY_BUFFER, vbo);
glBufferData (GL_ARRAY_BUFFER, size, &verts [0], GL_STATIC_DRAW);
// This points to address **0** relative to the buffer currently bound to
// GL_ARRAY_BUFFER.
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, 0);
I have a udp client for reading points from a server that generates points in 4 msec and each packet have near 3000 points. i read this points and map them to a matrix with 1 million elements of coordinates. (i create a hash table from streamed points with fixed keys. i mean you can think of a 1000 keys and each key has maximum 1000 points but its varying. after completing this table any new value i get from server, map to my keys in a sorted manner, i mean, i updated points in smaller keys first and ... :D
i really shamed i cant do this, my LCD with 1990x1080 pixels do this so faster than me!! :-((
i want to insert this to a VBO and then upgrading each key parts in 30 ms.
so i want to create 1000 vbos for drawing and 1000 vbos for buffering and after each 33 ms iteration i change 50 vbos of drawing ones with buffering ones. each vbo has maximum 0f 1000 points and colors, and number of points in one VBO change from 10 to 1000;
my problem start here i don't understand how VBO's work and best solution for implementation of this problem? :D
1- my first question: why i create a VAO and how its associated to new gen buffers?
its look like its associated to exactly next glGenBuffer() right? but in any sample i read i don't see any using of this pointer (just use for draw)? this is my code for creating a vbo i downloaded from somewhere
glGenVertexArrays(1, &vertexArrayObject);
glBindVertexArray(vertexArrayObject);
// First see if the vertex array buffer has been created...
if(uiVertexArray == 0) { // Nope, we need to create it
glGenBuffers(1, &uiVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, uiVertexArray);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * nNumVerts, NULL, GL_DYNAMIC_DRAW);
}
// Now see if it's already mapped, if not, map it
if(pVerts == NULL) {
glBindBuffer(GL_ARRAY_BUFFER, uiVertexArray);
pVerts = (M3DVector3f *)glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY);
}
// Ignore if we go past the end, keeps things from blowing up
if(nVertsBuilding >= nNumVerts)
return;
// Copy it in...
pVerts[nVertsBuilding][0] = x;
pVerts[nVertsBuilding][1] = y;
pVerts[nVertsBuilding][2] = z;
nVertsBuilding++;
for color and texture mapping its look like. which has better performance (GL_DYNAMIC_DRAW, GL_STREAM_DRAW)
2- this is a draw function? why use last line? for creating the next VBO and VAO?
// Set up the vertex array object
glBindVertexArray(vertexArrayObject);
glDrawArrays(primitiveType, 0, nNumVerts);
glBindVertexArray(0);
3- in the next time i want change a buffered VBO contents with new points i must do this all again for different number of points and if i do this i must delete last buffer of VBO or not?
// Vertex buffer objects
if(uiVertexArray != 0)
glDeleteBuffers(1, &uiVertexArray);
4- can i resize my VBOs and don't use recreate buffer VBO's?
5- How to copy data from buffer VBO's to draw VBO's?
this is my swap buffers with draw VBO's in main draw function, i recreate VBO data from first in swap function
for(int i=0; i<1000; i++)
if(must_show_buffer(i))
{
bufferVBO[i].draw();
drawedVBO[i].swap(bufferVBO[i]);
}
else drawedVBO[i].draw();
UPDATE:
i use this functions for swap data:
if(batch->cVerts != NULL)
{
CopyVertexData3f(batch->cVerts);
CopyColorData4f(batch->cColors);
}
End();
void GLBatch::CopyColorData4f(M3DVector4f *vColors)
{
// First time, create the buffer object, allocate the space
if(uiColorArray == 0) {
glGenBuffers(1, &uiColorArray);
glBindBuffer(GL_ARRAY_BUFFER, uiColorArray);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * nNumVerts, vColors, GL_DYNAMIC_DRAW);
}
else { // Just bind to existing object
glBindBuffer(GL_ARRAY_BUFFER, uiColorArray);
// Copy the data in
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(GLfloat) * 4 * nNumVerts, vColors);
pColors = NULL;
}
}
this approach is work correct but glBufferSubData increase memory usage so fast and i get a memory out exception
and this approach
if(batch->uiVertexArray != NULL)
{
// First time, create the buffer object, allocate the space
if(uiVertexArray == 0) {
glGenBuffers(1, &uiVertexArray);
glBindBuffer(GL_ARRAY_BUFFER, uiVertexArray);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * nNumVerts, NULL, GL_DYNAMIC_DRAW);
}
// Fast copy data
glBindBuffer(GL_COPY_READ_BUFFER, batch->uiVertexArray);
glBindBuffer(GL_COPY_WRITE_BUFFER, uiVertexArray);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nNumVerts);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
//copy color data
if(uiColorArray == 0) {
glGenBuffers(1, &uiColorArray);
glBindBuffer(GL_ARRAY_BUFFER, uiColorArray);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 4 * nNumVerts, NULL, GL_DYNAMIC_DRAW);
}
glBindBuffer(GL_COPY_READ_BUFFER, batch->uiColorArray);
glBindBuffer(GL_COPY_WRITE_BUFFER, uiColorArray);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, nNumVerts);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
}
without memory allocation problem but doesn't clear before points
what i miss in each implementation?
VBOs are associated to the currently bound VAO during glVertexAttribPointer
unbinding buffers is a good practice, remember that bind state is global and the less global state you rely on the better
no need to delete the VBO you can just skip the glGenBuffers(1, &uiVertexArray); call
yes a new call to glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * nNumVerts, NULL, GL_DYNAMIC_DRAW); with the new nNumVerts will resize the buffer (and thrash any existing data)
you can do the following:
glBindBuffer(GL_COPY_READ_BUFFER, bufferVBO);
glBindBuffer(GL_COPY_WRITE_BUFFER, drawVBO);
glCopyBufferSubData(GL_COPY_READ_BUFFER, GL_COPY_WRITE_BUFFER, 0, 0, size);
glBindBuffer(GL_COPY_READ_BUFFER, 0);
glBindBuffer(GL_COPY_WRITE_BUFFER, 0);
I have written out 700k points/s to the screen no problem simply by uploading them all into a single VBO and drawing that.
I've hopefully understood the following correct:
When making different VBO:s in OpenGL for vertices, normals and indices I can use less memory because of reusing but it isn't as effective.
When using interleaved VBO:s the normal routine is that the same vertices and normals will be written more than once, right?
My question is if the use of more memory is something people just accept for the gain in speed, or is it worth it to do some kind of trick to "reuse" already given data with indices or something similar?
interleaved VBO holds essentially a array of structs:
struct vertexAttr{
GLfloat posX, posY, posZ;
GLfloat normX, normY, normZ;
}
glBindBuffer(GL_ARRAY_BUFFER, vert);
vertexAttr* verts = new vertexAttr[numVerts];
//fill verts
glBuffer(GL_ARRAY_BUFFER, numVerts, verts, GL_STATIC_DRAW);
delete[] verts;
glBindProgram(prog);
glVertexAttribPointer(posAttr, 3, GL_FLOAT, false, sizeof(vertexAttr), 0);
glVertexAttribPointer(normAttr, 3, GL_FLOAT, false, sizeof(vertexAttr), offsetof(vertexAttr, normX));
you still need to use a separate buffer for the indexes.