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.

Publicado por

jcanton

Javier is a Computer Science Engineer who has always had a passion for 3D graphics and software architecture. He learned C# almost at the same time as he learned to talk, and his first word was "base". He enjoys imparting talks about technology and has contributed in many important software and video game events. He has participated in multitude of software projects involving multitouch technologies, innovative user interfaces, augmented reality, and video games. Some of these projects were developed for companies such as Microsoft, Syderis, and nVidia. His professional achievements include being MVP for Windows DirectX and DirectX XNA for the last eight years, Xbox Ambassador, as well as Microsoft Student Partner and Microsoft Most Valuable Student during his years at college. Currently he works at Plainconcepts and he is the development team lead at WaveEngine project.

7 comentarios sobre “XNA Graphics Pipeline”

  1. Hola Javier, a riesgo de parecer algo mamón me gustaría hacerte una pregunta. ¿Cómo obtienes esta información, a base de probar y probar o tenéis los mvp información más detallada de los intestinos de XNA?

    Gracias de antemano.
    Te saluda un gaditano, 🙂

  2. No hombre XD, toda esta información es pública aunque principalmente en Inglés (solo hay que buscar un poco), yo simplemente lo he vuelto a explicar a mi manera.

    Saludos

  3. Hola Javier, mi equipo y yo tenemos un problemilla con el dibujado en nuestro juego en XNA, y quizá puedas aclararnos una cosa.

    He creado una lista de quads, cada uno con una textura con transparencia. Luego un método Draw() dibuja cada quad de esa lista recorrida en un bucle for().

    El problema viene que, dependiendo del orden de esa lista, la transparencia dibuja o no los objetos que hay detrás de la textura. Si le pido al programa que dibuje primero los objetos que hay más cerca de la cámara, se dibuja la transparencia como si no hubiera nada detrás de ella.

    Estoy seguro de que debe haber técnicas para evitar esto. Para nosotros es un problema recurrente que haya errores de dibujado como estos cuando empezamos a usar transparencias. Podríamos checkear la profundidad de los objetos para cambiar el orden de la lista en cada frame, pero eso quizá consuma demasiados recursos.

    Por si fuera poco, en la escena no sólo hay sprites estáticos, sino modelos 3d y billboards que cambian de posición que complican aún más el tema del dibujado y el control de la profundidad.
    ¿Cómo abordar este problema?

    Muchas gracias por la atención. 🙂

  4. Hola otra vez. xD

    Supongo que será mucha tela explicar cómo resolver un problema tan complicado, así que me daría por satisfecho sólo con algunos enlaces a artículos y códigos de ejemplo que te parezcan aconsejable leer, para que pueda seguir investigando a partir de ahí. Gracias. 🙂

  5. Buenas Pedro,

    Siento no haberte respondido antes, he estado algo liado.

    Si el problema que tenéis es conocido y «fácil» de solucionar, debéis dibujar primero todos los objetos opacos y después desactivar la escritura en el zbuffer y dibujar todos los objetos transparentes ordenados por profundidad.

    Hay muchas optimizaciones sobre esto, y es preordenar antes de enviar a la gráfica los elementos a dibujar, pero por ahora probar con esa simple idea a ver que tal os va.

    Saludos

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *