2012-10-19 9 views
5

Sto cercando di trovare una soluzione che mi consenta di ruotare sprite di punti attorno all'asse z con un attributo variabile (ad esempio, l'uniforme non funzionerà).Sprites di punto OpenGL ES 2.0 con rotazioni distinte - calcolare la matrice nello shader?

Nella mia app ho molte centinaia/migliaia di sprite di punti che vengono disegnate per fotogramma, che vengono quindi memorizzate in VBO (può essere quasi fattibile> 1.000.000). In quanto tale, sto cercando il miglior compromesso tra utilizzo della memoria e prestazioni.

Vertex shaders & frammento aspetto attuale come questo:

// VERTEX SHADER 
attribute vec4 a_position; 
attribute vec4 a_color; 
attribute float a_size; 
uniform mat4 u_mvpMatrix; 
varying vec4 v_color; 

void main() 
{ 
    v_color = a_color; 
    gl_Position = u_mvpMatrix * a_position; 
    gl_PointSize = a_size; 
} 


// FRAGMENT SHADER 
precision mediump float; 
uniform sampler2D s_texture; 
varying vec4 v_color; 

void main() 
{ 
    vec4 textureColor = texture2D(s_texture, gl_PointCoord); 
    gl_FragColor = v_color * textureColor; 
} 

posso immaginare attualmente le seguenti possibilità:

  • Aggiungere un attributo mat4 rotMatrix i miei dati punto sprite. Passare questo allo shader frammento e ruotare ogni frammento:

    vec2 texCoord = (rotMatrix * vec4(gl_PointCoord, 0, 1)).xy 
    gl_FragColor = v_color * texture2D(s_texture, texCoord); 
    
    • Vantaggi:
      • Mantiene shader semplice.
      • Codice semplice per calcolare matrici al di fuori degli shader (utilizzando GLKit ad esempio).
    • Svantaggi:
      • aumenta notevolmente la dimensione dei dati sprite punti (da 16 a 80 byte/punto per una matrice 4x4; a 52 byte/punto per una matrice 3x3 ... Credo che sia possibile usare una matrice di rotazione 3x3?). Ciò potrebbe potenzialmente causare il crash della mia app 3-5 volte prima!
      • Spinge molto più il calcolo sulla CPU (centinaia/migliaia di calcoli a matrice per fotogramma).


  • Aggiungere un attributo float angle miei dati punto sprite, quindi calcolare la matrice di rotazione nel vertex. Passa la matrice di rotazione allo shader del frammento come sopra.

    • Vantaggi:
      • mantiene il punto dimensione dei dati sprite piccola (da 16 a 20 byte/punto).
      • Spinge la matematica della matrice di sollevamento pesante alla GPU.
    • Svantaggi:
      • bisogno di scrivere la funzione GLSL personalizzato per creare matrice di rotazione. Non è un problema enorme, ma la matematica della mia matrice è arrugginita, quindi potrebbe essere soggetta a errori, soprattutto se sto cercando di capire la soluzione 3x3 matrix ...
      • Dato che questo deve accadere su centinaia/migliaia di vertici, questo sarà un serio ostacolo alle prestazioni (nonostante sia gestito dalla GPU)?


  • ho potuto realisticamente affrontare 1 byte per l'attributo angolo (255 angolazioni diverse sarebbero sufficienti). C'è un modo in cui posso usare qualche tipo di ricerca in modo da non dover ricalcolare inutilmente le stesse matrici di rotazione? Memorizzare le costanti nel vertex shader è stato il mio primo pensiero, ma non voglio iniziare a inserire dichiarazioni di rami nei miei ombreggiatori.

Qualche idea su un buon approccio?

risposta

2

La soluzione che ho seguito alla fine è stata la seconda dalla domanda: calcolare la matrice di rotazione nel vertex shader. Ciò presenta i seguenti vantaggi:

  • Mantiene piccola la dimensione dei dati del punto di sprite.
  • I calcoli di rotazione vengono eseguiti dalla GPU.

