Mappatura delle texture corretta in prospettiva;Il calcolo della distanza z potrebbe essere errato

StackOverflow https://stackoverflow.com/questions/2068134

  •  20-09-2019
  •  | 
  •  

Domanda

Sto creando un rasterizzatore software e ho riscontrato un piccolo intoppo:Non riesco a far funzionare la mappatura delle texture con la prospettiva corretta.

Il mio algoritmo consiste nell'ordinare innanzitutto le coordinate in base alle quali tracciare y.Ciò restituisce un punto più alto, più basso e centrale.Quindi cammino attraverso le linee di scansione utilizzando i delta:

// 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 eseguiamo il rendering da order[0]->y A order[2]->y, aumentando il x_start E x_end da un delta.Quando si renderizza la parte superiore, i delta sono x_delta[0] E x_delta[1].Quando si renderizza la parte inferiore, i delta sono x_delta[0] E x_delta[2].Quindi interpoliamo linearmente tra x_start e x_end sulla nostra linea di scansione.Le coordinate UV vengono interpolate nello stesso modo, ordinate per y, iniziando dall'inizio e dalla fine, a cui vengono applicati i delta in ogni passaggio.

Funziona bene tranne quando provo a eseguire la mappatura UV corretta in prospettiva.L'algoritmo di base è 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.Questo però è il risultato:

alt text

La parte invertita ti dice dove sono invertiti i delta.Come puoi vedere, i due triangoli sembrano entrambi dirigersi verso punti diversi dell'orizzonte.

Ecco cosa utilizzo per calcolare la Z in un punto nello 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, è un problema di buffer z?

È stato utile?

Soluzione

ZBuffer non ha nulla a che fare con questo.

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

Dato che stai disegnando 2 triangoli che non si sovrappongono, questo non può essere il problema.

Una volta ho realizzato un rasterizzatore software in punto fisso (per un telefono cellulare), ma non ho i sorgenti sul mio laptop.Quindi fammi controllare stasera, come ho fatto.In sostanza quello che hai ottenuto non è male!Una cosa del genere potrebbe essere causata da un errore molto piccolo

Suggerimenti generali per il debugging consistono nell'avere alcuni triangoli di prova (pendenza sul lato sinistro, pendenza sul lato destro, angoli di 90 gradi, ecc. Ecc.) ed esaminarli con il debugger e vedere come la tua logica gestisce i casi.

MODIFICARE:

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

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

Tieni presente che calcolo già gli offset di interpolazione necessari per la parte inferiore nel frammento "pseudocodice" sottostante

  • prima ordina le coordinate(x,y,z,u,v) in modo che coord[0].y < coord[1].y < coord[2].y
  • quindi controlla se due gruppi di coordinate sono identici (controlla solo x e y).Se è così non disegnare
  • eccezione:il triangolo ha il vertice piatto?se è così, la prima pendenza sarà infinita
  • eccezione2:il triangolo ha un fondo piatto (sì, anche i triangoli possono averli ;^)) allora 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 si calcola in funzione di:se il lato sinistro del triangolo ora è davvero sul lato sinistro (o deve essere scambiato)

frammento di 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
  }
  • imposta currentLeftX e currentRightX entrambi su x[0]
  • imposta currentLeftU su leftDeltaU, currentLeftV su leftDeltaV e currentLeftZ su leftDeltaZ
  • inizio e punto finale del calcolo per il primo intervallo Y:inizioY = ceil(y[0]);fineY = ceil(y[1])
  • PRESSEP X, U, V e Z Per la parte frazionaria di Y per l'accuratezza del subpixel (immagino che questo sia anche necessario per i galleggianti) per i miei algoritmi a punto fisso, questo era necessario per far sì che le linee e le trame danno l'illusione di muoversi in passaggi molto più fini di allora la risoluzione del display)
  • calcola dove dovrebbe essere x 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 :a metàU = (u[2]-u[0]) * (y[1]-y[0]) / (y[2]-y[0]) + u[0]
  • e utilizzando halfwayX calcola lo stepper per U, V e z:if(metàX - x[1] == 0){ pendenzaU=0, pendenzaV=0, pendenzaZ=0 } else { pendenzaU = (metàU - U[1]) / (metàX - x[1])} //( e lo stesso per v e z)
  • ritaglia la parte superiore Y (quindi calcola dove inizieremo a disegnare nel caso in cui la parte superiore del triangolo sia fuori dallo schermo (o fuori dal rettangolo di ritaglio))
  • per y=inizioY;y < fineY;y ++) {
    • Y è oltre la parte inferiore dello schermo?smetti di renderizzare!
    • calc startx ed endX per la prima linea orizzontale LeftCurx = ceil (startX);leftCurY = ceil(endy);
    • ritaglia la linea da tracciare sul bordo orizzontale sinistro dello schermo (o sull'area di ritaglio)
    • Prepara un puntatore al buffer di destinazione (farlo attraverso gli indici dell'array ogni volta è troppo lento) senza segno buf = destbuf + (ypasso) + inizioX;(Unsigned Int nel caso in cui stai facendo il rendering a 24 bit o 32 bit) Prepara anche il tuo puntatore ZBuffer qui (se lo stai usando)
    • for(x=inizioX;x < fineX;x ++) {
      • ora per la mappatura delle texture prospettiche (usando senza interpolazione bilineare fai quanto segue):

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
  • linea fittizia
    • linea fittizia
      • linea fittizia
      • opzionale:controlla lo zbuffer se il pixel precedentemente tracciato a questa coordinata è più alto o più basso del nostro.
      • tracciare il pixel
      • inizioZ += pendenzaZ;inizioU+=pendenzaU;inizioV += pendenzaV;//aggiorna tutti gli interpolatori
    • } fine del ciclo x
    • LeftCurX+= LeftDeltaX;CurXdestra +=DeltaXdestra;LeftCurU+= RightDeltaU;CurvaVsinistra +=DeltaV destra;sinistraCurZ += destraDeltaZ;//aggiorna gli interpolatori Y
  • } fine del ciclo y

    //questa è la fine della prima parte.Ora abbiamo disegnato metà del triangolo.dall'alto, alla coordinata Y centrale.// ora facciamo sostanzialmente la stessa identica cosa, ma ora per la metà inferiore del triangolo (usando l'altro set di interpolatori)

scusate per le "linee fittizie"..erano necessari per sincronizzare i codici di ribasso.(Mi ci è voluto un po' per far sì che tutto apparisse come previsto)

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

Altri suggerimenti

Non so che posso aiutare con la vostra domanda, ma uno dei migliori libri sul rendering software che avevo letto, al momento è disponibile in linea Grafica Programmazione Black Book da Michael abrash.

Se state interpolando 1/z, è necessario moltiplicare UV/z da 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.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top