XNAnimation para XNA 3.1

Actualización rápida: acabo de colgar la librería XNAnimation 0.7.0.0 Beta 2 compilada para su uso en XNA 3.1; la versión de CodePlex, aparte de estar un poco inactiva, aún tiene los archivos de solución y de proyecto configurados para XNA 3.0, lo que puede dar más de un quebradero de cabeza si intentas usar los archivos descargados desde allí directamente en tu juego.

Una lástima que una librería tan útil y potente como esta lleve parada tanto tiempo… va siendo hora que alguien retome su desarrollo, ¿no? 😛

Nos vamos de rebajas a… Juguetilandia

Entre hoy y anteayer, a la salida de ambos exámenes, me he dado una vuelta por la sección de videojuegos del Juguetilandia del centro comercial cercano a la universidad. ¿El resultado? Lo que viene a continuación…

22012010291
Sam & Max hit the road, No one lives forever 2, Tribes 2

22012010292
Tribes vengeance, F.E.A.R. extraction point, Commandos strike force

22012010293
Tribes, Gunman chronicles (¡el último que quedaba! casi se lo he quitado de las manos a Edu)

Si os dais una vuelta… lo siento, casi todo lo bueno ya me lo he llevado yo 😀

XNA Demos (IV): reduciendo la carga del Pixel Shader con un Z-Prepass

descargar_codigo

Si el ejemplo anterior trataba sobre la complejidad geométrica de una escena, en éste se va a explicar un método para reducir el trabajo que tiene que realizar el raster a la hora de aplicar los Pixel Shaders.

Normalmente, el bucle de dibujado de una escena consiste en recorrer todos los objetos (si son completamente opacos no hace falta ordenarlos de más lejano a más cercano) y ejecutar las instrucciones de dibujado de cada uno. La GPU recibe todos los datos, descompone las formas complejas (rectángulos, triangle strips, triangle fans) en triángulos sencillos y ejecuta los siguientes pasos por cada uno:

diagrama_rasterProceso de raster, simplificado.

Por lo tanto, en el mismo momento en que se determina que un triángulo o vértice es visible (pasar el Z-Test), se procede a actualizar su profundidad en el Z-Buffer (sobrescribiendo los valores antiguos) y se rasterizan todos los píxeles afectados por la nueva geometría.

depthZ-Buffer (también llamado Depth Buffer) con la profundidad de la escena.

Pero esta implementación, aunque es completamente correcta y lógica, tiene un punto débil: en una escena ordenada de más lejano a más cercano (o, si no lo está, también pueden darse las condiciones necesarias desde determinada posición y ángulo), cada objeto dibujado superpone al anterior; de esta manera, todos los objetos pasarán el Z-Test y serán enviados al raster. Si los Pixel Shader de los objetos tienen un número elevado de instrucciones, se estará desaprovechando una buena parte del trabajo de la GPU en dibujar píxeles que más tarde serán ocluidos; para evitar esta pérdida de rendimiento, vamos a implementar una técnica llamada Z-Prepass.

El Z-Prepass es un algoritmo muy sencillo, en el que se dibuja toda la escena dos veces; aún teniendo el doble de coste geométrico, el ahorro de trabajo en los Pixel Shader suele compensar ésta desventaja. Los pasos a seguir son los siguientes:

  1. Paso previo:
    1. Activar el Z-Test (RenderState.DepthBufferEnable = true).
  2. Z-Prepass:
    1. Activar escritura en Depth Buffer (RenderState.DepthBufferWriteEnable = true).
    2. Desactivar escritura en canales de color (RenderState.ColorWriteChannels = ColorWriteChannels.None).
    3. Dibujar escena con el shader de Z-Prepass.
  3. Escena final:
    1. Reactivar escritura en canales de color (RenderState.ColorWriteChannels = ColorWriteChannels.All).
    2. Desactivar escritura en Depth Buffer (RenderState.DepthBufferWriteEnable = false).
    3. Dibujar escena con los shaders correspondientes.

Los cambios de estado de ColorWriteChannels no son explícitamente obligatorios, pero ayudan a ganar algo de rendimiento; debido a que la única funcionalidad que nos interesa del shader especial de Z-Prepass es la transformación de los vértices y que la GPU rellene el Depth Buffer, no nos importa descartar la información de color pues no le vamos a dar utilidad ninguna.

Así pues, contando con un Z-Buffer pregenerado, en el segundo pase de geometría, y al estar desactivada la escritura (sólo se comprueban los valores), sólo se rasterizan los píxeles estrictamente visibles, evitando el redibujado de partes ocultas. Además, existe hardware (como la XBOX 360 y algunos modelos de tarjetas gráficas) optimizado para ejecutar un Z-Prepass con la mayor eficiencia posible.

Por último, queda aclarar que el Z-Prepass no es la panacea ni una solución universal; su mayor ventaja es aplicarlo a escenas en las que el trabajo de los Pixel Shaders puede ralentizar el proceso de render en general, debido a que ejecutan un gran número de instrucciones (normal mapping, übershader, sombras con PCF). En los peores casos se puede llegar incluso a perder rendimiento, debido a que el coste de dibujar la geometría dos veces es mayor que el de rasterizar toda la escena.