Gli svantaggi che ho indovinato non sembrano applicarsi. Non ho notato un successo in termini di prestazioni, anche in esecuzione su un iPad di 1 gen. Il calcolo della matrice in GLSL è alquanto complicato, ma funziona correttamente. A beneficio di chiunque altro cercando di fare lo stesso, qui è la parte rilevante del vertex shader:

//... 
attribute float a_angle; 
varying mat4 v_rotationMatrix; 

void main() 
{ 
    //... 

    float cos = cos(a_angle); 
    float sin = sin(a_angle); 
    mat4 transInMat = mat4(1.0, 0.0, 0.0, 0.0, 
          0.0, 1.0, 0.0, 0.0, 
          0.0, 0.0, 1.0, 0.0, 
          0.5, 0.5, 0.0, 1.0); 
    mat4 rotMat = mat4(cos, -sin, 0.0, 0.0, 
         sin, cos, 0.0, 0.0, 
         0.0, 0.0, 1.0, 0.0, 
         0.0, 0.0, 0.0, 1.0); 
    mat4 resultMat = transInMat * rotMat; 
    resultMat[3][0] = resultMat[3][0] + resultMat[0][0] * -0.5 + resultMat[1][0] * -0.5; 
    resultMat[3][1] = resultMat[3][1] + resultMat[0][1] * -0.5 + resultMat[1][1] * -0.5; 
    resultMat[3][2] = resultMat[3][2] + resultMat[0][2] * -0.5 + resultMat[1][2] * -0.5; 
    v_rotationMatrix = resultMat; 

    //... 
} 

Dato che non v'è alcuna prestazione notevole colpito questa soluzione è ideale, in quanto non v'è alcuna necessità di creare tessitura mappe/ricerche e consumo di memoria aggiuntiva, e mantiene il resto del codice pulito e semplice.

Non posso dire che non ci siano aspetti negativi nel calcolo di una matrice per ogni vertice (durata della batteria ridotta, ad esempio), e le prestazioni potrebbero essere un problema in diversi scenari, ma è buono per ciò di cui ho bisogno.

1

Hai mai pensato di utilizzare diverse trame pre-calcolate e ruotate (un atlante di texture)? Se solo pochi angoli sono sufficienti per l'effetto che stai cercando di ottenere, sarebbe una soluzione molto veloce.

Su una nota diversa, vi è una penalità di prestazioni per il calcolo delle coordinate di trama all'interno dello shader di frammenti (ricerche di texture indirette). Questo potrebbe non essere importante per il tuo caso, ma vale la pena tenerlo a mente.

+0

Ciao, non l'ho ancora provato e sembra una soluzione ragionevole. Da quando ho postato la domanda ho risolto il mio problema, ma ho dimenticato di postare la risposta (grazie per avermelo ricordato!). Ho finito per calcolare la matrice di rotazione nel vertex shader.Il colpo di performance è trascurabile anche su un iPad di 1 gen, e mantiene le mie strutture dati piccole. Il tuo metodo potrebbe anche funzionare, ma uno svantaggio è che aumenterebbe l'utilizzo della memoria delle texture (avrei bisogno di oltre 200 rotazioni per ogni sprite). Mi piace la soluzione vertex shader perché non ho bisogno di giocherellare con gli sprite e le prestazioni sono accettabili. – Stuart

1

Ecco il vostro pre-moltiplicato matrice di rotazione:

matrice
v_rotationMatrix = mat3(cos, sin, 0.0, 
         -sin, cos, 0.0, 
         (sin-cos+1.0)*0.5, (-sin-cos+1.0)*0.5, 1.0); 
1

FWIW, questo è il 3x3 pre-calcolata ho ottenuto che corrisponde il codice di Stuart:

v_rotationMatrix = Mat3 (cos, -sin, 0.0, sin, cos, 0.0, (1.0-cos-sin) * 0.5, (1.0 + sin-cos) * 0.5, 1.0);

Si noti che le matrici glsl sono in formato colonna principale.

Problemi correlati