我试图在opengl中最有效地掌握移动物体(通常)和线条(特别是)的窍门,因此我正在编写一个应用程序,其中多个线段以恒定的速度从右到左行进。在每个时间点,最左边的点将被删除,整条线将向左移动,并在该线的最右边添加一个新点(这个新数据点是实时流式传输/接收/计算的,每 10 毫秒左右)。为了说明我的意思,请看这张图片:
因为我想处理许多对象,所以我决定使用顶点缓冲区对象以最大限度地减少gl*
调用量。我当前的代码如下所示:
A)设置初始顶点:
# 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)更新顶点:
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)
在哪里my_func
会做这样的事情:
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
这工作得很好,但是如果我假设有 20 个跨越整个屏幕的线条,那么事情就会大大减慢。 因此我的问题:
1)我应该使用glMapBuffer来绑定GPU上的缓冲区并直接填充数据(而不是使用glBufferSubData)?或者这对性能没有影响?
2)我应该使用着色器来移动对象(这里是线条)而不是调用 glTranslatef 吗?如果是这样,这样的着色器会是什么样子?(我怀疑着色器是错误的方法,因为我的线带不是周期函数,而是包含随机数据)。
3)如果调整窗口大小会发生什么?如何保持纵横比并相应地缩放顶点?glViewport() 仅有助于在 y 方向上缩放,而不是在 x 方向上。如果窗口在 x 方向上重新缩放,那么在我当前的实现中,我将不得不重新计算整个线条的位置(调用my_func
以获取新的 x 坐标)并将其上传到 GPU。我想这可以更优雅地完成吗?我该怎么做?
4)我注意到,当我使用glTranslatef
非整数值时,如果线条由数千个点组成,屏幕开始闪烁。这很可能是因为我用来计算线带的精细分辨率与屏幕的像素分辨率不匹配,因此有时一些点出现在前面,有时出现在其他点后面(当你不渲染时,这尤其烦人正弦波,但一些“随机”数据)。我怎样才能防止这种情况发生(除了平移 1 像素的整数倍的明显解决方案)?如果一个窗口的大小从最初的 800x800 像素重新调整为 100x100 像素,并且我仍然想可视化 20 秒的线条,那么在 x 方向上移动必须以某种方式以亚像素精度无闪烁,对吧?
5)如你所见,我总是打电话glTranslatef(-local_shift, 0.0, 0.0)
- 从来没有做相反的事情。因此,我不断将整个视图向右移动。这就是为什么我需要跟踪绝对 x 位置(以便将新数据放置在正确的位置)。这个问题最终会导致一个伪影,即线条与窗口的边缘重叠。我想一定有更好的方法来做到这一点,对吧?就像保持 x 值固定而只是移动和更新 y 值?
编辑我删除了正弦波示例并用更好的示例替换它。我的问题通常是关于如何最有效地在空间中移动线条(同时向它们添加新值)。因此,任何诸如“预先计算 t -> infinity 的值”之类的建议在这里都无济于事(我也可以只绘制在我家门前测量的当前温度)。
EDIT2 考虑这个玩具示例,在每个时间步之后,删除第一个点并在末尾添加一个新点:
t = 0
*
* * *
* **** *
1234567890
t = 1
*
* * * *
**** *
2345678901
t = 2
* *
* * *
**** *
3456789012
我不认为我可以在这里使用着色器,可以吗?
编辑 3:带有两条线带的示例。
编辑4:根据蒂姆的回答,我现在正在使用以下代码,它运行良好,但将行分成两部分(因为我有两次调用glDrawArrays
),另请参见以下两个屏幕截图。
# 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生成的解决方案显然必须在屏幕上呈现一行 - 并且没有两行缺少连接。Tim 的循环缓冲解决方案提供了如何移动绘图的解决方案,但我最终得到了两条线,而不是一条。