Interpolación de profundidad para z-buffer, con scanline

10

Tengo que escribir mi propio rasterizador 3D de software, y hasta ahora puedo proyectar mi modelo 3D hecho de triángulos en el espacio 2d:

Roto, traduzco y proyecto mis puntos para obtener una representación espacial en 2d de cada triángulo. Luego, tomo los 3 puntos del triángulo e implemento el algoritmo de línea de exploración (usando interpolación lineal) para encontrar todos los puntos [x] [y] a lo largo de los bordes (izquierda y derecha) de los triángulos, para poder escanear el triángulo horizontalmente, fila por fila, y llénalo con píxeles.

Esto funciona. Excepto que también tengo que implementar z-buffering. Esto significa que, conociendo las coordenadas z rotadas y traducidas de los 3 vértices del triángulo, debo interpolar la coordenada z para todos los demás puntos que encuentre con mi algoritmo de línea de exploración.

El concepto parece bastante claro, primero encuentro a Za y Zb con estos cálculos:

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

Luego, para cada Zp, hago la misma interpolación horizontalmente:

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

ingrese la descripción de la imagen aquí

Y si la z actual está más cerca del espectador que la z anterior en ese índice, ENTONCES escriba el color en el búfer de color Y escriba la nueva z en el búfer z. (mi sistema de coordenadas es x: izquierda -> derecha; y: arriba -> abajo; z: su cara -> pantalla de la computadora;)

El problema es que se vuelve loco. El proyecto está aquí y si selecciona el botón de opción "Z-Buffered", verá los resultados ... ( tenga en cuenta que utilizo el algoritmo del pintor (-solo- para dibujar el wireframe) en modo "Z-Buffered" para fines de depuración )

PD: He leído aquí que debes convertir las z en sus recíprocos (significado z = 1/z) antes de interpolar. Lo intenté y parece que no hay cambio. ¿Qué me estoy perdiendo? (¿Alguien podría aclarar, precisamente dónde debe convertir z en 1 / z y dónde (si) para volverlo?)

[EDITAR] Aquí hay algunos datos sobre los valores z máximos y mínimos que obtengo:

    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.

Antes de entrar en una depuración minuciosa, ¿alguien puede confirmar que mi concepto hasta ahora es correcto?

[EDIT2]

He resuelto el z-buffering. Como resultado, el orden de dibujo no fue desordenado en absoluto. Las coordenadas z se calculaban correctamente.

El problema era que, en un intento de aumentar mi velocidad de cuadros, dibujaba cuadros de 4 px / 4 px, cada 4 píxeles, en lugar de píxeles reales en la pantalla. Así que dibujaba 16 píxeles por píxel, pero comprobaba el búfer z solo para uno de ellos. Soy un bobo

TL / DR: La pregunta sigue en pie: ¿Cómo / por qué / cuándo tienes que usar el recíproco de Z (como en 1 / z) en lugar de Z? Porque en este momento, todo funciona de cualquier manera. (No hay diferencia notable).

Salto espectral
fuente
Re: "Y, por supuesto, agrego al zBuffer, si el z actual está más cerca del espectador que el valor anterior en ese índice". No está claro para mí que hayas dicho cómo querías pero querías asegurarte de que lo que querías decir era "si la z actual está más cerca del espectador que la z anterior en ese índice ENTONCES escribe el color en el búfer de color Y escribe el nueva z para el búfer z "El propósito del búfer z es bloquear las escrituras en color si un color en ese píxel ya se escribió más cerca del ojo de la cámara.
Alturis
Eso es correcto. Lo siento, era tarde cuando formulé mi pregunta. Lo revisaré.
Salto espectral

Respuestas:

5

Respuesta rápida: Z no es una función lineal de (X ', Y'), pero 1 / Z sí. Como interpola linealmente, obtiene resultados correctos para 1 / Z, pero no para Z.

