2010-01-14 8 views
6

Realizzo un software di rasterizzazione e mi sono imbattuto in un piccolo inconveniente: non riesco a ottenere il corretto funzionamento della mappatura della trama in prospettiva.Mappatura della trama corretta in prospettiva; Il calcolo della distanza z potrebbe essere errato

Il mio algoritmo è prima di ordinare le coordinate da stampare per . Questo restituisce un punto più alto, più basso e più centrale. Ho poi passeggiata attraverso le linee di scansione utilizzando il delta del:

// ordering by y is put here 

order[0] = &a_Triangle.p[v_order[0]]; 
order[1] = &a_Triangle.p[v_order[1]]; 
order[2] = &a_Triangle.p[v_order[2]]; 

float height1, height2, height3; 

height1 = (float)((int)(order[2]->y + 1) - (int)(order[0]->y)); 
height2 = (float)((int)(order[1]->y + 1) - (int)(order[0]->y)); 
height3 = (float)((int)(order[2]->y + 1) - (int)(order[1]->y)); 

// x 

float x_start, x_end; 
float x[3]; 
float x_delta[3]; 

x_delta[0] = (order[2]->x - order[0]->x)/height1; 
x_delta[1] = (order[1]->x - order[0]->x)/height2; 
x_delta[2] = (order[2]->x - order[1]->x)/height3; 

x[0] = order[0]->x; 
x[1] = order[0]->x; 
x[2] = order[1]->x; 

E poi rendiamo order[0]->y-order[2]->y, aumentando la x_start e x_end di un delta. Quando si esegue il rendering della parte superiore, i delta sono x_delta[0] e x_delta[1]. Quando si esegue il rendering della parte inferiore, i delta sono x_delta[0] e x_delta[2]. Quindi interpoliamo linearmente tra x_start e x_end sulla nostra scanline. Le coordinate UV sono interpolate nello stesso modo, ordinate da y, a partire dall'inizio e alla fine, a cui vengono applicate le delta ogni passo.

Funziona bene tranne quando cerco di eseguire la mappatura UV in prospettiva corretta. L'algoritmo di base è quello di prendere UV/z e 1/z per ogni vertice e interpolare tra di loro. Per ogni pixel, la coordinata UV diventa UV_current * z_current. Tuttavia, questo è il risultato:

alt text

La parte inversed ti dice dove il delta del sono capovolte. Come puoi vedere, entrambi i triangoli sembrano andare verso diversi punti dell'orizzonte.

Ecco quello che uso per calcolare la Z in un punto dello spazio:

float GetZToPoint(Vec3 a_Point) 
{ 
    Vec3 projected = m_Rotation * (a_Point - m_Position); 

    // #define FOV_ANGLE 60.f 
    // static const float FOCAL_LENGTH = 1/tanf(_RadToDeg(FOV_ANGLE)/2); 
    // static const float DEPTH = HALFHEIGHT * FOCAL_LENGTH; 
    float zcamera = DEPTH/projected.z; 

    return zcamera; 
} 

ho ragione, si tratta di un problema di buffer z?

risposta

4

ZBuffer non ha nulla a che fare con questo.

Lo ZBuffer è utile solo quando i triangoli si sovrappongono e si desidera assicurarsi che siano disegnati correttamente (ad esempio correttamente ordinati nella Z). Lo ZBuffer, per ogni pixel del triangolo, determinerà se un pixel precedentemente posizionato è più vicino alla fotocamera e, in tal caso, non disegnerà il pixel del triangolo.

Poiché si disegnano 2 triangoli che non si sovrappongono, questo non può essere il problema.

Ho realizzato un rasterizzatore software in virgola fissa una volta (per un telefono cellulare), ma non ho le fonti sul mio laptop. Quindi fammi controllare stasera, come l'ho fatto. In sostanza quello che hai non è male! Una cosa del genere potrebbe essere causata da un errore molto piccolo

I suggerimenti generali per il debug di questo sono di avere alcuni triangoli di prova (pendenza a sinistra, pendenza a destra, angoli di 90 gradi, ecc. Ecc.) E attraversarlo con il debugger e vedi come la tua logica si occupa dei casi.

EDIT:

