Todos los capítulos · Capítulo 9/9

Que parezca de 1997

Arrancando

Una línea:

usePostProcess(dither({ levels: COLOR_LEVELS.genesis }));

Quítala y vuelve a ponerla. Es el capítulo entero.

Post-proceso: un filtro sobre la imagen terminada

Todo lo que has hecho hasta ahora le dice al motor qué dibujar. El post-proceso es distinto: el motor dibuja el fotograma completo y después le pasa un shader por encima, a la imagen entera, antes de enseñarla.

Por eso usePostProcess no va asociado a ningún sprite. No es de la pala ni de la bola: es de la pantalla.

Qué hace el dithering

levels: 8 significa ocho tonos por canal de color en vez de los 256 que tiene tu monitor. Eso es exactamente lo que podía enseñar una Mega Drive: 3 bits por canal.

Reducir los colores a lo bruto produce bandas feas: escalones visibles donde antes había un degradado suave. El dithering es el truco de la época para taparlas: mezcla los dos tonos disponibles más cercanos en un patrón regular y deja que tu ojo los promedie. Grano en lugar de escalones.

El detalle que hace que esto funcione está dentro y no lo tienes que tocar: la amplitud del ruido tiene que valer exactamente un escalón de cuantización. Si te quedas corto, las bandas siguen ahí debajo del grano; si te pasas, los píxeles bailan entre colores que no son vecinos y parece interferencia. Aquí ese número sale de levels, así que los dos no se pueden desincronizar.

Prueba otros:

COLOR_LEVELS.genesis   // 8 tonos: Mega Drive
COLOR_LEVELS.snes      // 32: Super Nintendo
COLOR_LEVELS.ps1       // 32: PlayStation, Saturn
COLOR_LEVELS.poster    // 4: no es ninguna consola, pero queda bien

Y strength, que escala el efecto sin romper esa relación:

usePostProcess(dither({ levels: COLOR_LEVELS.poster, strength: 0.5 }));

Por qué esto es el motor y no un shader tuyo

Podrías escribir el shader a mano: el motor te deja. Pero acertar con la amplitud del ruido es la parte difícil, y equivocarse no da un error: da una imagen que casi está bien y no sabes por qué.

Esa es la idea de todo NacatamalOn, y ahora ya la has visto por dentro: la limitación (paleta corta, poca geometría, grano) no es una carencia que haya que disimular. Es el estilo, y viene puesta.

¿Qué es un shader?

Un programa pequeñísimo que se ejecuta en la tarjeta gráfica, una vez por píxel, en paralelo para todos a la vez.

Aquí el shader recibe el color que iba a tener un píxel y devuelve otro: el mismo, redondeado a uno de los ocho tonos disponibles, más un pelín de ruido ordenado. Multiplica eso por los 153.600 píxeles de un juego de 480×320 y sesenta veces por segundo, y entiendes por qué esto lo hace la gráfica y no el procesador.

No hace falta que sepas escribirlos para usar el motor. dither y sus compañeros vienen hechos.

Ya está

Tienes un juego. Se puede jugar, se puede ganar, se puede perder, y tiene el aspecto que querías.

Repasa lo que has aprendido, porque no es poco: el bucle y dt, sprites y coordenadas, teclado, colisión de cajas, crear objetos con un bucle, destruirlos de forma diferida, texto de mapa de bits y post-proceso. Con eso se hace un juego de verdad, no un juego de tutorial.

Y no has instalado nada.

Ya está. Es tuyo.

Has escrito un juego entero. El siguiente paso es sacarlo de esta página y llevártelo a tu ordenador, y para eso estamos preparando una plantilla.