我试图在opengl中最有效地移动对象(通常)和线条(特别是),因此我正在编写一个应用程序,其中多个线段以从右到左的恒定速度行进.在每个时间点,最左边的点将被移除,整条线将向左移动,并且在该线的最右侧将添加一个新点(这个新数据点在流动时被流式传输/接收/计算,每隔10ms左右).为了说明我的意思,请看这个图片:
因为我想使用许多对象,所以我决定使用顶点缓冲区对象以最小化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)
Run Code Online (Sandbox Code Playgroud)
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)
Run Code Online (Sandbox Code Playgroud)
会在哪里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
Run Code Online (Sandbox Code Playgroud)
这样可以正常工作,但是如果我让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 - >无穷大的值"这样的建议在这里都没有帮助(我也可以只绘制在我家门前测量的当前温度).
EDIT2 考虑这个玩具示例,在每个时间步之后,第一个点被移除,并且在结束时添加一个新点:
t = 0
*
* * *
* **** *
1234567890
Run Code Online (Sandbox Code Playgroud)
t = 1
*
* * * *
**** *
2345678901
Run Code Online (Sandbox Code Playgroud)
t = 2
* *
* * *
**** *
3456789012
Run Code Online (Sandbox Code Playgroud)
我不认为我可以在这里使用着色器,是吗?
编辑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
Run Code Online (Sandbox Code Playgroud)
EDIT5生成的解决方案显然必须在屏幕上呈现一条线 - 并且没有两条线缺少连接.Tim的循环缓冲解决方案提供了如何移动绘图的解决方案,但我最终得到两行,而不是一行.
以下是我对修订后问题的看法:
1)我应该使用glMapBuffer绑定GPU上的缓冲区并直接填充数据(而不是使用glBufferSubData)?或者这对性能没有区别吗?
我不知道两者之间有任何显着的性能,但我可能更喜欢glBufferSubData.
在我的情况下,我可能建议创建一个带有N个浮点数的VBO,然后使用它类似于循环缓冲区.将索引本地保存到缓冲区的"结束"所在的位置,然后每次更新都将"end"下的值替换为新值,并递增指针.这样,您只需每个周期更新一个浮点数.
完成后,您可以使用2x翻译和2x glDrawArrays/Elements绘制此缓冲区:
假设您有一个包含10个元素的数组,缓冲区结束指针位于元素4.您的数组将包含以下10个值,其中x是常量值,f(n- d)是来自的随机样本d周期前:
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) )
Run Code Online (Sandbox Code Playgroud)
要绘制这个(伪猜测代码,可能不完全正确):
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
Run Code Online (Sandbox Code Playgroud)
然后在下一个循环中,用新的随机值替换最旧的值,并向前移动循环缓冲区指针.
2)我应该使用着色器移动对象(这里是条带)而不是调用glTranslatef?如果是这样,这样的着色器怎么样?(我怀疑着色器是错误的方法,因为我的线条不是句点功能,而是包含随机数据).
如果您使用我在#1中描述的方法,可能是可选的.在这里使用一个没有特别的优势.
3)如果窗口调整大小会发生什么?如何相应地保持纵横比和缩放顶点?glViewport()仅帮助在y方向上缩放,而不是在x方向上缩放.如果窗口在x方向上重新缩放,那么在我当前的实现中,我将不得不重新计算整个线条的位置(调用my_func来获取新的x坐标)并将其上传到GPU.我想这可以做得更优雅吗?我该怎么办?
您不必重新计算任何数据.只需在一些对您有意义的固定坐标系中定义所有数据,然后使用投影矩阵将此范围映射到窗口.没有更多细节,很难回答.
4)我注意到当我使用具有非整数值的glTranslatef时,如果线条由数千个点组成,则屏幕开始闪烁.这很可能是因为我用来计算线条的精细分辨率与屏幕的像素分辨率不匹配,因此有时候某些点出现在前面,有时会出现在其他点之后(当你不渲染时,这尤其令人讨厌正弦波,但一些'随机'数据).如何防止这种情况发生(除了翻译1像素的整数倍的明显解决方案)?如果一个窗口从原来的800x800像素到100x100像素重新调整尺寸并且我仍然想要显示20秒的线条,那么在x方向上的移位必须以子像素精度以某种方式无闪烁,对吗?
你的假设似乎是对的.我认为在这里要做的事情要么是启用某种抗锯齿(你可以阅读其他帖子以了解如何做到这一点),或者使线条更宽.
归档时间: |
|
查看次数: |
4318 次 |
最近记录: |