¿Cómo puedo hacer una demostración de gráficos sin un búfer de fotogramas?
Estoy programando un microcontrolador, una pequeña computadora de 8 bits que tiene aproximadamente 100Mhz - 300Mhz de velocidad de reloj de CPU y 64K de RAM de aplicación (en realidad ROM), pero solo una pequeña cantidad de RAM (6K), ni mucho menos lo suficiente para un búfer de trama de gráficos.
Entonces, quiero mostrar una demostración de gráficos animados en esta máquina. Parece tener suficiente potencia informática para calcular gráficos animados de algún tipo.
Para que se muestren gráficos animados, de alguna manera necesitaré tener un programa de computadora que, cada vez que se llame, devuelva una sola línea de píxeles de un cuadro de imagen, que luego se muestra. El programa del controlador de pantalla continúa haciendo esto, llamando línea tras línea hasta que ha mostrado un cuadro completo, luego comienza en la parte superior nuevamente para el siguiente cuadro.
Para que quede claro, no estoy preguntando sobre el programa que controla la pantalla. Estoy preguntando por el programa de demostración. Necesito un programa de demostración que genere sus gráficos una línea a la vez, que luego mi controlador de pantalla arrojará a la pantalla.
Estoy tratando de imaginar qué tipo de programa de gráficos podría funcionar de esta manera. Presumiblemente la visualización de algún tipo de fórmula matemática.
¿Alguien puede sugerir técnicas para la programación gráfica que funcionen de esta manera, en las que se pueda llamar a una función, que calcula y devuelve una línea del marco, solo cuando se le solicita? ¿Existen programas que funcionen así que pueda examinar?
Espero que la pregunta tenga sentido. Se agradecería cualquier sugerencia o sugerencia en la dirección correcta.
Respuestas
Este es un caso en el que sería razonable observar el funcionamiento de hardware más retro . El hardware más antiguo tenía fuertes limitaciones tanto en la memoria como en la potencia de procesamiento. Su chip de 100 + Mhz es mucho más rápido que la mayoría de los chips de consumo de los 80 y anteriores. Entonces, si bien esas CPU necesitaban chips gráficos dedicados para poder renderizar, su CPU mucho más rápida probablemente pueda manejar la tarea de manera adecuada.
Sugeriría comenzar con algo como la arquitectura tilemap-and-sprite de NES. Estas arquitecturas están diseñadas para ser eficientes en memoria (sacar mucho del espacio de almacenamiento limitado) y computacionalmente eficientes en una salida línea por línea, ya que generan datos de píxeles y los envían directamente al dispositivo de visualización a la velocidad esperada de un CRT.
La idea general es esta. Un mapa de mosaicos consta de dos partes: una serie de mosaicos y una matriz 2D de índices en el mapa de mosaicos que representa la imagen que se está creando. Los mosaicos son típicamente de 8x8, y en la NES eran de 2 bits por píxel y los mosaicos usaban paletas. O más específicamente, el índice en el mapa de mosaicos incluye no solo el índice de un mosaico, sino también el índice de la paleta para usar con ese mosaico. Por tanto, un mosaico no está asociado de forma inherente a una paleta; la asociación se realiza en el punto de uso (técnicamente, en la NES, el mapa de mosaicos y el mapa de paleta estaban separados, ya que cada bloque de 2x2 de mosaicos tenía que usar la misma paleta).
Para permitir el desplazamiento, el mapa de mosaicos es más grande que la pantalla visible y hay un desplazamiento que representa dónde se encuentra la esquina superior izquierda de la pantalla visible dentro del mapa de mosaicos. Deje que este desplazamiento esté en la posición Xoff
y Yoff
.
Puede ver cómo esto hace que el procesamiento fila por fila sea trivial. Para generar una fila horizontal para la posición horizontal Ypos
(en el espacio de la pantalla), debe obtener el píxel inicial dentro del mapa de mosaicos. Eso requiere transformar la posición XY (0, Ypos)
del espacio de la pantalla al espacio del mapa de mosaicos. Entonces le agregas el vector (Xoff, Yoff)
, ocultando el vector `` Xoff, Yoff + Ypos) `.
Tenga en cuenta que al realizar cualquier mapeo desde el espacio de la pantalla al espacio del mapa de mosaicos, el espacio del mapa de mosaicos debe ajustarse en los ejes X e Y. Por lo tanto, cada vez que calcula un nuevo píxel en el espacio del mapa de mosaicos, debe ajustarlo al tamaño del espacio del mapa de mosaicos.
Ahora, necesitamos dividir este píxel del mapa de mosaicos en dos componentes 2D: el índice de mosaico dentro del mapa de mosaicos que nos da este píxel y el píxel dentro de ese mosaico que necesitamos buscar para este píxel. El índice de mosaico es solo el número entero de píxeles del espacio del mapa de mosaico dividido por el tamaño del mosaico. La coordenada de píxeles es el número entero de píxeles del espacio del mapa de mosaicos modificado por el tamaño del mosaico. Dado un tamaño de mosaico de 8x8, estás haciendo esto:
ivec2 tilemap_pixel = ...; //Compute the tilemap starting pixel as above.
ivec2 tilemap_tile = ivec2(tilemap_pixel.x & ~0x7, tilemap_pixel.y & ~0x7); //Mask off the lower 3 bits.
ivec2 pixel_in_tile = ivec2(tilemap_pixel.x & 0x7, tilemap_pixel.y & 0x7); //Mask off all but the lower 3 bits.
Dado tilemap_tile
, ahora tenemos el índice del mosaico en el que estamos trabajando actualmente. Y pixel_in_tile
nos da el píxel en el que estamos trabajando. De modo que podemos buscar ese píxel, cuyo valor se puede mapear en una paleta para producir el color final para ese píxel. O podemos usarlo directamente.
Obtener el siguiente píxel es bastante trivial. Simplemente incrementamos pixel_in_tile.x
en 1, módulo el tamaño del mosaico. Si el incremento excedió el tamaño del mosaico, también lo incrementamos tilemap_tile.x
en 1, módulo el tamaño del mapa de mosaico. Y continúa hasta que hayas completado la fila de píxeles.
Existen muchas oportunidades para optimizar el rendimiento de dicho algoritmo.
Los mosaicos probablemente representan los datos más grandes. Un conjunto de mosaicos de 128 elementos de mosaicos de 8x8, incluso a 2bpp, es 2K. Pero la cuestión es que se pueden almacenar en ROM , ya que probablemente no esté cambiando los mosaicos en sí. El tamaño del mapa de mosaicos (asumiendo que debe estar en RAM) depende de la resolución de salida deseada y del tamaño del mosaico. Un mapa de mosaicos para mosaicos de 8x8 que puede cubrir una pantalla de 320x240 tiene 1200 bytes. No exactamente diminuto. Deberá ocupar más memoria si desea espacio para un desplazamiento suave (y, por lo tanto, un mapa de mosaicos más grande).
Dicho esto, también es importante tener en cuenta que el "tamaño de salida" no tiene que ser el tamaño real del dispositivo de visualización . Por ejemplo, si tiene la intención de dibujar en un dispositivo de visualización de 1080p, aún puede renderizar a una resolución interna que sea, por ejemplo, 8 veces más pequeña, 240x135. Es fácil modificar este algoritmo para generar esencialmente el mismo valor de píxel 8 veces seguidas, así como reutilizar la misma línea 8 veces por línea de 1080p. De hecho, no sería demasiado difícil que este algoritmo permitiera el desplazamiento de subpíxeles (desplazamiento en un espacio de 1080p, no en un espacio de 135p) e incluso agregar algunos filtros entre los valores de píxeles. Un mapa de mosaicos de "tamaño de salida" de 240x135 solo requeriría 510 bytes para la región visible, por lo que tendría más espacio para un área de desplazamiento más grande.