peudocode del mio rasterizzazione (solo U, V e Z sono presi in considerazione ...se vuoi anche fare gouraud devi anche fare tutto per R G e B in modo simile a quello che stai facendo per U e V e Z:

L'idea è che un triangolo può essere scomposto in 2 parti. La parte superiore e la parte inferiore. La parte superiore è da y [0] a y [1] e la parte inferiore è da y [1] a y [2]. Per entrambi i set è necessario calcolare le variabili del passo con cui si interpola. L'esempio seguente mostra come eseguire la parte superiore. Se necessario, posso fornire anche la parte inferiore.

prega di notare che io già calcola gli offset di interpolazione necessari per la parte inferiore nel frammento di seguito 'pseudocodice'

  • primo ordine le coordinate (x, y, z, u, v) nell'ordine in modo che coord [0] .y < coord [1] .y < coord [2] .y
  • successivo controllo se 2 set di coordinate sono identici (solo controllare xey). Se è così non disegnare
  • eccezione: il triangolo ha una cima piatta? in tal caso, la prima pendenza sarà infinita
  • exception2: il triangolo ha un fondo piatto (sì i triangoli possono avere anche questi; ^)) quindi anche l'ultima pendenza sarà infinita
  • calcolare 2 pendenze (lato sinistro e lato destro)
    leftDeltaX = (x [1] - x [0])/(y [1] -y [0]) e rightDeltaX = (x [2] - x [0])/(y [2] -y [0])
  • la seconda parte del triangolo è calcolato in base: se il lato sinistro del triangolo è ora davvero sul lato sinistro (o deve scambiare) frammento

codice:

