Interpolazione di profondità per z-buffer, con scanline


10

Devo scrivere il mio software 3d rasterizer e finora sono in grado di proiettare il mio modello 3d fatto di triangoli nello spazio 2d:

Ruoto, traduco e proietto i miei punti per ottenere una rappresentazione dello spazio 2D di ciascun triangolo. Quindi prendo i 3 punti del triangolo e implemento l'algoritmo scanline (usando l'interpolazione lineare) per trovare tutti i punti [x] [y] lungo i bordi (sinistro e destro) dei triangoli, in modo da poter scansionare il triangolo in orizzontale, riga per riga e riempilo di pixel.

Questo funziona Tranne che devo implementare anche il buffering z. Ciò significa che conoscendo le coordinate z ruotate e tradotte dei 3 vertici del triangolo, devo interpolare la coordinata z per tutti gli altri punti che trovo con il mio algoritmo scanline.

Il concetto sembra abbastanza chiaro, per prima cosa trovo Za e Zb con questi calcoli:

var Z_Slope = (bottom_point_z - top_point_z) / (bottom_point_y - top_point_y);
var Za = top_point_z + ((current_point_y - top_point_y) * Z_Slope);

Quindi per ogni Zp faccio la stessa interpolazione in senso orizzontale:

var Z_Slope = (right_z - left_z) / (right_x - left_x);
var Zp = left_z + ((current_point_x - left_x) * Z_Slope);

inserisci qui la descrizione dell'immagine

E se l'attuale z è più vicino al visualizzatore rispetto alla z precedente in quell'indice, allora scrivi il colore nel buffer colore E scrivi il nuovo z nel buffer z. (il mio sistema di coordinate è x: sinistra -> destra; y: superiore -> inferiore; z: il tuo viso -> schermo del computer;)

