what is the most efficient way of moving multiple objects (stored in VBO) in space? should I use glTranslatef or a shader?

opengl, vbo

Solution

Here's my thoughts to the revised question:

1) should I use glMapBuffer to bind the buffer on the GPU and fill the data directly (instead of using glBufferSubData)? Or will this make no difference performance wise?

I'm not aware that there is any significant performance between the two, though I would probably prefer glBufferSubData.

What I might suggest in your case is to create a VBO with N floats, and then use it similar to a circular buffer. Keep an index locally to where the 'end' of the buffer is, then every update replace the value under 'end' with the new value, and increment the pointer. This way you only have to update a single float each cycle.

Having done that, you can draw this buffer using 2x translates and 2x glDrawArrays/Elements:

Imagine that you've got an array of 10 elements, and the buffer end pointer is at element 4. Your array will contain the following 10 values, where x is a constant value, and f(n-d) is the random sample from d cycles ago:

0: (0, f(n-4) )
1: (1, f(n-3) )
2: (2, f(n-2) )
3: (3, f(n-1) )  
4: (4, f(n)   )  <-- end of buffer 
5: (5, f(n-9) )  <-- start of buffer
6: (6, f(n-8) )
7: (7, f(n-7) )
8: (8, f(n-6) )
9: (9, f(n-5) )

To draw this (pseudo-guess code, might not be exactly correct):

glTranslatef( -end, 0, 0);
glDrawArrays( LINE_STRIP, end+1, (10-end)); //draw elems 5-9 shifted left by 4
glPopMatrix();
glTranslatef( end+1, 0, 0);
glDrawArrays(LINE_STRIP, 0, end); // draw elems 0-4 shifted right by 5 

Then in the next cycle, replace the oldest value with the new random value,and shift the circular buffer pointer forward.

2) should I use a shader for moving objects (here line strip) instead of calling glTranslatef? If so, how would such a shader look like? (I suspect that a shader is the wrong way to go, since my line strip is NOT a period function but rather contains random data).

Probably optional, if you use the method that I've described in #1. There's not a particular advantage to using one here.

3) what happens if the window get's resized? how do I keep aspect ratio and scale vertices accordingly? glViewport() only helps scaling in y direction, not in x direction. If the window is rescaled in x-direction, then in my current implementation I would have to recalculate the position of the entire line strip (calling my_func to get the new x coordinates) and upload it to the GPU. I guess this could be done more elegantly? How would I do that?

You shouldn't have to recalculate any data. Just define all your data in some fixed coordinate system that makes sense to you, and then use projection matrix to map this range to the window. Without more specifics its hard to answer.

