Я пытаюсь освоить движущиеся объекты (вообще )и линейные полосы (в частности )наиболее эффективно в 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 для привязки буфера к графическому процессору и непосредственного заполнения данных (вместо использования glBufferSubData )? Или это не повлияет на производительность?
2 )Должен ли я использовать шейдер для перемещения объектов (здесь линейная полоса )вместо вызова glTranslatef? Если да, то как будет выглядеть такой шейдер? (Я подозреваю, что шейдер — это неправильный путь, так как моя линейная полоса НЕ является функцией периода, а скорее содержит случайные данные ).
3 )что произойдет, если размер окна изменится?как мне сохранить соотношение сторон и соответственно масштабировать вершины? glViewport ()помогает масштабировать только в направлении Y, но не в направлении X. Если окно масштабируется в направлении x -, то в моей текущей реализации мне пришлось бы пересчитывать положение всей линейной полосы (, вызывая my_func
, чтобы получить новые координаты x )и загружать их в ГП. Я думаю, это можно было бы сделать более элегантно? Как бы я это сделал?
4 )Я заметил, что когда я использую glTranslatef
с нецелым значением, экран начинает мерцать, если полоса линии состоит из тысяч точек. Скорее всего, это связано с тем, что точное разрешение, которое я использую для расчета линейной полосы, не соответствует разрешению экрана в пикселях, и поэтому иногда некоторые точки появляются впереди, а иногда позади других точек (это особенно раздражает, когда вы не визуализировать синусоиду, но некоторые «случайные» данные ). Как я могу предотвратить это (, кроме очевидного решения перевода на целое число, кратное 1 пикселю )? Если размер окна изменяется -с, скажем, первоначальных 800x800 пикселей до 100x100 пикселей, и я все еще хочу визуализировать линейную полосу продолжительностью 20 секунд, тогда смещение в направлении x должно каким-то образом работать без мерцания с субпиксельной точностью, верно?
5 )как видите, я всегда коллируюglTranslatef(-local_shift, 0.0, 0.0)
-и никогда не делаю обратного. Поэтому я продолжаю смещать весь вид вправо. И именно поэтому мне нужно отслеживать абсолютную позицию x (, чтобы поместить новые данные в правильное место ). Эта проблема в конечном итоге приведет к артефакту, когда линия перекрывается с краями окна. Я думаю, должен быть лучший способ сделать это, верно? Как сохранить фиксированные значения x и просто переместить и обновить значения y?
РЕДАКТИРОВАТЬ Я удалил пример с синусоидой и заменил его лучшим примером.Мой вопрос обычно о том, как наиболее эффективно перемещать линейные полосы в пространстве (при добавлении к ним новых значений ). Поэтому любые предложения типа «предварительно вычислить значения для t -> бесконечность» здесь не помогут (. Я мог бы также просто нарисовать текущую температуру, измеренную перед моим домом ).
РЕДАКТИРОВАТЬ2 Рассмотрим этот игрушечный пример, где после каждого временного шага первая точка удаляется и в конец добавляется новая:
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
РЕДАКТИРОВАТЬ5 Полученное решение, очевидно, должно отображать одну строку на экране -и не должно быть двух строк, в которых отсутствует связь. Циклический буфер от Тима дает решение о том, как перемещать график, но я получаю две строки вместо одной.