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);
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).
fuente
Respuestas:
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.
fuente