2010-01-14 6 views
6

Estoy haciendo un rasterizador de software, y me he encontrado con un pequeño inconveniente: parece que no puedo obtener un mapeo de texturas correcto en perspectiva para que funcione.Correlación de textura correcta de perspectiva; z El cálculo de la distancia z puede ser incorrecto

Mi algoritmo es primero ordenar las coordenadas para trazar por y. Esto devuelve un punto más alto, más bajo y central. entonces camino a través de las líneas de exploración utilizando el 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; 

Y entonces podremos dar order[0]->y-order[2]->y, el aumento de la x_start y x_end por un delta. Al representar la parte superior, los delta son x_delta[0] y x_delta[1]. Al representar la parte inferior, los delta son x_delta[0] y x_delta[2]. Luego, interpolamos linealmente entre x_start y x_end en nuestra línea de exploración. Las coordenadas UV se interpolan de la misma manera, ordenadas por y, comenzando al principio y al final, a las cuales se aplican delta en cada paso.

Esto funciona bien, excepto cuando trato de hacer un mapeo de UV correcto en perspectiva. El algoritmo básico es tomar UV/z y 1/z para cada vértice e interpolar entre ellos. Para cada píxel, la coordenada UV se convierte en UV_current * z_current. Sin embargo, este es el resultado:

alt text

La parte Inversed le dice a donde el delta de se voltean. Como puede ver, los dos triángulos parecen dirigirse hacia diferentes puntos en el horizonte.

Esto es lo que uso para el cálculo de la Z en un punto en el espacio:

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; 
} 

Estoy en lo cierto, ¿es un problema de memoria intermedia z?

Respuesta

4

ZBuffer no tiene nada que ver con eso.

El ZBuffer solo es útil cuando los triángulos se superponen y desea asegurarse de que estén dibujados correctamente (por ejemplo, ordenados correctamente en la Z). El ZBuffer determinará, por cada píxel del triángulo, si un píxel colocado previamente está más cerca de la cámara y, de ser así, no dibujará el píxel de su triángulo.

Dado que está dibujando 2 triángulos que no se superponen, este no puede ser el problema.

He hecho un rasterizador de software en punto fijo una vez (para un teléfono móvil), pero no tengo las fuentes en mi computadora portátil. Así que déjenme revisar esta noche, cómo lo hice. ¡En esencia, lo que tienes no es malo! Algo así podría deberse a un error muy pequeño

Consejos generales para depurar esto es tener algunos triángulos de prueba (pendiente en el lado izquierdo, pendiente en el lado derecho, ángulos de 90 grados, etc.) y recorrerlo con el depurador y vea cómo su lógica se ocupa de los casos.

EDIT:

peudocode de mi impresora de trama (sólo U, V y Z son tomados en cuenta ...si también quieres hacer gouraud, también tienes que hacer todo lo que estás haciendo para U y V y Z:

La idea es que un triángulo se puede dividir en 2 partes. La parte superior y la parte inferior. La parte superior es de y [0] a y [1] y la parte inferior es de y [1] a y [2]. Para ambos conjuntos, debe calcular las variables de paso con las que está interpolando. El siguiente ejemplo muestra cómo hacer la parte superior. Si es necesario, también puedo suministrar la parte inferior.

Tenga en cuenta que yo ya calcular las compensaciones de interpolación necesarios para la parte inferior en el fragmento a continuación 'pseudo'

  • de primer orden las coordenadas (x, y, z, u, v) en el orden para que coord [0] .y < coord [1] .y < coord [2] .y
  • siguiente compruebe si 2 juegos de coordenadas son idénticos (solo marque xey). Si es así, no dibuje
  • excepción: ¿tiene el triángulo una parte superior plana? si es así, la primera pendiente será infinita
  • exception2: el triángulo tiene un fondo plano (sí, los triángulos pueden tener estos también; ^)) entonces la última pendiente también será infinita
  • calcule 2 pendientes (lado izquierdo y lado derecho)
    leftDeltaX = (x [1] - x [0])/(y [1] -y [0]) y rightDeltaX = (x [2] - x [0])/(y [2] -y [0])
  • la segunda parte del triángulo se calcula depende de: si el lado izquierdo del triángulo es ahora realmente en el lado izquierdo (o necesidades del intercambio) fragmento

