PDA

View Full Version : Rebuffering a color array object?



Robbie
11-28-2011, 01:08 PM
Hi all, I got some great help before on how to rebuffer my modified vertex and index array's. I decided to add in a color array object, and it seems to work the first time, but I'm unable to rebuffer the color data the same way as before. Here's my new initial buffer and rebuffer code:



void Mazegen::bindBuffers()
{
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

glGenBuffers(MAX_BUFFERS, &m_vbos[0]);
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]); //Bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_vertices.size(), &m_vertices[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX_BUFFER]); //Bind the index buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indices.size(), &m_indices[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_colors.size(), &m_colors[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

//Bind the color array, and set the color pointer to point at it
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]);
glColorPointer(3, GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]);
glVertexPointer(3, GL_FLOAT, 0, 0);

}
void Mazegen::rebufferData()
{
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_vertices.size(), &m_vertices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indices.size(), &m_indices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL
}


I call rebufferData after clearing m_vertices, m_indices, and m_colors and repopulating them with new scene data.

I tried modifying rebufferData as follows, but ended up with a blank screen after making the call to it.



void Mazegen::rebufferData()
{
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_vertices.size(), &m_vertices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indices.size(), &m_indices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_colors.size(), &m_colors[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL
}


Is there a better or more efficient way to do this? I intend to optimize my code so that the vector arrays for color, index, and vertex get modified instead of re-created each time, but thats a little down the road.

thokra
11-28-2011, 01:20 PM
First of all, if you're updating a buffer, you need to make sure which buffer you're updating.

OpenGL maintains one bound buffer object per target (GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, ... ) at a time. So, if you bind your vertex buffer, update the positions and afterwards update the same buffer with colors, then the positions will be overwritten.

You need to resize the buffer object to hold BOTH positions and colors. Then you can either combine positions and colors into one big array and use glBufferData to upload the whole array, or you can use separate arrays and use glBufferSubData.

tksuoran
11-28-2011, 01:21 PM
In rebufferData() you should bind the correct buffers before you call glBufferData().

Your bindBuffers() function not only does bind, it also generates new buffer objects. This can cause some confusion, it would perhaps be better not to recreate buffers if they already exist.

Robbie
11-28-2011, 01:31 PM
I think I'm tracking you...I'm only calling bindbuffers once per program run...so yeah, I guess I could separate the GenBuffers from it, or name the function something else to avoid confusion.

I have m_vbos[VERTEX_BUFFER], m_vbos[INDEX_BUFFER], m_vbos[COLOR_BUFFER] named under the bindbuffer commands...when I'm rebuffering do I need to add the bind calls to that function?

Edit: So basically, rebufferdata needs to be exactly like bindbuffers except I don't need the glGenBuffers command? Or is there more I can chop out from the rebuffer function? I set it up so it has everything that bindbuffers has except the genbuffer command, and it seems to work, but I don't want to have any memory leaks...is this correct?

tksuoran
11-28-2011, 01:54 PM
rebuffer does not need to enable/disable client states, and it does not need to set pointers. It only needs to bind buffer and set buffer data.

thokra
11-28-2011, 01:57 PM
glGenBuffers just creates a name, a unique handle by which to identify a buffer object. This name can be reused as long as needed so you don't have to generate a new handle over and over again.

Furthermore, 'rebuffer' isn't the correct term as it suggests that you upload data to video memory that has been previously deleted. In fact, you upload data to your buffer objects every time you call bindBuffers() AND rebufferData(). This is completely unecessary.

The idea is as follows:

At initilization:
a) Generate buffer object
b) Bind buffer object
c) Upload data

At render time:
a) Bind buffer object
b) Set pointers
b) Render

Before shutdown:
a) Delete buffer object

Robbie
11-28-2011, 02:01 PM
Sweet, that works! Thanks much!

Now a performance question. Since I'm working in a Grid...I want to switch to using solids. Instead of using lines, I'd like to use triangle strips for the walls, this will open me up for switching to 3d soon.

In order to plot triangle strips, I will need more vertices, and of course more indices.

Currently, I loop through and place a vertex to represent each possible line. A grid so to speak...then I place another vertex in the middle of each cell to represent the lines drawn as the solve path. Is there any kind of performance hit or problem with creating vertices that don't get utilized by elements? For example, in my current maze, an unsolved maze has one vertex in the middle of each cell that doesn't have any lines being drawn from it. is that a problem? Will that be a problem when I start using triangle strips?

Robbie
11-28-2011, 02:04 PM
Thokra. The problem with the layout you mentioned is that in some cases between initialization and render time, the data completely changes. the at render time needs to upload (or modify) data to the buffer, then bind it and set pointers, and render, etc...

Right?

thokra
11-28-2011, 02:32 PM
If the data really changes then yes. In this case you'll have to update the buffers. Still, of you maintain separate buffers for different kinds of data, you'll need to

a) bind buffer A
b) upload data for buffer A
c) bind buffer B
d) upload data for buffer B

ans so on. Setting pointers is still only necessary when rendering. Not when uploading. And generating buffer object names is only necessary if you need a new buffer or have previously deleted a buffer.

You might also have a look at vertex array objects which allow to save the vertex attrib pointer state so you don't have to set the pointers everytime you render.

Robbie
11-30-2011, 09:32 AM
Sorry to bring this thread back up. I've figured out that the projection problem on my wife's laptop is probably related to the reuploading of data to the buffer.

Here's the thread I started on that if you're interested: http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&Number=306630#Post3066 30

