2012-10-25 4 views
5

Quindi sto lavorando con un sistema di animazione scheletrica 2D.Ottimizzazione dei vertici per l'animazione scheletrica in OpenGL ES

Ci sono il numero X di ossa, ogni osso ha almeno 1 parte (un quadruplo, due triangoli). In media, ho forse 20 ossa e 30 parti. La maggior parte delle ossa dipende da un genitore, le ossa si sposteranno su ogni fotogramma. Ci sono fino a 1000 frame in totale per animazione e sto usando circa 50 animazioni. Un totale di circa 50.000 frame caricati in memoria in qualsiasi momento. Le parti differiscono tra le istanze dello scheletro.

Il primo approccio che ho preso è stato quello di calcolare la posizione/la rotazione di ogni osso, e costruire una matrice di vertice, che consisteva in questo, per ogni parte:

[x1,y1,u1,v1],[x2,y2,u2,v2],[x3,y3,u3,v3],[x4,y4,u4,v4] 

E passare questo fino alla glDrawElements ciascuno telaio.

Che sembra a posto, copre tutti gli scenari di cui ho bisogno, non utilizza molta memoria, ma funziona come un cane. Su un iPod 4, potresti ottenere 15 fps con 10 di questi scheletri da renderizzare.

Ho capito che la maggior parte delle prestazioni veniva consumata copiando così tanti dati di vertice per ogni fotogramma. Ho deciso di andare a un altro estremo e "pre-calcolato" le animazioni, costruendo un buffer di vertici all'inizio per ogni carattere, che conteneva le coordinate xyuv per ogni fotogramma, per ogni parte, in un singolo carattere. Quindi, calcolo l'indice del frame che deve essere utilizzato per un determinato tempo e calcola un valore delta, che viene passato allo shader utilizzato per interpolare tra le posizioni XY attuali e successive.

I vertici si presentava così, per ogni frame

[--------------------- Frame 1 ---------------------],[------- Frame 2 ------] 
[x1,y1,u1,v1,boneIndex],[x2, ...],[x3, ...],[x4, ...],[x1, ...][x2, ...][....] 

Il vertex shader assomiglia a questo:

attribute vec4 a_position; 
attribute vec4 a_nextPosition; 
attribute vec2 a_texCoords; 
attribute float a_boneIndex; 

uniform mat4 u_projectionViewMatrix; 
uniform float u_boneAlpha[255]; 

varying vec2 v_texCoords; 

void main() { 
    float alpha = u_boneAlpha[int(a_boneIndex)]; 
    vec4 position = mix(a_position, a_nextPosition, alpha); 
    gl_Position = u_projectionViewMatrix * position; 
    v_texCoords = a_texCoords; 
} 

Ora, la prestazione è ottima, con 10 di questi sullo schermo, si siede comodamente a 50fps. Ma ora usa una tonnellata di memoria. L'ho ottimizzato perdendo un po 'di precisione su xyuv, che ora sono molto utili.

C'è anche il problema che le dipendenze ossee sono perse. Se ci sono due ossa, un genitore e un bambino, e il bambino ha un fotogramma chiave a 0 e 2, il genitore ha un fotogramma chiave a 0, 0,5, 1,5, 2 secondi, quindi il bambino non verrà cambiato tra 0,5 e 1,5 secondi come dovrebbe

Mi è venuta una soluzione per risolvere questo problema osseo - costringendo il bambino ad avere fotogrammi chiave nello stesso punto dei genitori. Ma questo usa ancora più memoria e fondamentalmente uccide il punto della gerarchia ossea.

Questo è il punto in cui mi trovo ora. Sto cercando di trovare un equilibrio tra prestazioni e utilizzo della memoria. So che ci sono molte informazioni ridondanti qui (le coordinate UV sono identiche per tutti i fotogrammi di una particolare parte, quindi ripetute ~ 30 volte). E un nuovo buffer deve essere creato per ogni insieme di parti (che hanno coordinate XYUV uniche - le posizioni cambiano perché le diverse parti sono di dimensioni diverse)

In questo momento ho intenzione di provare a configurare un array di vertici per carattere, che ha il xyuv per tutte le parti e calcola le matrici per ogni parte e riposizionandole nello shader. So che questo funzionerà, ma sono preoccupato che le prestazioni non siano migliori del semplice caricamento delle XYUV per ogni frame che stavo facendo all'inizio.

C'è un modo migliore per farlo senza perdere le prestazioni che ho acquisito?

Ci sono idee selvagge che potrei provare?

+0

Bella domanda signore – Weacked

+0

Tutte le ossa "si muovono da sole" ogni fotogramma o ci sono molte ossa appena spostate perché il genitore si è mosso? – Dirk

+0

Tutte tranne una delle ossa si muove con il genitore. Ci sono alcune ossa che non fanno quasi nulla indipendentemente dal genitore, e poi ce ne sono alcune che si muovono molte più volte rispetto a quando il genitore viene spostato. –

risposta

1

Il modo migliore per farlo è trasformare le tue 30 parti al volo, non creare migliaia di copie delle tue parti in posizioni diverse. Il tuo vertice buffer conterrà una copia dei tuoi dati di vertice, risparmiando tonnellate di memoria. Quindi ogni frame può essere rappresentato da un insieme di trasformazioni passate come uniformi al vertex shader per ogni osso disegnato con una chiamata a glDrawElements(). Ogni trasformazione dell'osso dipendente è costruita rispetto all'osso genitore. Quindi, a seconda di dove si trova il continuum tra la creazione manuale e la generazione procedurale delle animazioni, le serie di trasformazioni possono richiedere più o meno spazio e tempo di elaborazione della CPU.

libro libero di Jason L. McKesson, Learning Modern 3D Graphics Programming, dà una buona spiegazione su come ottenere questo risultato nel capitolo 6. Il programma di esempio alla fine di questo capitolo mostra come utilizzare una pila di matrice per implementare un modello gerarchico. I have an OpenGL ES 2.0 on iOS port of this program available.

+0

Sfortunatamente, è esattamente come funzionava la mia versione originale. E non ha funzionato da nessuna parte quasi altrettanto rapidamente dell'attuale implementazione. Sapevo che farlo in questo modo avrebbe usato una quantità enorme di memoria e aumenterebbe i tempi di caricamento, ma è almeno il 50% più veloce con molti personaggi sullo schermo. Ma, grazie per i suggerimenti, ha votato. –