XNA Graphics Pipeline

Si queremos extraerle el máximo partido gráfico a XNA usando HLSL es importante conocer el pipeline gráfico usado por este framework:

simple_pipeline

Cada uno de estos “módulos” cumplen un papel especial hasta conseguir pintar un objeto en pantalla, ya sea un modelo 3D ó una imagen 2D (la cual es pintada sobre un quad “anclado” a la pantalla).

Veamos que se hace en cada uno de estos “módulos”:

Vertex Data: Contiene un buffer de vértices sin transformar indexado o no indexado, es posible indicar mediante VertexDeclaration que información (position, color, texture coordinates, normals, …) viene definida por cada vértice en este buffer.

Primitive Data: Contiene las primitivas de geometría como points, lines, triangles y polygons leídos del index buffer que referencian a los vértices contenidos en el Vertex Data. En el index buffer aparecen cada una de estas primitivas y está asociada a una lista ordenada de vértices que la componen (hay que recordar que el orden es importante).

Textures: Aquí se almacenan el conjunto de texturas que son usadas por el modelo que se va a pintar, como máximo se pueden usar 8 (texture0 – texture7).

Texture Samplers: Todas las texturas que se vayan a usar en Vertex Processing ó Pixel Processing tienen que ser usadas a través de Samplers en los cuales se indica el modo de direccionamiento de la textura (TextureAddressModel) y como será filtrada (TextureFilter).

Tesselation: (No disponible bajo XNA) En esta unidad se trabaja con N-Patches ó Displacement maps para generar nuevos vértices y dar mayor nivel de detalle a los modelos evitando enviar previamente todos esos vértices a la tarjeta gráfica (lo cual produciría un cuello de botella). Estos vértices se añaden a los que ya existen en el vertex buffer y sólo existen en el pipeline de la tarjeta gráfica. Esta cualidad ha sido rediseñada para DirectX 11 para conseguir que se le pueda sacar mucho más partido y ya se han publicado algunos ejemplos increíbles sobre las nuevas posibilidades.

Vertex Processing: Aquí es donde se ejecuta el código HLSL del Vertex Shader de nuestro Effect, para cada uno de los vértices almacenados en el vertex buffer. La principal tarea que se debe realizar en el Vertex Shader es pasar todos estos vértices de Model Space a Projection Space que viene dada por la configuración de la cámara.

Greometry Processing: En este “módulo” tienen lugar varias tareas:

GeometryProcessing

  • Face Culling: Los triángulos que no miran hacia la cámara (el ángulo formado por el vector normal del triángulo y el vector lookAt de la cámara es mayor de 90º) son borrados de la escena.
  • User Clip Planes: Si el usuario a definido planos de corte, aquí es donde se realiza el test y todos los triángulos que están por detrás de estos planos son borrados de la escena.
  • Frustum Clipping: Clipping es el proceso por el cual los triángulos que no aparecen dentro de la visión de la cámara son borrados de la escena.
  • Homobeneous Divide: La información x,y,z de cada vértice es transformada a non-homogeneous space antes de ser enviada al rasterizer.
  • Viewport Mapping: Se lleva cabo el proceso de asignación de pixeles de pantalla a cada triángulo.

 

Pixel Processing: Es donde se ejecuta el código HLSL del Pixel Shader de nuestro Effect por cada uno de los pixeles que forman el modelo, desde la función de Pixel Shader se suele hacer uso de los Texture Samplers para aplicar las texturas al modelo y alguna información de la geometría (WorldPosition, Normal,…) para realizar ecuaciones de iluminación (Lambert, Phong) a nivel de pixel (PerPixel Lighting), ó técnicas más avanzadas como Normal Mapping, Parallax Mapping, etc.

Pixel Rendering: En este “módulo” tienen lugar los siguientes test configurables en el Device:

PixelRendering

  • Flog Blend: La API nos proporciona la funcionalidad de aplicar niebla a la escena y es aquí donde se realiza el test.
  • Scissor Test: También podemos definir un rectángulo de la pantalla (Scissor Rect) he indicar al Device que sólo se renderize esa porción del buffer. El test en el que se determina si los pixeles están dentro o fuera de dicho rectángulo se realiza aquí.
  • Alpha Test: Se realiza una comprobación para evaluar mediante los valores del canal alpha si cumplen o no una condición marcada por el Alpha Ref, más información.
  • Depth/Stencil Test: Se llevan a cabo los test de Depth (profundidad), se actualiza el depth buffer con la profundidad del pixel si este será visible. También se aplica el Stencil Test (plantilla) para ver si el pixel afectará al color final del pixel en pantalla.
  • Alpha Blend: En él se realizan las mezclas de colores que hacen posible el dibujado de objetos semitransparentes, más información.
  • Dither: Utiliza un algoritmo de interpolación de colores para combinar los pixeles adyacentes y así conseguir una gama de colores más consistente.
  • Channel mask: Se puede escribir solo en los canales que deseemos (RenderState.ColorWriteChannels).
  • RenderTarget: Se colocan los pixeles en el Render Target.
  • Presentation: El RenderTarget es presentado por pantalla en el monitor.

 

Conocer este pipeline nos puede ayudar a comprender mejor algunas técnicas de optimización muy usadas como Frustum Culling, ó Z-Prepass.