Sei davvero nel giusto percorso mentale.
Quando disegni la geometria, il vertex shader viene eseguito separatamente su ciascun vertice della geometria e trasforma le posizioni nello spazio clip, oltre a consentire la manipolazione di qualsiasi attributo associato.
Lo shader del frammento viene invocato su ciascun pixel considerato come coperto dalla geometria e gli attributi dei tre vertici sono interpolati sul triangolo.
Se si desidera modificare la posizione del proprio sprite nella vista, si altera l'attributo position, modificando il buffer dei vertici o modificando i parametri sullo shader con cui è possibile sfalsare la geometria nello spazio. Allo stesso modo, puoi modificare le coordinate della trama per decidere da quale regione della trama sei interessato al campionamento.
Se disegni solo uno o alcuni sprite, ciò che fai non ha un peso eccessivo sulle prestazioni.
Se sei interessato a disegnarli alla rinfusa, ti imbatterai in diversi colli di bottiglia:
- il ricalcolo e la memorizzazione dei vertici di tutti i tuoi sprite ha un costo di CPU e memoria;
- caricare la geometria ha una larghezza di banda e un colpo di latenza;
- ogni chiamata di estrazione ha un sacco di spese generali nel driver.
Una soluzione comune a questo è istanziamento . Invece di legare un singolo set di buffer di vertici che contengono gli attributi di vertice per una mesh, puoi associare due set.
Uno degli insiemi sarà un insieme di vertici, che descrive un singolo pezzo di geometria.
L'altro set rappresenta istanze distinte di quella geometria master, contenente gli attributi per istanza.
Se in precedenza avevi attributi vertice pos
e tc
in una configurazione come questa:
vertices =
// position texcoord
{ {corner , tc_corner}
, {corner + float2(width, 0) , tc_corner + float2(sprite_width, 0) }
, {corner + float2(width, height), tc_corner + float2(sprite_width, sprite_height)}
, {corner + float2(0, height) , tc_corner + float2(0, sprite_height) } }
con le istanze potresti separare le cose che variano per istanza, lasciandoti un buffer di vertici che può essere riutilizzato su più sprite:
pervertex =
// position texcoord
{ {float2(0, 0), float2(0, 0)}
, {float2(1, 0), float2(1, 0)}
, {float2(1, 1), float2(1, 1)}
, {float2(0, 1), float2(0, 1)} }
perinstance =
// world_pos world_size texture_pos texture_size
{ {corner, float2(width, height), tc_corner, float2(sprite_width, sprite_height)}
, another_instance
, more_instances... }
Dal punto di vista del vertex shader, assomiglierà a un gruppo di attributi, la distribuzione degli attributi per-vertice e per-instance viene eseguita in anticipo dall'assembly input.
Il vertex shader farebbe semplicemente i conti sulla falsariga di:
out_tc = texture_pos + tc * texture_size;
gl_Position = float4(world_pos + pos * world_size, 0.0, 1.0);
Nota che l'instancing non è necessario per fare questo genere di cose, puoi emularlo ragionevolmente bene con più draw call e passare i parametri per istanza in variabili uniformi al tuo programma shader.