4) I noticed that when I use glTranslatef with a non integral value, the screen starts to flicker if the line strip consists of thousands of points. This is most probably because the fine resolution that I use to calculate the line strip does not match the pixel resolution of the screen and therefore sometimes some points appear in front and sometimes behind other points (this is particularly annoying when you don't render a sine wave but some 'random' data). How can I prevent this from happening (besides the obvious solution of translating by a integer multiple of 1 pixel)? If a window get re-sized from let's say originally 800x800 pixels to 100x100 pixels and I still want to visualize a line strip of 20 seconds, then shifting in x direction must work flicker free somehow with sub pixel precision, right?

Your assumption seems correct. I think the thing to do here would either to enable some kind of antialiasing (you can read other posts for how to do that), or make the lines wider.

Problem

I'm trying to get the hang of moving objects (in general) and line strips (in particular) most efficiently in opengl and therefore I'm writing an application where multiple line segments are traveling with a constant speed from right to left. At every time point the left most point will be removed, the entire line will be shifted to the left, and a new point will be added at the very right of the line (this new data point is streamed / received / calculated on the fly, every 10ms or so). To illustrate what I mean, see this image: Because I want to work with many objects, I decided to use vertex buffer objects in order to minimize the amount of `gl*` calls. My current code looks something like this: A) setup initial vertices: ``` # calculate my_func(x) in range [0, n] # (could also be random data) data = my_func(0, n) # create & bind buffer vbo_id = GLuint() glGenBuffers(1, vbo_id); glBindBuffer(GL_ARRAY_BUFFER, vbo_id) # allocate memory & transfer data to GPU glBufferData(GL_ARRAY_BUFFER, sizeof(data), data, GL_DYNAMIC_DRAW) ``` B) update vertices: ``` draw(): # get new data and update offset data = my_func(n+dx, n+2*dx) # update offset 'n' which is the current absolute value of x. n = n + 2*dx # upload data glBindBuffer(GL_ARRAY_BUFFER, vbo_id) glBufferSubData(GL_ARRAY_BUFFER, n, sizeof(data), data) # translate scene so it looks like line strip has moved to the left. glTranslatef(-local_shift, 0.0, 0.0) # draw all points from offset glVertexPointer(2, GL_FLOAT, 0, n) glDrawArrays(GL_LINE_STRIP, 0, points_per_vbo) ``` where `my_func` would do something like this: ``` my_func(start_x, end_x): # generate the correct x locations. x_values = range(start_x, end_x, STEP_SIZE) # generate the y values. We could be getting these values from a sensor. y_values = [] for j in x_values: y_values.append(random()) data = [] for i, j in zip(x_values, y_values): data.extend([i, j]) return data ``` This works just fine, however if I have let's say 20 of those line strips that span the entire screen, then things slow down considerably. Therefore my questions: 1) should I use glMapBuffer to bind the buffer on the GPU and fill the data directly (instead of using glBufferSubData)? Or will this make no difference performance wise? 2) should I use a shader for moving objects (here line strip) instead of calling glTranslatef? If so, how would such a shader look like? (I suspect that a shader is the wrong way to go, since my line strip is NOT a period function but rather contains random data). 3) what happens if the window get's resized? how do I keep aspect ratio and scale vertices accordingly? glViewport() only helps scaling in y direction, not in x direction. If the window is rescaled in x-direction, then in my current implementation I would have to recalculate the position of the entire line strip (calling `my_func` to get the new x coordinates) and upload it to the GPU. I guess this could be done more elegantly? How would I do that? 4) I noticed that when I use `glTranslatef` with a non integral value, the screen starts to flicker if the line strip consists of thousands of points. This is most probably because the fine resolution that I use to calculate the line strip does not match the pixel resolution of the screen and therefore sometimes some points appear in front and sometimes behind other points (this is particularly annoying when you don't render a sine wave but some 'random' data). How can I prevent this from happening (besides the obvious solution of translating by a integer multiple of 1 pixel)? If a window get re-sized from let's say originally 800x800 pixels to 100x100 pixels and I still want to visualize a line strip of 20 seconds, then shifting in x direction must work flicker free somehow with sub pixel precision, right? 5) as you can see I always call `glTranslatef(-local_shift, 0.0, 0.0)` - without ever doing the opposite. Therefore I keep shifting the entire view to the right. And that's why I need to keep track of the absolute x position (in order to place new data at the correct location). This problem will eventually lead to an artifact, where the line is overlapping with the edges of the window. I guess there must be a better way for doing this, right? Like keeping the x values fixed and just moving & updating the y values? EDIT I've removed the sine wave example and replaced it with a better example. My question is generally about how to move line strips in space most efficiently (while adding new values to them). Therefore any suggestions like "precompute the values for t -> infinity" don't help here (I could also just be drawing the current temperature measured in front of my house). EDIT2 Consider this toy example where after each time step, the first point is removed and a new one is added to the end: t = 0 ``` * * * * * **** * 1234567890 ``` t = 1 ``` * * * * * **** * 2345678901 ``` t = 2 ``` * * * * * **** * 3456789012 ``` I don't think I can use a shader here, can I? EDIT 3: example with two line strips. EDIT 4: based on Tim's answer I'm using now the following code, which works nicely, but breaks the line into two (since I have two calls of `glDrawArrays`), see also the following two screenshots. ``` # calculate the difference diff_first = x[1] - x[0] ''' first part of the line ''' # push the matrix glPushMatrix() move_to = -(diff_first * c) print 'going to %d ' % (move_to) glTranslatef(move_to, 0, 0) # format of glVertexPointer: nbr points per vertex, data type, stride, byte offset # calculate the offset into the Vertex offset_bytes = c * BYTES_PER_POINT stride = 0 glVertexPointer(2, GL_FLOAT, stride, offset_bytes) # format of glDrawArrays: mode, Specifies the starting index in the enabled arrays, nbr of points nbr_points_to_render = (nbr_points - c) starting_point_in_above_selected_Vertex = 0 glDrawArrays(GL_POINTS, starting_point_in_above_selected_Vertex, nbr_points_to_render) # pop the matrix glPopMatrix() ''' second part of the line ''' # push the matrix glPushMatrix() move_to = (nbr_points - c) * diff_first print 'moving to %d ' %(move_to) glTranslatef(move_to, 0, 0) # select the vertex offset_bytes = 0 stride = 0 glVertexPointer(2, GL_FLOAT, stride, offset_bytes) # draw the line nbr_points_to_render = c starting_point_in_above_selected_Vertex = 0 glDrawArrays(GL_POINTS, starting_point_in_above_selected_Vertex, nbr_points_to_render) # pop the matrix glPopMatrix() # update counter c += 1 if c == nbr_points: c = 0 ``` EDIT5 the resulting solution must obviously render one line across the screen - and no two lines that are missing a connection. The circular buffer solution by Tim provides a solution on how to move the plot, but I end up with two lines, instead of one.

Original source

Related problems