No se da cuenta porque mientras la comparación entre Z1 y Z2 sea correcta, el zbuffer hará lo correcto, incluso si ambos valores son incorrectos. Definitivamente notará cuando agregue mapeo de textura (y para responder la pregunta que tendrá entonces: interpolar 1 / Z, U / Z y V / Z, y reconstruir U y V a partir de estos valores: U = (U / Z) / (1 / Z), V = (V / Z) / (1 / Z). Me lo agradecerás más tarde)

Un ejemplo. Consigue un pedazo de papel. Vista de arriba hacia abajo, así que olvide la coordenada Y. X es el eje horizontal, Z es el eje vertical, la cámara está en (0, 0), el plano de proyección es z = 1.

Considere los puntos A (-2, 2) y B (2, 4). El punto medio M del segmento AB es (0, 3). Hasta ahora tan bueno.

Proyecta A en A ': X' = X / Z = -1, entonces A 'es (-1, 1). Del mismo modo, B 'es (0.5, 1). Pero tenga en cuenta que la proyección de M es (0, 1), que NO es el punto medio de A'B '. ¿Por qué? Debido a que la mitad derecha del segmento está más lejos de la cámara que la mitad izquierda, se ve más pequeña.

Entonces, ¿qué sucede si intentas calcular la Z de M 'usando interpolación lineal? dx = (0.5 - -1) = 1.5, dz = (4 - 2) = 2, entonces para M 'donde X' = 0, la Z interpolada linealmente es zA + (dz / dx) (x - xA) = 2 + (2 / 1.5) (0 - -1) = 2 + 1.333 = 3.3333 - ¡NO 3!

¿Por qué? Porque por cada paso en la dirección X ', no se mueve la misma cantidad en la dirección Z (o, en otras palabras, Z no es una función lineal de X'). ¿Por qué? Debido a que cuanto más avanzas hacia la derecha, más lejos está el segmento de la cámara, por lo que un píxel representa una distancia más larga en el espacio.

Finalmente, ¿qué sucede si interpolas 1 / Z en su lugar? Primero calcula 1 / Z en A y B: 0.5 y 0.25 respectivamente. Luego interpola: dx = (0.5 - -1) = 1.5, dz = (0.25 - 0.5) = -0.25, entonces en X '= 0 calcula 1 / Z = 0.5 + (-0.25 / 1.5) * (0 - -1) = 0.3333. Pero eso es 1 / Z, por lo que el valor de Z es ... exactamente, 3. Como debería ser.

Sí, las matemáticas son increíbles.

ggambett
fuente
1
Ah, y con respecto a "cuándo": calcule los valores de 1 / Z antes de comenzar a rasterizar el triángulo (por ejemplo, justo antes del bucle vertical), para obtener 1 / Z interpolado a la izquierda y a la derecha de la línea de exploración. Interpola estos linealmente (NO hagas 1 / Z de nuevo, ¡los valores interpolados ya son 1 / Z!), Y deshace la transformación justo antes de comprobar el zbuffer.
ggambett
1
Y finalmente, por qué. Un plano (donde está incrustado el triángulo) es Ax + By + Cz + D = 0. z es una función claramente lineal de (x, y). Proyectas así x '= x / z e y' = y / z. A partir de ahí, x = x'z e y = y'z. Si los reemplaza en la ecuación original, obtendrá Ax'z + By'x + Cz + D = 0. Ahora z = -D / (Ax '+ By' + C), donde está claro que z no es una función lineal de (x ', y'). Pero 1 / z es por lo tanto (Ax '+ By' + C) / -D, que es una función lineal de (x ', y').
ggambett
1
Sabes, leí algunos artículos y cursos, y ninguno de ellos fue tan claro como tu respuesta. Para la posteridad, también notaré que "Las letras" U "y" V "denotan los ejes de la textura 2D porque" X "," Y "y" Z "ya se utilizan para denotar los ejes del objeto 3D en el modelo espacio. La textura UV permite que los polígonos que forman un objeto 3D se pinten con el color de una imagen ". - Wikipedia - Mapeo UV
Spectraljump
Me alegra escuchar eso. De hecho,
enseñé
Muchas gracias, siempre he tenido curiosidad por esto, ¡y no sé si alguna vez habría encontrado una mejor respuesta! +1
Codesmith