código:

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 
    } 
  • establezca la currentLeftX y currentRightX tanto en x [0]
  • conjunto currentLeftU en leftDeltaU, currentLeftV en leftDeltaV y currentLeftZ en leftDeltaZ
  • calc iniciar y punto final para primera gama Y: starty = ceil (y [0]); endY = ceil (y [1])
  • prestep x, u, v y z para la parte fraccional de y para la precisión de subpíxeles (supongo que esto también es necesario para flotadores) Para mis algoritmos de punto fijo esto fue necesario para hacer líneas y texturas dan la ilusión de moverse en pasos mucho más finos que la resolución de la pantalla)
  • calcule donde x debe estar en y [1]: halfwayX = (x [2] -x [0]) * (y [ 1] -y [0])/(y [2] -y [0]) + x [0] y lo mismo para U, V yz: halfwayU = (u [2] -u [0]) * (y [1] -y [0])/(y [2] -y [0]) + u [0]
  • y utilizando la mitad de camino X calcule el paso a paso para U, V yz: if (halfwayX - x [1] == 0) {slopeU = 0, slopeV = 0, slopeZ = 0} else {slopeU = (halfwayU - U [1])/(halfwayX - x [1])}// (y lo mismo para v y z)
  • recorte para la parte superior Y (así que calcula dónde vamos a empezar a dibujar en caso de que la parte superior del triángulo esté fuera de pantalla (o fuera del rectángulo). para y = startY; y < endY; y ++) {
    • ¿está Y al final de la pantalla? dejar de renderizar!
    • calc startX y endX para la primera línea horizontal leftCurX = ceil (startx); leftCurY = ceil (endy);
    • clip de la línea que deben extraerse de la frontera horizontal izquierda de la pantalla (o región de recorte)
    • preparar un puntero a la memoria intermedia de destino (hacerlo a través de los índices de matriz cada vez es demasiado lento) unsigned int buf = destbuf + (y pitch) + startX; (Unsigned int en caso de que lo están haciendo de 24 bits o 32 bits renderizado) también preparar el puntero ZBuffer aquí (si está usando este)
    • para (x = startX; x < EndX; x ++) {
      • ahora para el mapeo perspectiva textura (sin interpolación usando bilineair hace lo siguiente):

fragmento de código:

  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 
  • línea ficticia
    • línea ficticia
      • línea ficticia
      • opcional: compruebe la ZBuffer si el píxel representados previamente en esta coordenada es mayor o menor entonces la nuestra.
      • trazar el píxel
      • startZ + = slopeZ; startU + = slopeU; startV + = slopeV; // actualización todos los interpoladores
    • } final de x bucle
    • leftCurX + = leftDeltaX; rightCurX + = rightDeltaX; leftCurU + = rightDeltaU; leftCurV + = rightDeltaV; leftCurZ + = rightDeltaZ; // interpoladores actualización Y
  • } final de y bucle

    // esto es el final de la primera parte. Ahora hemos dibujado la mitad del triángulo. desde la parte superior, hasta la coordenada Y media. // ahora, básicamente, hacen exactamente lo mismo, pero ahora para la mitad inferior del triángulo (usando el otro juego de interpoladores)

lo de las 'líneas ficticias' .. que se necesitaban para obtener el códigos de descuentos en sincronización. (tardé un poco en ordenar todo para que parezca como estaba previsto)

¡Avísame si esto te ayuda a resolver el problema al que te enfrentas!

+0

Otro consejo para la depuración, calcule cada punto de textura utilizando las anticuadas coordenadas U y V no incrementales para cada píxel y compárelas con los valores incrementales calculados por su algoritmo (lo que permite algunas fallas debido al error de redondeo). –

+1

Guau ... ¡eso es una respuesta y media! : D – Goz

+0

goz: ahora solo espero que sea útil, ya que probablemente ya tiene la mayor parte de este código establecido a juzgar por la imagen. Estoy pensando que probablemente calculó mal uno de los valores de interpolación. Pero, con un poco de suerte, si pone su algoritmo al lado del mío, puede descubrir cuál es; ^) – Toad

0

No sé si puedo ayudarte con tu pregunta, pero uno de los mejores libros sobre representación de software que había leído en ese momento está disponible en línea en Graphics Programming Black Book de Michael Abrash.

0

Si está interpolando 1/z, necesita multiplicar UV/z por z, no 1/z.Asumiendo que tiene esto:

UV = UV_current * z_current

y z_current se 1/z interpolación, se debe cambiar a:

UV = UV_current/z_current

Y entonces puede que desee cambiar el nombre a algo así como z_currentone_over_z_current.

+0

Probablemente sea un truco acelerado: necesita dividir por z. Pero como la multiplicación es más rápida que la división, se multiplica por 1/z. – Toad

+0

No se puede evitar una división con una asignación de textura de perspectiva correcta. Puede aproximarlo sobre un spam de píxeles, pero no puede eliminarlo. Asumiendo que quieres algo que se vea correcto. – MSN