Utilizzo della risoluzione completa del buffer di profondità per il rendering 2D


9

Sto lavorando su un renderer fronte-retro per un motore 2D usando una proiezione ortografica. Voglio usare il buffer di profondità per evitare il sovraccarico. Ho un buffer di profondità a 16 bit, una telecamera su Z = 100 guarda Z = 0, zNear è 1 e zFar è 1000. Ogni sprite renderizzato imposta le sue coordinate Z su valori sempre più distanti, consentendo al test di profondità di saltare il rendering tutto ciò che è sotto.

Tuttavia, sono consapevole che il modo in cui le posizioni Z finiscono con i valori del buffer Z non è lineare. Voglio sfruttare la piena risoluzione del buffer di profondità a 16 bit, ovvero consentire 65536 valori univoci. Quindi per ogni sprite renderizzato, voglio incrementare la posizione Z alla posizione successiva per correlare al successivo valore di buffer di profondità univoco.

In altre parole, voglio trasformare un indice incrementale (0, 1, 2, 3 ...) dello sprite che viene disegnato nella posizione Z appropriata affinché ogni sprite abbia un valore di buffer di profondità univoco. Non sono sicuro della matematica dietro questo. Qual è il calcolo per fare questo?

Nota: sto lavorando in WebGL (fondamentalmente OpenGL ES 2) e ho bisogno di supportare una vasta gamma di hardware, quindi mentre estensioni come gl_FragDepth potrebbero renderlo più semplice, non posso usarlo per motivi di compatibilità.


Non riesco a immaginare che l'uso del buffer z ti offrirà un notevole miglioramento delle prestazioni (se presente) dopo aver aggiunto tutta la scrittura, i calcoli e i confronti del buffer z rispetto alla copia di trame in primo piano, per non parlare di trasparenza / fusione alfa guai.
Matt Esch,

@MattEsch: l'idea è che tutti questi calcoli vengano eseguiti nella GPU a velocità incredibilmente elevate, quindi ha senso farlo.
Panda Pajama,

@MattEsch: FWIW si rivolge alle GPU integrate Intel, che utilizzano la memoria di sistema anziché la memoria GPU dedicata. Ciò li rende piuttosto lenti e suscettibili di superare i limiti di riempimento se si superano molti sprite. Intel mi ha consigliato questo approccio come un modo per aggirarlo. Presumibilmente, la loro implementazione dei test di profondità è ben ottimizzata e può far risparmiare molto. Resta da vedere però, non l'ho ancora profilato!
AshleysBrain

Il blocco di @PandaPajama copiare la memoria è in realtà molto veloce, quindi se si stesse semplicemente fondendo trame su una superficie, sarebbe davvero molto veloce. Il primo grande sovraccarico è innanzitutto quello di ottenere dati sulla GPU, che come sottolinea Ashley può essere più costosa per le GPU integrate. Scoprite che anche molti giochi 3d svolgono una quantità non banale di lavoro sulla CPU (come l'animazione dell'osso) perché il caricamento dei dati necessari per eseguire quei calcoli con matrici in primo luogo è troppo costoso.
Matt Esch,

@MattEsch: c'è solo così tanto che puoi fare solo con le benedizioni. Vengono in mente rotazioni, ridimensionamenti e deformazioni, ma anche dal momento che si hanno shader pixel / vertici, il limite di ciò che si può fare con l'hardware è molto più alto di quello che si può fare con il blitting.
Panda Pajama,

Risposte:


5

In effetti, i valori memorizzati nel buffer z non sono lineari rispetto alle coordinate z effettive dei tuoi oggetti, ma al loro reciproco, al fine di dare più risoluzione a ciò che è vicino all'occhio che a ciò che è più vicino al piano posteriore.

Quello che fai è che si mappa la vostra zNearper 0e la vostra zFara 1. Per zNear=1e zFar=2, dovrebbe apparire così

Zbuffer

Il modo per calcolare questo è definito da:

z_buffer_value = k * (a + (b / z))

Dove

 k = (1 << N), maximum value the Z buffer can store
 N = number of bits of Z precision
 a = zFar / ( zFar - zNear )
 b = zFar * zNear / ( zNear - zFar )
 z = distance from the eye to the object

... e z_buffer_value è un numero intero.

Sopra l'equazione ti viene offerta per gentile concessione di questa fantastica pagina , che spiega z-buffers in un modo davvero buono.

Quindi, al fine di trovare il necessario zper un dato z_buffer_value, cancelliamo semplicemente z:

z = (k * b) / (z_buffer_value - (k * a))

Grazie per la risposta! Sono un po 'confuso su come hai ottenuto la tua formula finale. Se prendo z_buffer_value = k * (a + (b / z))e semplicemente riorganizzo per risolvere z, allora ottengo: z = b / ((z_buffer_value / k) - a)- come sei arrivato alla diversa ultima formula?
AshleysBrain,

@AshleysBrain: prendi il denominatore (v / k) - a => (v - k * a) / ke crolli (k * b) / (v - (k * a)). È lo stesso risultato.
Panda Pajama,

Ah, capisco. Grazie per la risposta, funziona bene!
AshleysBrain,

0

Forse dovresti cambiare Il tuo approccio a qualcosa di più semplice. Cosa farei; Mantieni la tua profondità Z, ma mantieni un elenco di ciò che rendi. Ordina tale elenco in base al valore di profondità z e visualizza gli oggetti nell'ordine dell'elenco.

Spero che questo possa aiutare. Le persone mi dicono sempre di mantenere le cose semplici.


1
Mi dispiace, non è di grande aiuto. Lo sto già facendo. La domanda riguarda quali posizioni Z scegliere.
AshleysBrain,

0

Dato che hai già un elenco ordinato di cose da renderizzare (dalla parte anteriore a quella posteriore), devi davvero incrementare l'indice Z? Non puoi usare "minore o uguale" per la "funzione di controllo"? In questo modo verificherebbe effettivamente se un pixel specifico era già stato disegnato o meno.


"minore o uguale" farà comunque sì che tutto venga sovrascritto, poiché tutto avrebbe sempre un uguale indice Z e quindi supererebbe il test di profondità.
AshleysBrain
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.