Sto cercando di ottenere il blocco degli oggetti in movimento (in generale) e delle strisce (in particolare) in modo più efficiente in opengl e quindi sto scrivendo un'applicazione in cui più segmenti di linea viaggiano con una velocità costante da destra a sinistra . In ogni momento il punto più a sinistra verrà rimosso, l'intera linea verrà spostata a sinistra e verrà aggiunto un nuovo punto all'estremità destra della linea (questo nuovo punto dati viene trasmesso/ricevuto/calcolato al volo ogni 10ms circa). Per illustrare quello che voglio dire, vedo questa immagine:qual è il modo più efficiente di spostare più oggetti (memorizzati in VBO) nello spazio? dovrei usare glTranslatef o uno shader?
Perché voglio lavorare con molti oggetti, ho deciso di usare oggetti vertex buffer per ridurre al minimo la quantità di gl*
chiamate. Il mio codice corrente simile a questa:
A) di impostazione vertici iniziali:
# 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) i vertici aggiornamento:
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)
dove my_func
sarebbe fare qualcosa di simile:
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
Questo funziona bene, tuttavia se ho diciamo 20 di quelle strisce di linea che si estendono l'intero schermo, quindi le cose rallentano notevolmente. Quindi le mie domande:
1) dovrei usare glMapBuffer per legare il buffer sulla GPU e riempire i dati direttamente (invece di utilizzare glBufferSubData)? O questo non cambierà le prestazioni?
2) dovrei usare uno shader per spostare oggetti (qui striscia di linea) invece di chiamare glTranslatef? Se sì, come sarebbe uno shader simile? (Sospetto che uno shader sia la strada sbagliata, dal momento che la mia striscia di linea NON è una funzione di periodo, ma piuttosto contiene dati casuali).
3) cosa succede se la finestra viene ridimensionata? come posso mantenere le proporzioni e scalare i vertici di conseguenza? glViewport() aiuta solo il ridimensionamento in direzione y, non in direzione x. Se la finestra viene ridimensionata in direzione x, nella mia attuale implementazione dovrei ricalcolare la posizione dell'intera striscia di linea (chiamando my_func
per ottenere le nuove coordinate x) e caricarla sulla GPU. Immagino che questo potrebbe essere fatto in modo più elegante? Come potrei farlo?
4) Ho notato che quando uso glTranslatef
con un valore non integrale, lo schermo inizia a sfarfallare se la striscia di linea è composta da migliaia di punti. Ciò è probabilmente dovuto al fatto che la risoluzione fine che utilizzo per calcolare la striscia di linea non corrisponde alla risoluzione in pixel dello schermo e quindi a volte alcuni punti appaiono davanti e talvolta dietro altri punti (questo è particolarmente fastidioso quando non si esegue il rendering di un onda sinusoidale ma alcuni dati "casuali"). Come posso evitare che ciò accada (oltre all'ovvia soluzione di tradurre da un multiplo intero di 1 pixel)? Se una finestra viene ridimensionata da diciamo originariamente da 800x800 pixel a 100x100 pixel e voglio ancora visualizzare una striscia di linea di 20 secondi, quindi lo spostamento in direzione x deve funzionare senza sfarfallio in qualche modo con precisione sub pixel, giusto?
5) come potete vedere, chiamo sempre glTranslatef(-local_shift, 0.0, 0.0)
- senza mai fare il contrario. Pertanto continuo a spostare l'intera vista a destra. Ed è per questo che ho bisogno di tenere traccia della posizione x assoluta (al fine di inserire nuovi dati nella posizione corretta).Questo problema alla fine porterà a un artefatto, in cui la linea si sovrappone ai bordi della finestra. Immagino che ci debba essere un modo migliore per farlo, giusto? Come mantenere i valori x fissi e semplicemente spostando & aggiornando i valori y?
EDIT Ho rimosso l'esempio di onda sinusoidale e l'ho sostituito con un esempio migliore. La mia domanda è in generale su come spostare le strisce di linea nello spazio nel modo più efficiente (aggiungendo loro nuovi valori). Quindi qualsiasi suggerimento come "precomputare i valori per t -> infinito" non aiuta qui (potrei anche solo disegnare la temperatura attuale misurata davanti a casa mia).
EDIT2 Consideriamo questo esempio giocattolo dove dopo ogni passo, il primo punto viene rimosso e uno nuovo viene aggiunto alla fine:
t = 0
*
* * *
* **** *
1234567890
t = 1
*
* * * *
**** *
2345678901
t = 2
* *
* * *
**** *
3456789012
Non penso di poter usare uno shader qui, no?
MODIFICA 3: esempio con due strisce di linea.
EDIT 4: in base alla risposta di Tim che sto usando ora il seguente codice, che funziona bene, ma spezza la linea in due (dal momento che ho due chiamate di glDrawArrays
), puoi anche consultare il seguente due screenshot.
# 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 la soluzione risultante deve ovviamente rendering una linea sullo schermo - e non esistono due linee che mancano una connessione. La soluzione di buffer circolare di Tim fornisce una soluzione su come spostare la trama, ma alla fine ho due righe, anziché una.
'glViewport' non riguarda le proporzioni. Piuttosto, le proporzioni vengono impostate utilizzando una matrice di proiezione corrispondente. Vedi [qui] (http://stackoverflow.com/questions/4338729/preserve-aspect-ratio-of-2d-object-on-window-resize) per un esempio. Non è necessario ricalcolare i dati a causa delle modifiche apportate alle finestre. –
"l'onda sinusoidale qui è solo un esempio" Quindi rendi più chiaro (e no, attaccare un commento nel mezzo del tuo post * dopo che l'esempio * non conta). La tua domanda * suona * come se tu parlassi di streaming di dati sui vertici, ma è molto confuso. Rivedi la domanda. –
@NicolBolas grazie per il vostro feedback! Ho provato a chiarire la prima parte della mia domanda. Non so che cosa significhi "dati dei vertici in streaming", ma forse puoi aiutarmi a capirlo. – memyself