if (leftDeltaX < rightDeltaX) 
{ 
     leftDeltaX2 = (x[2]-x[1])/(y[2]-y[1]) 
     rightDeltaX2 = rightDeltaX 
     leftDeltaU = (u[1]-u[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaU2 = (u[2]-u[1])/(y[2]-y[1]) 
     leftDeltaV = (v[1]-v[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaV2 = (v[2]-v[1])/(y[2]-y[1]) 
     leftDeltaZ = (z[1]-z[0])/(y[1]-y[0]) //for texture mapping 
     leftDeltaZ2 = (z[2]-z[1])/(y[2]-y[1]) 
} 
else 
{ 
     swap(leftDeltaX, rightDeltaX); 
     leftDeltaX2 = leftDeltaX; 
     rightDeltaX2 = (x[2]-x[1])/(y[2]-y[1]) 
     leftDeltaU = (u[2]-u[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaU2 = leftDeltaU 
     leftDeltaV = (v[2]-v[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaV2 = leftDeltaV 
     leftDeltaZ = (z[2]-z[0])/(y[2]-y[0]) //for texture mapping 
     leftDeltaZ2 = leftDeltaZ 
    } 
  • impostare il currentLeftX e currentRightX sia x [0]
  • set currentLeftU su leftDeltaU, currentLeftV su leftDeltaV e currentLeftZ su leftDeltaZ
  • calc aprire endpoint per primo intervallo Y: starty = ceil (y [0]); endY = ceil (y [1])
  • prestep x, u, v e z per la parte frazionaria di y per accuratezza subpixel (credo sia necessario anche per i galleggianti) Per i miei algoritmi a punto fisso questo era necessario per rendere linee e trame danno l'illusione di muoversi in passi molto più fini rispetto alla risoluzione del display)
  • calcola dove x dovrebbe essere in y [1]: halfwayX = (x [2] -x [0]) * (y [ 1] -y [0])/(y [2] -y [0]) + x [0] e lo stesso per U e V e z: halfwayU = (u [2] -u [0]) * (y [1] -y [0]) + (y [2] -y [0]) + u [0]
  • e utilizzando la scala a metàX calcolare lo stepper per U e V e z: se (a metà strada - x [1] == 0) {slopeU = 0, slopeV = 0, slopeZ = 0} else {slopeU = (halfwayU - U [1])/(halfwayX - x [1])}// (E lo stesso per v e z)
  • non saturazione per la parte superiore Y (quindi calcolare dove stiamo per iniziare a disegnare nel caso la parte superiore del triangolo è fuori dallo schermo (o disattivare il rettangolo di ridimensionamento))
  • per y = startY; y < endY; y ++) {
    • è Y passato in fondo allo schermo? smetti di renderizzare!
    • calc startX e endX per la prima riga orizzontale leftCurX = ceil (startx); leftCurY = ceil (endy);
    • agganciare la linea da trarre al bordo orizzontale sinistro dello schermo (o clipping regione)
    • preparare un puntatore al buffer di destinazione (farlo attraverso indici di matrice ogni volta è troppo lento) unsigned int BUF = destbuf + (y pitch) + startX; (Unsigned int nel caso in cui si sta facendo 24bit o il rendering 32 bit) anche preparare il vostro puntatore Zbuffer qui (se si utilizza questo)
    • per (x = startx; x < EndX; x ++) {
      • ora per la mappatura punto di vista di struttura (non usando bilineair interpolazione di effettuare le seguenti):

frammento di codice:

  float tv = startV/startZ 
     float tu = startU/startZ; 
     tv %= texturePitch; //make sure the texture coordinates stay on the texture if they are too wide/high 
     tu %= texturePitch; //I'm assuming square textures here. With fixed point you could have used &= 
     unsigned int *textPtr = textureBuf+tu + (tv*texturePitch); //in case of fixedpoints one could have shifted the tv. Now we have to multiply everytime. 
     int destColTm = *(textPtr); //this is the color (if we only use texture mapping) we'll be needing for the pixel 
  • riga fittizia
    • riga fittizia
      • riga fittizia
      • opzionale: controllare lo Zbuffer se il pixel precedentemente tracciata in questa coordinata è maggiore o minore nostra.
      • grafico del pixel
      • startZ + = slopeZ; startU + = slopeU; startV + = slopeV; // aggiornamento tutti interpolatori
    • } x fine ciclo
    • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; leftCurV + = rightDeltaV; leftCurZ + = rightDeltaZ; // aggiornamento Y interpolatori
  • } fine del ciclo y

    // questa è la fine della prima parte. Ora abbiamo disegnato metà del triangolo. dalla cima, alla coordinata Y centrale. // ora praticamente fare la stessa cosa, ma ora per la metà inferiore del triangolo (utilizzando l'altra serie di interpolatori)

dispiace per le 'linee fittizie' .. loro sono stati necessari per ottenere il codici di markdown sincronizzati. (mi ci è voluto un po 'per ottenere tutto in ordine, visto come previsto)

fammi sapere se questo ti aiuta a risolvere il problema che stai affrontando!

+0

altro suggerimento per il debugging, calcolare ogni punto trama utilizzando il vecchio, non incrementale U e V coordinate per ciascun pixel e confronta i valori calcolati dal incrementali dell'algoritmo (consentendo un certo slop causa di errore di arrotondamento). –

+1

Wow ... questa è una risposta e mezza! : D – Goz

+0

goz: ora spero solo che sia utile poiché probabilmente ha già la maggior parte di questo codice in atto a giudicare dalla foto. Sto pensando che probabilmente ha calcolato male uno dei valori di interpolazione. Ma se tutto va bene se mette il suo algoritmo vicino al mio, può scoprire quale è; ^) – Toad

0

Non so che posso aiutare con la tua domanda, ma uno dei migliori libri sul rendering del software che avevo letto in quel momento è disponibile online Graphics Programming Black Book da Michael Abrash.

0

Se si interpola 1/z, è necessario moltiplicare UV/z per z, non 1/z.Supponendo di avere questo:

UV = UV_current * z_current

e z_current è interpolando 1/z, si dovrebbe cambiare a:

UV = UV_current/z_current

E allora si potrebbe desiderare di rinominare z_current a qualcosa di simile one_over_z_current.

+0

Probabilmente è un trucco da accelerare: ha bisogno di dividere per z. Ma poiché la moltiplicazione è più veloce della divisione, si moltiplica per 1/z. – Toad

+0

Non è possibile evitare una divisione con la mappatura della trama corretta in prospettiva. Puoi approssimarlo a uno spam di pixel, ma non puoi eliminarlo. Supponendo che tu voglia qualcosa che sembra corretto. – MSN

Problemi correlati