Página sobre Pseudo 3d de LouTraducción: Luis Peña(C) 2013 Louis Gorenfeld, actualizado el 3 de Mayo del 2013 (Traducción: Luis Peña) NUEVO: Detalles importantes del sistema de carretera segmentada y algunos enlaces adicionales NUEVO: Una explicación (opcional) acerca de encontrar el campo-de-visión para la fórmula de proyección 3d NUEVO: Un análisis de S.T.U.N. Runner NUEVO: Mejoras generales de escritura Actualización previa: Tabla de contenidosIntroducciónLas Bases de la Carretera Dirección y curvas Sprites y Datos Colinas Llevando a las carreteras rasterizadas más allá Segmentos proyectados en 3d real Mejoras Efectos Relacionados Casos de Análisis Asuntos del código Glosario La galería IntroducciónPor qué Pseudo 3d? Pero también tienen muchos inconvenientes. La profundidad de las físicas de los juegos de simulación tiende a perderse. Estos motores no son adecuados para todos los propósitos. Sin embargo, son fáciles y rápidos de implementar, ejecutar, y son generalmente muy divertidos a la hora de jugar! Vale la pena señalar que no todos los juegos de carreras antiguos usaron estas técnicas. De hecho, el método que se muestra aquí es sólo una forma posible de hacer una carretera pseudo 3d. Algunos utilizan sprites proyectados y escalados, otros parecen implicar diferentes grados de proyección real para el camino. Cómo tú mezclas las matemáticas con el "engaño" depende de ti. Espero que se diviertan tanto explorando este efecto especial como yo lo hice. Cúanta matemática necesito? Esta es una técnica bastante flexible, y que realmente puedes lograr con tan sólo sumar! Con matemática más avanzada,puede ser mejor, pero solamente con la aritmética, puedes alcanzar el nivel de detalle visto en juegos como Pole Position o el primer OutRun. Cuántos conocimientos de programación necesito? Listo? Comencemos! Efectos rasterizados - Un pantallazo Las Bases de las CarreterasIntroducción a las carreteras rasterizadas El pseudo efecto rasterizado de la carretera funciona realmente de forma similar al efecto de perspectiva de Street Fighter II , en que deforma una imagen estática para crear la ilusión de 3d. Así es como se hace: La mayoría de las carreteras rasterizadas comienza con la imagen de una carretera plana. Esto es esencialmente un gráfico de dos líneas paralelas en el suelo perdiéndose en la distancia. A medida que estas líneas se pierden en la distancia, parecen estar más juntas para el espectador. Esta es la regla básica de la perspectiva. Ahora, para dar la ilusión de movimiento, la mayoría de los juegos arcade de carrera tienen rayas en la carretera. Moviendo estas rayas hacia adelante es generalmente o bien logrado con ciclo de color, o bien cambiando la paleta en cada línea. Las curvas y direcciones se hacen desplazando cada línea independientemente de la otra, así como en Street Fighter II. Abordaremos las curvas y direcciones en el siguiente capítulo. Por ahora, dejémoslas a un lado y concentrémonos en hacer que la carretera parezca desplazarse hacia adelante. La Carretera más Simple Lo que no aparece en esta imagen son las marcas de la carretera para dar la impresión de distancia. Los juegos usan rayas iluminadas y oscuras de forma alternada, entre otras marcas de la carretera, para este efecto. Para hacerlo más complejo, definamos una variable "texture position". Esta variable comienza en cero al fondo de la pantalla y se incrementa por cada línea hacia arriba de la pantalla. Cuando está por debajo de una cierta cantidad, la carretera es dibujada en un sombreado. Cuando supera esa cantidad, se dibuja en el otro tono. La variable de posición vuelve a 0 cuando supera la cantidad máxima, causando un patrón repetitivo. No alcanza con cambiar esto por una cierta cantidad en cada línea, pues entonces verás varias rayas de diferentes colores que no se hacen más pequeñas a medida que la carretera se pierde en la distancia. Esto significa que necesitas otra variables que cambien por una determinada cantidad, lo sumen a la otra variable por cada línea, y luego se sume la ultima al cambio en la posición de la textura. Aquí hay un ejemplo que muestra, desde el fondo de la pantalla, lo que será el valor Z para cada línea a medida que se pierden en la distancia. Luego de las variables, imprimo lo que se le suma para obtener los valores de la siguiente línea. Nombré los valores DDZ (delta delta Z), DZ (delta Z), y Z. DDZ se mantiene constante, DZ cambia de forma lineal, y Z es el valor de las curvas. Puedes pensar en Z como la representación de la posición Z, DZ lo que mantiene la velocidad de la posición, y DDZ la aceleración de la posición (el cambio en la aceleración). El valor que elegí, cuatro, es arbitrario y conveniente para este ejemplo.
Fíjate que DZ es el primero en modificarse, y luego se usa para modificar Z. Para resumir, diremos que te estás moviendo a través de la textura a una velocidad de 4. Eso significa que luego de la primera línea, estarás leyendo la textura en la posición 4. La siguiente línea será 12. Luego,24. Así, de esta manera se lee a través de la textura rápido y más rápido. Por eso nos referimos a estas variables como la posición de la textura (dónde estamos leyendo dentro de la textura), la velocidad de la textura (qué tan rápido leemos a través de la textura), y la aceleración de la textura (qué tan rápido se incrementa la velocidad de la textura).
Un método similar también puede ser utilizado para dibujar curvas y colinas sin demasiados números. Ahora, para hacer que la carretera parezca moverse, sólo cambia dónde comienza la posición de la textura al fondo de la pantalla para cada frame. Ahora, puedes notar un inconveniente con este truco: la tasa de zoom no es precisa. Esto causa una distorsión a la que me referiré como el "efecto harina de avena". Es un efecto de deformación presente en los primeros juegos como OutRun donde los objetos, incluyendo las rayas en la carretera, parecen desacelerar a medida que se mueven hacia afuera del centro de la pantalla. Este método para encontrar el valor de Z tiene otra desventaja: No es fácilmente predecible el valor correspondiente a cada distancia, especialmente cuando incluimos las colinas. Aprenderemos un método más avanzado al que llamaré el "Z-map". Ésta es una tabla que calcula la distancia Z para cada scanline de la pantalla. Pero primero, necesitamos un poco más de matemática... Un Desvío Matemático: Proyección de Perspectiva 3d
En la imagen de arriba, un globo ocular (abajo a la izquierda) está mirando, a través de la pantalla (la línea vertical azul), un objeto en nuestro mundo 3d ("y_world"). Este ojo está a una distancia "dist" de la pantalla, y a una distancia "z_world" del objeto. Ahora, una cosa que podrías haber notado si pensanste en la geometría o trigonometría es que no hay uno sino dos triángulos en la imagen. El primero es el más grande, del ojo sobre el suelo del lado derecho y arriba al objeto al que estamos observando. El segundo triángulo lo he coloreado amarillo. Está desde el ojo hacia la parte de la pantalla donde veremos nuestro objeto, debajo del suelo , y detrás. Las hipotenusas de ambos triángulos (la línea desde el ojo al objeto) están en el mismo ángulo incluso aunque una sea más larga que la otra. Son esencialmente el mismo triángulo, pero el menor está reducido. Lo que esto implica es que el radio de los lados horizontales como los verticales debe ser el mismo! En términos matemáticos:
Lo que necesitamos hacer ahora es acomodar la ecuación para obtener el valor de y_screen. Eso nos da:
En resumen, para encontrar la coordenada y de un objeto en la pantalla, tomamos la coordenada y world, la multiplicamos por la distancia a la que estamos de la pantalla, y luego se divide eso por la distancia a la que se encuentra en el mundo. Por supuesto, si sólo hacemos eso, el centro de nuestra vista estará en la esquina superior izquierda de la pantalla! Sólo iguala y_world a 0 para verlo. Lo que podemos hacer para centrarla es sumarle la mitad de nuestra resolución de pantalla al resultado. Esta ecuación también puede ser simplificada un poco asumiendo que nuestras narices están justo sobre la pantalla. En tal caso, dist=1. Por lo tanto, la ecuación final es:
Hay una relación entre los radios y el ángulo de visión, así como también el escalar la imagen de forma que su resolución sea neutral, pero no necesitaremos realmente nada de eso para arreglar el problema de nuestra carretera. Si eres curioso, intenta mirar el diagrama que está visto desede arriba: el ángulo al borde de la pantalla es nuestro campo de visión y se mantiene la misma relación! Más Matemática: Agregando el Campo-de-Visión la Proyección 3d Volvamos a la fórmula de proyección original. La distancia "dist" que explicamos arriba ahora será llamada "scaling":
La idea es que necesitamos escalar todos los puntos de la pantalla por algún valor que permite que aquellos puntos que están dentro de un determinado campo-de-visión (FOV) permanezcan visibles. Necesitarás una constante para el valor x del FOV y otra para el valor y. Como ejemplo, asumamos que estamos trabajando en una resolución de 640x480 y queremos un FOV de 60 grados. Hemos visto un diagrama de
proyección 3d vista de forma lateral. Para esto, en su lugar echemos un vistazo a la vista cenital del espacio
de proyección:
Hemos reemplazado a/2 por 30 (la mitad de 60 grados), reconociendo que sin/cos = tan, y voilá! Debemos ser capaces de probar esto colocando un objeto en el borde derecho del campo-de-visión, estableciendo estos valores en la ecuación de proyección original, y asegurándonos que el valor X termina como 640. Por ejemplo, un punto (x, z) en (20, 34.64) caerá en X=640 porque 20 es 40*sin(30) y 34.64 es 40*cos(30). Nota que tendrás diferentes valores del FOV para el horizontal (x) y el vertical (y) para un monitor estándar o panorámico con orientación horizontal. Una Carretera Más Precisa - Usando un Z Map Primero, toma la ecuación de la última sección:
Ahora, ya que obtuvimos Y_screen (cada línea), acomoda la ecuación de forma tal que encontremos la Z:
Y_world es básicamente la diferencia entre el suelo y la altura de la cámara, que será negativa. Esto aplica para cada línea porque, como describimos en el párrafo de introducción, por ahora estamos interesados en una carretera llana. Además de verse mucho más precisa y evitar el "efecto de harina de avena", tiene la ventaja de que es fácil de computar cuál es la distancia máxima a dibujar. La carretera es mapeada en la pantalla leyendo este buffer: Para cada distancia, debes saber qué parte de la textura de la carretera pertenece a allí a partir de cuántas unidades de franja o pixel de la textura requiere.
Aunque ahora sabemos la distancia de cada fila de la pantalla, puede también
ser útil cachear ya sea la anchura de la carretera o el factor de escalado para cada
línea. El factor de escala sólo sería la inversa
de la distancia, ajustado para que el valor sea 1 en la línea
donde el vehículo del jugador pase la mayor parte del tiempo. Este valor puede entonces
ser utilizado para escalar los sprites da una línea dada, o para
hallar la anchura de la carretera.
Dirección y CurvasHaciéndola Curva No obstante, hay algunos problemas con este método. Uno es que las
curvas-S no son convenientes. Otra limitación que tiene entrar en la curva y que tambien
se encuentra al salir de ésta: la carretera se curva, y simplemente
se endereza. Para mejorar la situación, introduciremos la idea de segmentos de carretera. Un segmento es una partición la cual es invisible al jugador. Piensa en ello como una división horizontal invisible que establece la curva de la carretera por encima de esa línea. En cualquier momento, uno de estos segmentos divisores está al fondo de la pantalla y otro viene desplazándose hacia el fondo en una tasa que se incrementa continuamente. Lllamaremos al que se encuentra al fondo el segmento base, ya que establece el inicio de la curva de la carretera. Aquí se ve cómo funciona: Cuando comenzamos a dibujar la carretera, la primero que hacemos es mirar el punto base y establecer los parámetros para dibujar en consecuencia. Cuando una curva se aproxima, la línea del segmento comenzaría en la distancia y viene hacia el jugador como cualquier otro objeto de la carretera, con la excepción de que necesita descender por la pantalla en una tasa continua. Es decir, para una velocidad específica a la que el jugador está viajando, el segmento divisor desciende por la pantalla a algunas líneas por frame. O, si está utilizando un Z-Map, unas tantas entradas del z-map por frame. Si el segmento fuera a 'acelerar' hacia el jugador tal como lo hacen los objetos 3d en la pista, la carretera oscilaría salvajemente. Veamos cómo funciona todo esto. Suponiendo que la línea del segmento para una curva hacia la izquierda está a medio camino en la carretera y el segmento base es sólo una carretera recta. A medida que la carretera es dibujada, no empieza a curvarse hasta que no "impacte" con el segmento de "curva izquierda". Luego la curva de la carretera comienza a cambiar a la tasa especificada para ese punto. A medida que el segmento que se mueve llega al fondo de la pantalla, se convierte en el nuevo segmento base y el que previamente era el base vuelve a la cima de la carretera. Debajo se muestran dos carreteras: Una es una recta seguida por una curva a izquierda, y la otra es una curva a izquierda seguida por una recta. En ambos casos, la posición del segmento está a medio camino en el Z Map (o, a medio camino en la pantalla). En otras palabras, la carretera comienza a curvarse o enderezarse a medio camino en la carretera, y la cámara está entrando en la curva en la primera imagen y saliendo de ésta en la segunda.
Y esta es la misma técnica y la misma posición del segmento aplicadas a una curva-S: La mejor forma de realizar un seguimiento de la posición del segmento es en términos de dónde está en el Z Map. Es decir, en lugar de asociar la posición del segmento a una coordenada y de la pantalla, asóciala a una posición en el Z Map. De esta manera, aún comenzará en el horizonte de la carretera, pero será más capaz de manejar las colinas. Nota que en una carretera llana ambos métodos de realizar un seguimiento de la posición del segmento son equivalentes. Ilustrémoslo con algo de código:
Una gran ventaja de hacer las curvas de esta forma es que si tu tienes una curva seguida de una recta, puedes ver cómo la recta viene hacia ti luego de la curva. De esta manera, si tienes una curva de seguida de otra curva en la dirección opuesta (o incluso una curva más cerrada en la misma dirección), puedes ver la suguiente fracción de pista que se aproxima sobre la curva antes de que te la encuentres. Para completar la ilusión, necesitas tener un gráfico del horizonte. A medida que la curva se aproxima, el horizonte no cambia (o hace un scroll muy suave). Luego cuando la curva está completamente dibujada, asume que el auto está yendo de forma curvada, y el horizonte realiza un scroll rápidamente en la dirección opuesta a la que la curva apunta. A medida que la curva se vuelve a enderezar, el fondo continua desplazándose hasta que la curva se completa. Si estás usando segmentos, puedes solamente desplazar el horizonte de acuerdo a las configuraciones del segmento base. Fórmula General de Curva Viendo el ejemplo de curva utilizando la "Z" de la sección "La Carretera más Simple", podemos ver que la posición z (o la posición x) de una línea dada es la suma de una sucesión incremental de números (ej. 1 + 2 + 3 + 4). Esto es lo que llamamos una serie aritmética, o progresión aritmética. En lugar de 1 + 2 + 3 + 4, una curva más pronunciada se puede producir sumando 2 + 4 + 6 + 8, o 2*1 + 2*2 + 2*3 + 2*4. El "2" en este caso es la variable segment.dx de arriba. Puede también ser factorizado para obtener 2(1 + 2 + 3 + 4)! Ahora todo lo que se debe hacer es hallar una fórmula para describir 1 + 2 + ... + N, donde N es el número de línea que conforman la curva. Resulta que la suma de una serie aritmética es igual a N(N+1)/2. Entonces, la fórmula se puede escribir como s = A * [ N(N+1)/2 ] donde A es la pronunciación de la curva y s es la suma. Esto además se puede modificar para agregar un punto de inicio, por ejemplo, el centro de la carretera al fondo de la pantalla. Si llamamos esto 'x', tenemos ahora s = x + A * [ N(N+1)/2 ]. Tenemos ahora una fórmula para describir nuestra curva. La pregunta que queremos resolver es, "dado un punto inicial x y N líneas de la curva, cuánto debería valer A para que la curva alcance la posición x 's' al terminar?" Acomodando la ecuación para despejar A obtenemos A = 2(s - x)/[n(n+1)]. Esto significa que la pronunciación de la curva de una curva dada puede ser almacenada en términos de la posición X del punto final, haciendo al motor gráfico independiente de la resolución. Direccionamiento Con Estilo de Perspectiva Sprites y DatosColocando y Escalando los Objetos La posición X del objeto debe ser registrado con relación al centro de la carretera. La forma más sencilla para posicionar el sprite horizontalmente es sólo multiplcarlo por el factor de escalado para la línea actual (el inverso de Z) y sumarlo al centro de la carretera. Almacenando Datos de la Pista Si, no obstante, estás utilizando un sistema segmentado, puedes usar una lista de comandos. La distancia que cada comando requiere es equivalente a cuán rápido desciende en pantalla el segmento invisible. Esto también te deja crear un formato de pista que trabaje en un tile map, para representar alguna geografía realista de la pista. Es decir, cada tile podría ser un segmento. Una curva pronunciada podría doblar la pista 90 grados, mientras una curva más suave lo haría en 45 grados. Texturizando la Carretera Si quieres que la carretera se vea más precisa, haz que el valor Z para cada línea corresponda con un número de fila en un gráfico de textura. Voila! Un carretera texturizada! Sin embargo, si sólo quieres franjas de colores alternadas, la respuesta
es incluso más simple-- especialmente cuando se usa punto fijo. Para cada Z, haz que
uno de los bits
represente el tono de la carretera (oscuro o claro). Luego, sólo dibuja el patrón
de carretera apropiado o los colores para ese bit.
Colinas
Variedades de Colinas
Colinas Falsas
Así es cómo se hace: Primero que nada, el bucle de dibujo comenzaría al comienzo del Z Map (lo más cercano) y pararía cuando alcance el final (lo más lejano). Si decrementamos en 1 la posición de dibujo en cada línea, la carretera se dibujará llana. Sin embargo, si decrementamos la posición de dibujo en 2, duplicando las líneas, la carretera será dibujada dos veces más alta. Finalmente, variando la cantidad que decrementamos la posición de dibujo en cada línea, podemos dibujar una colina que comienza llana y se curva hacia arriba. Si la siguiente posición de dibujo está a más de una línea de diferencia de la posición de dibujo actual, la línea del Z Map actual es repetida hasta que lleguemos a ese valor, produciendo un efecto de escalado. Las pendientes son similares: Si la posición de dibujo es incrementada en lugar de decrementada, se moverá debajo de la última línea dibujada. Por supuesto, las líneas que están debajo del horizonte no serán visibles en-pantalla-- sólo las que están 1 o más píxeles arriba de la última línea deben ser dibujadas. Sin embargo, vamos a querer aún mantener un registro o seguimiento de los objetos que están debajo del horizonte. Para hacerlo, mantén la posición Y en pantalla de cada sprite a medida que recorres el Z Map. Puede ser útil hacer el Z Map más grande que lo necesario para una carretera llana. De esta forma, cuando el buffer se expanda, no se volverá tan pixelada. Ahora, tenemos que mover el horizonte para convencer al jugador. Me gusta usar un background del estilo de Lotus, en donde el horizonte no consiste sólo en una línea de horizonte (skyline), sino también un gráfico de un terreno distante. Cuando la colina se curva hacia arriba (elongando la vista), el horizonte debe moverse hacia abajo ligeramente con relación al tope de la carretera. Cuando la colina se curva hacia abajo a medida que la cámara avanza sobre la cresta de ésta (acortando la vista), el horizonte debe moverse hacia arriba. Así es como luce el efecto para una cuesta arriba y hacia abajo-- menos el gráfico del horizonte por supuesto:
Pros
Llevando a las Carreteras Rasterizadas Más AlláEsas fórmulas del estilo de sumatoria se pueden usar textualmente si no necesitas curvas locas o grandes, o colinas rodantes. Muchos juegos que usan esos tipos de trucos desplazan la carretera tan rápido que incluso una curva suave puede ser convincente.No obstante, puedes queres exagerar el efecto para obtener una carretera más dramática. Una cosa que se puede hacer con algunas de aquellas fórmulas es usar valores altos para ddx o ddy, pero sin permitir que dx o dy excedan un valor sensato. Y un usuario en YouTube, Foppygames, ha descubierto otro truco para obtener curvas más exageradas a partir de dichas fórmulas: multiplicar el valor de dx o dy por el valor z para cada línea! Esto hace más severa a la curva en la distancia que adelante, y crea un efecto muy convincente. Además, la experimentación no acaba ahí. De hecho, lo mejor de estos motores es que no hay una forma "correcta" de hacerlo. Lo que sea que cree curvas y deformaciones que sean agradables al ojo está permitido! En mis primeros motores, usé una tabla de búsqueda sinusoidal para curvar la carretera. Puedes también usar la multiplicación: Para doblar la carretera a la derecha, puedes multiplicar la posición x por, por ejemplo, 1.01 cada línea. Para moverla a la izquierda la misma cantidad, podrías multiplicarla por 0.99, o 1/1.01 (recíproco de 1.01). Sin embargo, armado con el conocimiento de que varios procesadores antiguos no tenían multiplicación o eran muy lentos en ello, decidí usar la técnica de acumulación porque sólo usa suma. Parecía la forma más "auténtica" de curvar la carretera. Algunos juegos, como OutRun, utilizan incluso un sistema de spline simple-- al menos a juzgar por el gran port hecho con ingeniería inversa y open-source en C++, Cannonball. Por lo tanto, juega y experimenta, y mira qué técnica te gusta más! ...o, lee la descripción de un truco más inteligente que mezcla polígonos 3d, es casi tan rápido, es incluso más convincente,
y puede ser presentado con el mismo hardware "oldschool" de rasterizado. Intrigado?
Segmentos Proyectados en 3D Real
Segmentos Proyectados en 3d vs. Carreteras Rasterizadas Este truco es conocido por haber sido usado en juegos tales como Road Rash y Test Drive II: The Duel. Aquí está cómo se hace: La pista está hecha de segmentos poligonales. No obstante, en lugar de moverse en un espacio 3d completo, aún lo hace moviéndose hacia la cámara. Para las curvas, la carretera aún se dobla a izquierda y derecha en una forma casi idéntica a la rasterizada: no hay rotación real cuando se va por una curva como lo habría en un motor de puros polígonos. Aquí hay un resumen:
La Carretera 3D Básica
Debajo hay una carretera segmentada hecha de algunos pocos polígonos por lo que podemos ver fácilmente los límites entre los segmentos y cómo
afecta esto a la curvatura de la carretera: Al renderizar, primero encuentra la ubicación y de la pantalla de cada segmento tridimensional al usar la fórmula screen_y = world_y/z. O, si la división en demasiado lenta, puedes hallar la altura sobre el suelo de un segmento dado multiplicando la altura del segmento por el factor de escalado para esa línea. Eso podría luego ser sustraída de un Z-Map inverso (este mapa sería: para cada posición z de una carretera llana, cuál es su y?) para hallar la posición final en pantalla. Luego, podrías interpolar linealmente la anchura de la carretera y la textura (si lo deseas) entre aquellas alturas. Decidir qué segmentos tridimensionales dibujar y cuáles no puede ser determinado fácilmente: Desde el frente hacia atrás en la pantalla, un segmento 3d cuyo screen_y es proyectado más bajo que el último segmento dibujado no se mostraría (sin embargo, sus sprites podrían aún ser visibles porque éstos se mantienen-- tenlo en cuenta).
Desplazando la Carretera Pero hay un último detalle muy importante: digamos que la carretera es una curva pronunciada. Podrías haber notado que a medida que vas por esta curva poligonal, ésta se agita cuando cruzas el límite del segmento y la carretera se reestablece posteriormente. Una cosa obvia que ocurre y que causa esto es que cuando atraviesas un segmento sesgado, el centro de la cámara relativo a la carretera cambia. Es decir, cuando llegues al final de ese segmento, la carretera ya no estará centrada. Es como si estuvieras conduciendo en la carretera en un ángulo. Podrías estar tentado de reparar esto moviendo la carretera hasta centrarla tal como las posiciones x de los objetos son interpoladas linealmente. Sin embargo, esto está mal y no resuelve completamente nuestro problema: Si la carretera fuese sesgada en línea recta, estaría bien. El problema es que nuestra carretera se curva, por lo que los polígonos en la distancia aún no está alineados! Otra forma de pensar acerca de esto es la siguiente: Estamos aproximándonos a una curva usando segmentos poligonales. Lo que queremos es que la forma de la curva sea más o menos constante incluso cuando se desplaza. Jake de codeincomplete.com tiene una gran solución para esto. En lugar de cambiar la posición x de la carretera cuando te mueves a través del segmento, que hay si cambiamos el valor dx inicial de 0 a algo que mantenga la carretera en línea a medida que te mueves por el segmento? La fórmula usada es esta:
Este porcentaje tiene que ir de 0 a 1.0 y hacia atrás cuando la cámara cruza la cámara. En términos matemáticos, hacer esto hace a la X de la carretera una función de su Z. En otros términos, estamos manteniendo la curva en la misma forma a pesar de cómo se desplazan los puntos que la aproximan. El segmento más frontal es "estirado en posición" con el resto de la carretera, lo que significa que las posiciones X de los segmentos subsecuentes están correctamente ubicados. Puedes verlo claramente si lo pruebas con una carretera hecha de algunos polígonos. Esto resuelve los siguientes problemas a medida que el segmento es atravesado (asumiendo que la forma de la curva no cambia):
El siguiente video demuestra esta técnica. He utilizado algunos segmentos y una curva muy pronunciada para demostrar cómo luce. Observa que a medida que los polígonos se mueven hacia el jugador, describen una curva perfecta. Es más notorio si observas el lado derecho de la carretera.
Colocando Sprites Pero si un sprite se encuentra en un segmento que es invisible o parcialmente visible, podemos recortarlo fácilmente. Primero, encuentra la parte superior del sprite. Luego, cada línea del sprite será dibujada hasta que toque la posición Y en pantalla del último segmento visible. Es decir, si hay un segmento detrás del sprite que se supone debe cubrir parte de este, paras de dibujar ese sprite cuando tocas esa línea. Y si la parte superior del sprite está debajo de la posición Y del último segmento, el sprite no será visible en absoluto y será ignorado.
Variaciones y Tecnologías de Renderizado Observa que el comienzo y final de cada segmento de la carretera son perfectamente horizontales. Esto significa que siempre comienzan y terminan en una scanline específica. De la misma forma en que la carretera completamente pseudo 3d es renderizada en hardware para tiles desplazando el gráfico de la carretera plana a medida que está siendo dibujado, podemos hacer exactamente lo mismo con los segmentos 3D. Para mayor información, revisa la sección llamada Hardware Dedicado para Carreteras. Aunque habla de harware arcade diseñado desde un principio para lograr efectos de carretera, la misma técnica puede ser lograda con sistemas básicos de sprites/tiles 2D, desplazando el gráfico de la carretera verticalmente como también horizontalmente.
Más Información acerca de Segmentos Proyectados en 3D Pros
MejorasMúltiples Carreteras
Además, incluso más dramático, debajo está la autopista cuando las dos carreteras están superpuestas para formar seis carriles, con la segunda carretera como sin ella: Efectos RelacionadosTablero Interminable Debajo se ilustra este efecto de tablero de Space Harrier con y sin cambios en la paleta. Para convertir esto en un tablero, todo lo que hay que hacer es intercambiar la paleta cada unas pocas líneas. Piensa en esto como una analogía con las franjas oscuras y claras de la carretera.
Entonces, cómo se desplaza a izquierda y derecha? Utilizando sólo una variación de la curva en perspectiva: A medida que el
jugador se mueve a la izquierda/derecha, el gráfico del suelo se distorsiona. Luego del desplazamiento de algunos pixeles, el suelo
"restaura" su posición. Así es como parece que se desplaza infinitamente hacia izquierda y derecha.
Casos de Análisis
Hardware Dedicado para Carreteras Primero que nada, el chip tiene su propia memoria de gráficos. En esta ROM se almacena una vista en perspectiva aproximada de la carretera, dado que es llana, centrada, y sin curvarse. Luego, para cada línea de la pantalla, el programador especifica manualmente qué línea del gráfico dibujar. Cada línea tiene también un offset de X (para curvar la carretera) y cada línea puede tener también un valor de color diferente (para dibujar marcas de carretera y simular movimiento). Para ejemplificarlo, aquí hay algunas imágenes de los gráficos de la carretera de juegos de carreras de Sega junto con la carretera tal como se ve en estos juegos (agradecimiento especial a Charles MacDonald por su aplicación de visualización de carreteras): Lo primero que podrías notar es que el gráfico de la carretera es de una resolución mucho mayor que los gráficos en el juego. Específicamente en estos juegos, la carretera es de aproximadamente de 512x256 mientras que en pantalla se muestran en una resolución de sólo 320x224. Esto le da al motor gráfico plenitud para jugar con los gráficos, reduciendo la cantidad de "dientes". Otra cosa que puedes observar es que la perspectiva de la carretera almacenada en la ROM es completamente diferente a la perspectiva mostrada en el juego. Esto se debe a que el gráfico en la ROM almacena lo básico acerca de cómo se podría ver la carretera para diferentes anchos. Es asunto del programa seleccionar las líneas correctas de esta gran imagen para cada línea de la pantalla. El hardware soporta dos carreteras al mismo tiempo, por lo que puedes asignarle prioridad tanto a la carretera izquierda como la derecha. Esto aplica a las partes del juego en donde la carretera se bifurca. Para ustedes hacerks de ROM que andan por ahí, revisen los archivos src/mame/video/segaic16.c y src/mame/video/taitoic.c de MAME para ver ejemplos de chips de carretera. Observa que los gráficos de carretera en la Sega están almacenados en un formato plano de 2 bits, con el centro del gráfico capaz de desplegar un cuarto color (la línea amarilla que se ve en las imágenes de arriba).
Enduro
Como puedes ver aquí, Enduro luce un poco diferente de los otros motores de carretera que hemos visto hasta ahora. Inmediatamente evidente es que la carretera es dibujada sólo en su contorno: El terreno a los lados de la carretera no son dibujados en un color diferente al del pavimento. Tampoco hay obstáculos fuera de la carretera. Una vez que comienzas a jugar Enduro, puedes notar que la carretera no se mueve en perspectiva. En lugar de eso, el sprite del auto del jugador y la carretera se mueven a izquierda y derecha para dar la ilusión de curva. Para comprender mejor por qué Enduro luce así, veamos un poco las limitaciones de la Atari 2600. Esta consola fue diseñada principalmente para juegos del estilo Combat (juegos de tanques) y del Pong. Por lo tanto, era capaz de mostrar: dos sprites, dos cuadrados que representan misiles para cada jugador, un cuadrado representando una pelota, y un fondo muy cuadradito. Y eso es todo. Pero lo que es realmente notable acerca del hardware gráfico de Atari es que es esencialmente unidimensional: el software debe actualizar los gráficos para cada scanline. Por ejemplo, para mostrar un sprite, el programador tiene que cargar una nueva línea de gráficos para mostrar al comienzo de cada scanline. Para dibujar el objeto pelota, el programador activaría la pelota cuando el destello del televisor alcance la línea correspondiente, y la desactivaría (apagaría) cuando el destello alcance una línea donde la bola ya no debería ser visible. Esto causa un efecto secundario: una línea vertical sólida se puede dibujar en pantalla activando la pelota o los misiles sin desactivarlas nunca! Si el programador mueve esos objetos en cada línea, se pueden dibujar líneas diagonales. Ahora, volviendo a nuestro asunto. Podríamos dibujar la carretera utilizando los bloques del fondo, pero la resolución es mucho menor para ser efectiva. Lo que hicieron los juegos de conducción de Atari en su lugar fue utilizar los dos objetos misiles o de la pelota para dibujar los lados izquierdo y derecho de la carretera, generalmente de la misma forma en que podrían ser usadas para dibujar las líneas. Enduro en particular usa el primer sprite del misil del jugador y el sprite de la pelota para dibujar ambos lados. Pole Position, por el otro lado, utiliza ambos sprites de misiles para los lados de la carretera y la pelota para dibujar una línea punteada en el centro.
Una cosa que no he mencionado es cómo mover objetos línea por línea en una Atari 2600. El chip gráfico de Atari posee una facilidad llamada HMOVE (horizontal move). Esto permite al programador definir movimientos en cada línea para los diferentes objetos de una forma bastante sencilla. Todo lo que el programador tiene que hacer es definir cuántos pixeles mover para estos objetos, y luego llamar HMOVE y voila-- se mueven de acuerdo a como fueron definidos! Enduro explota esta ventaja para dibujar curvas. En resumen, lo que Enduro crea es una tabla en memoria con los valores correspondientes a cómo varía HMOVE hacia izquierda y derecha a medida que se dibuja en la pantalla. Esto hace uso de casi la mitad de la memoria disponible en la Atari 2600. Ya que la memoria de Atari es tan pequeña, este valor sólo es leído cada 4 líneas. Hay una tabla diferente para los lados izquierdo y derecho de la carretera. Cuando la carretera es recta, el arreglo para el lado derecho tiene todos sus valores en 8. La función HMOVE sólo utiliza los 4 bits más significativos, por lo que un 8 cargado en HMOVE no movería los lados de la carretera de ninguna manera. Los 4 bits menos significativos son usados como una forma de punto fijo. Como ejemplo, aquí se muestra cómo se ve una curva a medida que se acerca (el
horizonte es el final del arreglo): Y el siguiente frame: Observa que los valores de curva más altos se escriben progresivamente sobre los valores inferiores, retrocediendo hacia la parte frontal de la pantalla para darle al jugador la ilusión de que se está acercando una curva. Y qué hace Enduro con esta información? Aquí hay algo del código usado para describir la curva para el lado derecho de la carretera.
Para cada scanline de la carretera: Entonces qué estamos haciendo? Bueno, $be es un contador para el valor de curva el cual se incrementa. Cuando es cargado, los 4 bits más significativos son "arrojados por la borda", dejando un rango de 0 a 16 ($0-F). Luego, la entrada de la tabla de curvas para esa scanline en particular, es cargada y sumada. Por último, es almacenada de nuevo en el contador y cargada además en el registro de movimiento horizontal para el objeto pelota (el lado derecho de la carretera). Esto hace algunas cosas. Primero, sólo resulta en los lados de la carretera moviéndose cada dos líneas cuando la carretera es recta: Si el arreglo es completamente 8 y $be es 0 en la primera línea, la siguiente será 8 (los 4 bits superiores son aún 0). La siguiente línea valdrá luego $10. Pero cuando $10 esté cargada en el registro A en la siguiente scanline, los 4 bits superiores serán descartados volviendo a ser 0! Esto hace que el contador varíe entre $10 y 8. Debido a que los valores de HMOVE sólo utilizan los 4 bits más significativos, la línea mueve 0 posiciones 1 de forma alternada. OK, pero qué pasa si el arreglo posee todos 9 en lugar de 8? Esto es lo que ocurre: En la primera scanline, el 9 es almacenado en el registro HMOVE de la pelota y escrito en el contador. En la siguiente línea, se sumará de nuevo 9 al valor de la tabla resultando en $12 (18 decimal). Esto moverá la pelota 1 lugar (los 4 bits más significativos son 1). Ahora en la línea, los 4 bits más significativos son descartados quedando en 2. Agregar 9 de la tabla resulta en $B. Veamos una scanline más. B es cargado. No hay 4 bits más significativos. Sumando 9 se hace $14 (20). La secuencia descrita arriba es 09,12,0b,14. Esto aún causará que la pelota se mueva cada dos líneas para estas 4 líneas. Sin embargo, eventualmente el nybble (4 bits) menos significativo se volverá lo suficientemente mayor que hará que la rutina mueva el sprite de la pelota hacia la izquierda dos líneas en fila. El patrón se cubrirá, pero luego de algunas líneas más, el costado de la carretera se moverá de nuevo dos líneas en fila. En esencia, esto es una forma rápida y fugaz de matemática de punto fijo. Hay otro obstáculo a la hora de implementar un sistema de carretera en este hardware limitado: ubicar los sprites. En sistemas más sofisticados, los sprites se pueden posicionar horizontalmente en la carretera como un porcentaje del ancho de la carretera. Pero esto requiere multiplicación de punto fijo o flotante, que son extremadamente lentos en una 6502. En constraste, Enduro sólo tiene tres posiciones para los vehículos, lo que ahorra algunos cálculos.
Road Rash Como mencioné más arriba, tanto el motor del Road Rash como el de 3do Road Rash son una mezcla de 3d y pseudo 3d. Utilizan una técnica similar a la descrita en el capítulo "Colinas Realistas utilizando Segmentos Proyectados en 3d" en donde las colinas están en un espacio tridimensional, mientras que las curvas de la carretera no. Las curvas en Road Rash usan el mismo método descrito en este artículo, y cada segmento de carretera tiene su propio valor de DDX o "aceleración de x". Cada segmento tiene además una altura que es relativa a la altura del último segmento. Hay aproximadamente 50 segmentos en pantalla al mismo tiempo. Pero donde 3do Road Rash es realmente interesante es en que los programadores agregaron deformación que aumenta la sensación de velocidad que el jugador experimenta: Los objetos alejados de la cámara se mueven más lento, mientras que aquellos más cercanos lo hacen más rápido. El 3do Road Rash agrega también objetos poligonales a las afueras de la carretera, cuyas coordenadas X son aún relativas a la carretera. Estas son utilizadas para crear colinas, construcciones, y otros escenarios relativamente complejos. Todo esto hace uso de una cantidad significativa de datos, por lo que la geometría y las texturas son obtenidas del disco a medida que la pista es atravesada. S.T.U.N. Runner: Arcade vs. LynxS.T.U.N. Runner fue un juego maravilloso cuando hizo su aparición en los arcades en 1989. Este juego hizo un gran uso de tecnologías de polígonos completamente en 3d, permitiendo al jugador tomar el control de una nave de carreras futurista atravesando túneles retorcidos y cambiantes, a una velocidad rompecuellos. No mucho después, vi una versión para la Atari Lynx. Este fue un sistema portátil que apareció por los tiempos de la original Game Boy. Como este último, tenía un procesador de 8 bits y 4MHz. Entonces, el port era horrible, verdad? Bueno, mira este video:
En realidad, el port era fantástico! Estuvo cerca de capturar perfectamente lo que hizo a la versión de arcade tan emocionante. Con hardware portátil de la era del Game Boy, cómo rayos lo hizo? Resulta que la Lynx tenía un arma poderosa en su arsenal: escalado de hardware. Pero esto no es de gran ayuda a la hora de renderizar polígonos. Pues no era sólo la Lynx la que tenía algunos trucos bajo la manga: El autor que hizo el port también tenía los suyos. Para recrear la velocidad de la máquina de arcade, la versión de S.T.U.N Runner para la Lynx recurrió a un motor de pseudo-3d. Los trozos de polígono que componen las paredes del túnel son realmente sprites. Estas son esencialmente objetos a las afueras de la pista que están pegados a la carretera, al igual que los objetos en otros juegos de carreras en pseudo 3d, y están dibujados utilizando el algoritmo del pintor (del fondo hacia el frente). Esto crea convincentemente la ilusión de gráficos poligonales mientras sigue jugando con las fortalezas del hardware. Y para ahorrar espacio en el cartucho, el túnel no era un sprite de anillo completo. Esto no sólo ahorra el número de espacios en pixeles blancos y transparentes, sino que está definido para usar el volteo horizontal del hardware gráfico. Un asunto interesante que el autor tuvo que resolver fue al ramificarse el túnel. Puedes verlo en el video de arriba. El túnel ramificado es realmente un gran sprite que se escala hacia el jugador. Una vez que el jugador haya escogido un camino, el gráfico dividido desaparece. De acuerdo con el autor, a veces puedes ver los vehículos pasar directamente a través de este sprite! Si quieres leer más, puedes revisar la conversación con el autor original en AtariAge aquí.
Las Carreteras en la Commodore 64 Primero, algunos conocimientos previos: En varias consolas, se puede hacer una carretera en pseudo-3d dibujando una carretera recta con tiles y scrolling por línea para que parezca curvarse. No obstante, esto resultó ser demasiado lento para un juego con tasa de fotogramas completa en la Commodore 64. En su lugar, el motor de Simon utiliza el modo mapa de bits (bitmap) de la C64 y un algoritmo de relleno rápido. Este algoritmo utiliza código automodificable para acelerar los dibujos en pantalla: Cada línea es una serie de almacenamientos por pixel que especifica una dirección en la memoria de video. En el punto en donde tiene que cambiar el color, se altera el código. El comando de almacenar (store) se convierte en un comando cargar(load), y lo que era la dirección de almacenamiento se convierte en el número literal del nuevo color. Una de las mayores ventajas de esta técnica es que las técnicas de multiplexado de sprites para mostrar más de ocho sprites en pantalla aún se pueden utilizar. Según el propio Simon: "Desplazar el scroll horizontal para obtener un efecto rasterizado estable implicaría manipular el registro $D011. De lo contrario, el interruptor IRQ en $D012 parpadearía mucho según el número de sprites en esa línea en particular. Para tener una suerte de suavizado en pantalla se debería bloquear el procesador para obtener el tiempo justo, o no utilizar los gráficos en pantalla y cambiar sólo el color del borde. Esto debería ser sólido y libre de parpadeos, pero no habría carretera visible en pantalla puesto que tendría que ser desactivada. Estos cambios suaves en los colores del borde por línea fueron utilizados persiguiendo el interruptor por la pantalla, y podía además ser utilizado para 'esperar' donde se mostraría la parte superior de la pantalla. Esto se llamó espera de $D011 o a veces FLD por Flexible Line Distancing o Distanciamiento de Línea Flexible (la técnica usada para eliminar las líneas malas del VIC).
Otros Motores
Power Drift
Racin' Force Exploración AdicionalAquí hay algunos sitios que podrían ser de utilidad para aprender más acerca de carreteras en pseudo 3d:Asuntos del CódigoFórmulas y ConsejosProyección 3d
y_screen = (y_world*scale / z) + (screen_height >> 1)
o:
z = (y_world*scale) / (y_screen - (screen_height >> 1)) Esta fórmula toma las coordenadas x o y absolutas de un objeto, la z del objeto, y retorna la ubicación x o y del pixel. O, alternativamente, dadas las coordenadas del mundo y de la pantalla, retorna la ubicación z. La variable scale determina el campo-de-visión (FOV), y puede hallarse así:
scale_x = x_resolution/tan(x_angle/2)
Interpolación Lineal Rápida
o(x) = y1 + ((d * (y2-y1)) >> 16)
Esto asume que todos los números están en punto fijo de 16.16. y1 y y2 son los dos valores para interpolarse, y d es la distancia fraccional de 16 bits entre los dos puntos. Por ejemplo, si d=$7fff, sería la mitad de la distancia entre los dos valores. Esto resulta útil para hallar un valor entre dos segmentos. Aritmética de Punto Fijo El punto flotante es muy costoso para aquellos sistemas antiguos que no tenían hardware matemático dedicado. En su lugar, se utilizó un sistema llamado punto fijo. El mismo reservaba un cierto número de bits para la parte decimal del número. Para un caso de prueba, digamos que sólo reservas un bit para la cantidad decimal, dejando siete bits para la parte entera del número. Ese bit decimal representaría una mitad (debido a que una mitad más otra mitad es un entero). Para obtener el valor de un número almacenado en ese byte, el número es alterado otra vez. Esto se puede expandir para usar cualquier cantidad de bits para las porciones decimal y entera del número. La multiplicación de punto fijo es más complicada que la suma. En esta operación, se multiplican los dos números y
luego se desplaza a la derecha aunque varios bits están reservados para la parte decimal. Debido al desbordamiento, a veces puedes necesitar desplazar
antes de la multiplcación en vez de después. Mira "Interpolación Lineal Rápida" para un ejemplo de producto de punto fijo.
Generalmente, una rutina de escalado tiene parámetros como x, y, y el facto de escalado.
Sin embargo, ya que un factor de escala es tan sólo 1/z, podemos reutilizar el valor Z de ese
sprite! Aunque todavía necesitaremos este factor para determinar los límites
del sprite para así poder mantenerlo centrado a medida que se escala.
GlosarioLínea Mala - El chip de gráficos VIC II de la C64, en el primer pixel de cada tile del fondo, toma el control del procesador para obtener más información como los colores. Puesto que quedan menos ciclos para que un programa haga sus cálculos, estas son referidas como líneas malas Mapa de Altura (Height Map) - Un mapa de altura es un arreglo de valores de alturas. En un motor de polígonos o voxel, este podría ser un arreglo bidimensional (piensa en un paisaje visto desde arriba). Sin embargo, en un motor de carretera, un mapa de altura sólo necesitaría ser de una sola dimensión (piensa en un paisaje visto de costado). Modo de Color Indexado - Los sistemas más viejos que tenían pocos colores en pantalla a la vez, los muestran generalmente en modos de color indexado. Algunos de los modos de color indexado más habituales son los modos VGA de 256 colores. En estos modos, cada pixel es representado por un byte. Cada byte almacena un valor de índice entre 0 y 255. Cuando se dibuja en pantalla, se busca en la paleta el número de índice para cada pixel. Cada entrada en la paleta puede ser uno de los 262 o 144 posibles colores de VGA. En conclusión, incluso aunque sólo se pueden mostrar 256 colores en pantalla a la vez, el usuario puede escoger cada color de una paleta mucho más grande. Interpolación Lineal - El proceso de obtener valores intermedios de un conjunto de datos dibujando líneas entre los puntos. Algoritmo del Pintor - El Algoritmo del Pintor es la técnica de dibujar objetos superponiéndolos del más lejano al más cercano. Este algoritmo se asegura que los objetos más cercanos estén antes que aquellos más lejanos. Modo de Gráficos Planares - Un modo de Gráficos Planares es aquel en donde una imagen de N bits está hecha de N imágenes de 1 bit combinadas para producir la imagen final. Esto es lo opuesto a la mayoría de los modos gráficos, a veces referidos como sólidos, en donde una imagen de N bits está hecha de valores de pixel de N bits. Efecto Rasterizado - Un efecto rasterizado es una técnica gráfica que toma ventaja de la naturaleza basada en scanlines de la mayoría de las computadoras. Factor de Escalado - El recíproco de Z. Este te da la cantidad a la cual escalar un objeto a una distancia Z dada. Segmento (Carretera) - Estoy usando el término segmento para referirme a una posición en donde debajo, la carretera actúa de una manera, y de forma diferente arriba de la misma. Por ejemplo, el segmento podría dividir una curva hacia la izquierda en la parte inferior de la pantalla de una curva hacia la derecha en la parte superior. A medida que el segmento se dirige hacia el jugador, la carretera parecerá zigzaguear a izquierda y derecha. Segmento, 3d (Carretera) - Utilizo el término segmento 3d para referirme a una línea horizontal que tiene tanto una distancia Z como una altura Y. A diferencia de un vertex, que podría ser un punto 3d, un segmento tridimensional sería una línea 3d con los puntos extremos izquierdo y derecho del eje X al infinito negativo y positivo. Voxel - Un pixel 3d. Los motores de Raycast/escenarios con voxel fueron popularizados en el juego Commanche: Maximum Overkill. Z Map - Una tabla que asocia cada línea de la pantalla con una distancia Z.
La GaleríaHe aquí una colección de capturas de pantalla que ilustran varios tipos de motores de carretera poco ortodoxos. Echa un vistazo!Cisco Heat Pole Position Hydra Outrunners Road Rash
Turbo Spy Hunter II Pitstop II Enduro Enduro Racer Lotus Test Drive II Speed Buggy |