jaki jest najbardziej wydajny sposób przenoszenia wielu obiektów (przechowywanych w VBO) w kosmosie? powinienem używać glTranslatef lub shadera?

Próbuję uzyskać zrozumienie przenoszenia obiektów (ogólnie) i pasków linii (w szczególności) najbardziej efektywnie w opengl i dlatego piszę aplikację, w której wiele segmentów linii porusza się ze stałą prędkością od prawej do lewej. W każdym punkcie czasowym lewy punkt zostanie usunięty, cała linia zostanie przesunięta w lewo, a nowy punkt zostanie dodany po prawej stronie linii (ten nowy punkt danych jest przesyłany strumieniowo / odbierany / obliczany w locie , co 10 ms). Aby zilustrować, co mam na myśli, zobacz ten obraz:

Ponieważ chcę pracować z wieloma obiektami, postanowiłem użyć obiektów bufora wierzchołków w celu zminimalizowania ilościgl* połączenia. Mój obecny kod wygląda mniej więcej tak:

A) ustaw początkowe wierzchołki:

# 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) aktualizuj wierzchołki:

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)

gdziemy_func zrobiłby coś takiego:

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

Działa to dobrze, jednak jeśli powiem, że 20 z tych pasków linii obejmuje cały ekran, to rzeczy znacznie zwalniają.Dlatego moje pytania:

1) Czy powinienem użyć glMapBuffer, aby związać bufor na GPU i wypełnić dane bezpośrednio (zamiast używać glBufferSubData)? A może nie będzie to miało żadnego wpływu na wydajność?

2) Czy powinienem używać shadera do przenoszenia obiektów (tutaj pasek linii) zamiast wywoływać glTranslatef? Jeśli tak, jak wyglądałby taki shader? (Podejrzewam, że shader jest niewłaściwym sposobem, ponieważ mój pasek linii NIE jest funkcją okresu, ale zawiera dane losowe).

3) Co się stanie, jeśli rozmiar okna zostanie zmieniony? jak mam odpowiednio zachować proporcje i skalować wierzchołki? glViewport () pomaga tylko skalować w kierunku y, a nie w kierunku x. Jeśli okno zostanie przeskalowane w kierunku x, w mojej bieżącej implementacji musiałbym przeliczyć pozycję całego paska linii (wywołaniemy_func aby uzyskać nowe współrzędne x) i przesłać je do GPU. Myślę, że można to zrobić bardziej elegancko? Jak bym to zrobił?

4) Zauważyłem to, gdy używamglTranslatef przy wartości nie integralnej ekran zaczyna migotać, jeśli pasek linii składa się z tysięcy punktów. Jest to najprawdopodobniej dlatego, że dokładna rozdzielczość, której używam do obliczania paska linii, nie odpowiada rozdzielczości pikseli ekranu, a zatem czasami niektóre punkty pojawiają się z przodu, a czasami za innymi punktami (jest to szczególnie denerwujące, gdy nie renderujesz sinusoida, ale niektóre „losowe” dane). Jak mogę temu zapobiec (oprócz oczywistego rozwiązania polegającego na tłumaczeniu przez całkowitą wielokrotność 1 piksela)? Jeśli okno zmieni rozmiar z powiedzmy pierwotnie na 800x800 pikseli na 100x100 pikseli i nadal chcę wizualizować pasek linii o długości 20 sekund, to przesunięcie w kierunku x musi jakoś działać bez migotania z precyzją subpikseli, prawda?

5) jak widać zawsze dzwonięglTranslatef(-local_shift, 0.0, 0.0) - bez robienia czegoś odwrotnego. Dlatego cały czas przesuwam widok w prawo. I dlatego muszę śledzić bezwzględną pozycję x (aby umieścić nowe dane we właściwym miejscu). Ten problem ostatecznie doprowadzi do artefaktu, w którym linia nakłada się na krawędzie okna. Myślę, że musi być lepszy sposób na zrobienie tego, prawda? Tak jak ustalanie wartości x i przenoszenie i aktualizowanie wartości y?

EDYTOWAĆ Usunąłem przykład sinusoidy i zastąpiłem go lepszym przykładem. Moje pytanie dotyczy ogólnie tego, jak najbardziej efektywnie przenosić paski linii w przestrzeni (dodając do nich nowe wartości). Dlatego żadne sugestie, takie jak „prekomputuj wartości dla t -> nieskończoności”, nie pomagają tutaj (mógłbym też rysować bieżącą temperaturę mierzoną przed moim domem).

EDIT2 Rozważmy ten zabawkowy przykład, w którym po każdym kroku czasowym pierwszy punkt jest usuwany i nowy jest dodawany do końca:

t = 0

   * 
  * *    *
 *   **** *

 1234567890

t = 1

  * 
 * *    * *
    **** *

 2345678901

t = 2

 *        * 
  *    * *
   **** *

 3456789012

Nie sądzę, żebym mógł użyć shadera tutaj, prawda?

EDYCJA 3: przykład z dwoma paskami linii.

EDYTUJ 4: na podstawie odpowiedzi Tima używam teraz następującego kodu, który działa dobrze,ale dzieli linię na dwie części (ponieważ mam dwa połączeniaglDrawArrays), zobacz także dwa poniższe zrzuty ekranu.

# 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 wynikowe rozwiązanie musi oczywiście wyświetlać jedną linię na ekranie - i nie ma dwóch linii, w których brakuje połączenia. Okrągły roztwór buforowy Tim zapewnia rozwiązanie dotyczące sposobu przenoszenia wykresu, ale kończę na dwóch liniach zamiast jednej.

questionAnswers(3)

yourAnswerToTheQuestion