Il problema è che va in tilt. Il progetto è qui e se selezioni il pulsante di opzione "Z-Buffered", vedrai i risultati ... ( nota che uso l'algoritmo del pittore (-solo- per disegnare il wireframe) in modalità "Z-Buffered" per scopi di debug )

PS: Ho letto qui che devi trasformare le z nei loro reciproci (significato z = 1/z) prima di interpolare. L'ho provato e sembra che non ci siano cambiamenti. Cosa mi sto perdendo? (qualcuno potrebbe chiarire, esattamente dove devi trasformare z in 1 / z e dove (se) per tornare indietro?)

[EDIT] Ecco alcuni dati su quali valori z massimo e minimo ottengo:

    max z: 1;                  min z: -1;                 //<-- obvious, original z of the vertices of the triangles
    max z: 7.197753398761272;  min z: 3.791703256899924;   //<-- z of the points that were drawn to screen (you know, after rotation, translation), by the scanline with zbuffer, gotten with interpolation but not 1/z.
    max z: 0.2649908532179404; min z: 0.13849507306889008;//<-- same as above except I interpolated 1/z instead of z.
//yes, I am aware that changing z to 1/z means flipping the comparison in the zBuffer check. otherwise nothing gets drawn.

Prima di approfondire scrupolosamente il debug, qualcuno può confermare che il mio concetto finora è corretto?

[EDIT2]

Ho risolto il buffering z. A quanto pare, l'ordine del disegno non è stato affatto incasinato. Le coordinate z venivano calcolate correttamente.

Il problema era che, nel tentativo di aumentare la mia frequenza dei fotogrammi, stavo disegnando caselle 4px / 4px, ogni 4 pixel, anziché pixel effettivi sullo schermo. Quindi stavo disegnando 16 pixel per pixel, ma controllando il buffer z solo per uno di essi. Sono un tale boob.

TL / DR: la domanda è ancora valida: come / perché / quando è necessario utilizzare il reciproco di Z (come in 1 / z) anziché Z? Perché in questo momento, tutto funziona in entrambi i modi. (non c'è alcuna differenza evidente).


Ri: "E ovviamente aggiungo a zBuffer, se l'attuale z è più vicino al visualizzatore rispetto al valore precedente in quell'indice." Non è chiaro per me che hai formulato il modo in cui intendevi, ma volevi assicurarti che ciò che intendevi fosse "se la z corrente è più vicina allo spettatore rispetto alla z precedente in quell'indice, quindi scrivi il colore nel buffer dei colori E scrivi il nuovo z nel buffer z "Lo scopo del buffer z è di bloccare le scritture dei colori se un colore in quel pixel era già stato scritto più vicino all'occhio della fotocamera.
Alturis,

È corretto. Scusa, era tardi quando ho formulato la mia domanda. Revisionerò.
Spectraljump,

Risposte:


5

Risposta rapida: Z non è una funzione lineare di (X ', Y'), ma 1 / Z lo è. Dato che interpoli linearmente, ottieni risultati corretti per 1 / Z, ma non per Z.

Non ti accorgi perché fintanto che il confronto tra Z1 e Z2 è corretto, zbuffer farà la cosa giusta, anche se entrambi i valori sono sbagliati. Noterai sicuramente quando aggiungi la mappatura delle trame (e per rispondere alla domanda che dovrai quindi: interpolare 1 / Z, U / Z e V / Z e ricostruire U e V da questi valori: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Mi ringrazierai più tardi)

Un esempio. Prendi un pezzo di carta. Vista dall'alto in basso, quindi dimentica la coordinata Y. X è l'asse orizzontale, Z è l'asse verticale, la telecamera è su (0, 0), il piano di proiezione è z = 1.

Considera i punti A (-2, 2) e B (2, 4). Il punto medio M del segmento AB è (0, 3). Fin qui tutto bene.

Proiettate A in A ': X' = X / Z = -1, quindi A 'è (-1, 1). Allo stesso modo, B 'è (0,5, 1). Ma nota che la proiezione di M è (0, 1), che NON è il punto medio di A'B '. Perché? Perché la metà destra del segmento è più lontana dalla fotocamera rispetto alla metà sinistra, quindi sembra più piccola.

Quindi cosa succede se provi a calcolare la Z di M 'usando l'interpolazione lineare? dx = (0,5 - -1) = 1,5, dz = (4 - 2) = 2, quindi per M 'dove X' = 0, la Z interpolata linearmente è zA + (dz / dx) (x - xA) = 2 + (2 / 1.5) (0 - -1) = 2 + 1.333 = 3.3333 - NON 3!

Perché? Perché per ogni passo nella direzione X ', non si sposta la stessa quantità nella direzione Z (o, in altre parole, Z non è una funzione lineare di X'). Perché? Perché più vai a destra, più lontano il segmento è dalla telecamera, quindi un pixel rappresenta una distanza più lunga nello spazio.

Infine, cosa succede se interpoli 1 / Z invece? Per prima cosa calcoli 1 / Z in A e B: 0,5 e 0,25 rispettivamente. Quindi interpoli: dx = (0,5 - -1) = 1,5, dz = (0,25 - 0,5) = -0,25, quindi a X '= 0 calcoli 1 / Z = 0,5 + (-0,25 / 1,5) * (0 - -1) = 0,3333. Ma questo è 1 / Z, quindi il valore di Z è ... esattamente, 3. Come dovrebbe essere.

Sì, la matematica è fantastica.


1
Oh, e per quanto riguarda "quando": calcola i valori 1 / Z prima di iniziare a rasterizzare il triangolo (ad esempio appena prima del ciclo verticale), in modo da ottenere 1 / Z interpolato a sinistra e a destra della linea di scansione. Interpolare questi in modo lineare (NON eseguire nuovamente 1 / Z - i valori interpolati sono già 1 / Z!) E annullare la trasformazione appena prima di controllare zbuffer.
ggambett,

1
E infine, perché. Un piano (in cui è incorporato il triangolo) è Ax + By + Cz + D = 0. z è una funzione chiaramente lineare di (x, y). Si proietta così x '= x / z e y' = y / z. Da lì, x = x'z e y = y'z. Se li sostituisci nell'equazione originale ottieni Ax'z + By'x + Cz + D = 0. Ora z = -D / (Ax '+ By' + C), dove è chiaro che z non è una funzione lineare di (x ', y'). Ma 1 / z è quindi (Ax '+ By' + C) / -D, che è una funzione lineare di (x ', y').
ggambett,

1
Sai, ho letto parecchi articoli e corsi, e nessuno di loro era abbastanza chiaro come la tua risposta. Per i posteri, noterò anche che "Le lettere" U "e" V "indicano gli assi della trama 2D perché" X "," Y "e" Z "sono già usati per indicare gli assi dell'oggetto 3D nel modello spazio. La texture UV consente ai poligoni che compongono un oggetto 3D di essere dipinti con il colore di un'immagine ". - Wikipedia - Mappatura UV
Spectraljump

Felice di sentirlo. In effetti, ho insegnato Computer Graphics in una vita precedente :)
ggambett,

Grazie mille - sono sempre stato curioso di questo - e non so se avrei mai trovato una risposta migliore! +1
Codesmith,
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.