But here's the screenshots of what's going on.
http://imgur.com/a/KpANE

I've narrowed it down to the problem in this thread because the scrambling never happens on the first maze, only on the second and subsequent ones.

So, what I'm doing is this:

every frame, I do a render


void Mazegen::render()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//Load the identity matrix (reset to the default position and orientation)
glLoadIdentity();
updateProjection();
//glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);

renderMaze();
}
void Mazegen::renderMaze()
{
// Draw Walls and Paths
glDrawElements(GL_LINES, m_indices.size(), GL_UNSIGNED_INT, 0);

// Draw Player
glPointSize(4.0f);
if (!screenSaver) {
glDrawArrays(GL_POINTS, CurrCellVert, 1);
}

// Draw Start and End Cells
glPointSize(8.0f);
glDrawArrays(GL_POINTS, StartVert, 1);
glDrawArrays(GL_POINTS, EndVert, 1);
}
void Mazegen::updateProjection()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();

float screenAspect, mazeAspect, left, right, bottom, top;

screenAspect = (float) screenWidth / screenHeight;
mazeAspect = (float) (giCols+1) / (giRows+1);

left = 0;
right = 0;
bottom = 0;
top = 0;

if (screenAspect < 1.0) { //portrait
if (mazeAspect > screenAspect) { // tall maze
left = -1;
right = giCols+1;
bottom -= fabs((((((float) screenHeight * (giCols + 2)) / screenWidth) - giRows) / 2));
top = giRows + fabs((((((float) screenHeight * (giCols + 2)) / screenWidth) - giRows) / 2));
} else { // wide maze
left -= fabs((((((float) screenWidth * (giRows + 2)) / screenHeight) - giCols) / 2));
right = giCols + fabs((((((float) screenWidth * (giRows + 2)) / screenHeight) - giCols) / 2));
bottom = -1.0;
top = (giRows + 1);
}
} else { // landscape
if (mazeAspect < screenAspect) { // tall maze
left -= fabs((((((float) screenWidth * (giRows + 2)) / screenHeight) - giCols) / 2));
right = giCols + fabs((((((float) screenWidth * (giRows + 2)) / screenHeight) - giCols) / 2));
bottom = -1.0;
top = (giRows + 1);
} else { // wide maze
left = -1;
right = giCols+1;
bottom -= fabs((((((float) screenHeight * (giCols + 2)) / screenWidth) - giRows) / 2));
top = giRows + fabs((((((float) screenHeight * (giCols + 2)) / screenWidth) - giRows) / 2));
}
}

glOrtho(left, right, top, bottom, 1.0, 100.0);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}


All of that is code that runs every frame when nothing has changed in my scene.

When I change my scene (And the first time the program runs) I have a function called initializeBuffers that basically does the following:



void Mazegen::initializeBuffers()
{
m_vertices.clear();
m_indices.clear();
m_colors.clear();

//A whole lot of vertex, index, and color push_back's occur here
}


And then the first time my program runs AFTER going through the initializeBuffers above, I do this:



void Mazegen::genBuffers()
{
glEnableClientState(GL_COLOR_ARRAY);
glEnableClientState(GL_VERTEX_ARRAY);

glGenBuffers(MAX_BUFFERS, &amp;m_vbos[0]);
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]); //Bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_vertices.size(), &amp;m_vertices[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX_BUFFER]); //Bind the index buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indices.size(), &amp;m_indices[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]);
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_colors.size(), &amp;m_colors[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL

//Bind the color array, and set the color pointer to point at it
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]);
glColorPointer(3, GL_FLOAT, 0, 0);

glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]);
glVertexPointer(3, GL_FLOAT, 0, 0);
}


Which sets up my VBO's for vertex, index, and color.

After the discussions we've had in this thread...If I need to change my scene, I do that, and then I run initializeBuffers above to populate the m_vertices, m_indices, and m_colors with new updated data, and instead of running the genBuffers function, I instead call the following:



void Mazegen::rebufferData()
{
glBindBuffer(GL_ARRAY_BUFFER, m_vbos[VERTEX_BUFFER]); //Bind the vertex buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_vertices.size(), &amp;m_vertices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_vbos[INDEX_BUFFER]); //Bind the index buffer
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(GLuint) * m_indices.size(), &amp;m_indices[0], GL_DYNAMIC_DRAW); //Send the new data to OpenGL

glBindBuffer(GL_ARRAY_BUFFER, m_vbos[COLOR_BUFFER]); //Bind the color buffer
glBufferData(GL_ARRAY_BUFFER, sizeof(GLfloat) * 3 * m_colors.size(), &amp;m_colors[0], GL_DYNAMIC_DRAW); //Send the data to OpenGL
}


and after that I go back to my render loop until it changes again.

This method is working apparently just fine on all of the workstations here at work. But my wife's laptop isn't handling the rebuffering so well. I must be missing something or doing something wrong. Any ideas given the code you see above?

rebufferData is the code that I need to run to replace the contents of the buffers with the now modified m_vertices, m_indices, and m_colors. Do I even need to be doing that or does the buffer's already keep track of what's in those vector arrays?

Robbie
11-30-2011, 11:07 AM
Ok, I tried getting rid of all calls to rebufferData, and the program seems to work (again, I can't test it on my wifes laptop til I get home).

Only problem is, there's some anomalies because the sizes of the arrays change each time I normally would call rebufferdata.

I'm looking into VAO's but not having much luck yet.