Occlusion Culling (III de IV)

************************************************************************************************

Back-face culling

Frustum Culling

Occlusion Culling

Optimizaciones para el Occlusion Culling

************************************************************************************************

Por fin toca escribir sobre la técnica que puso nombre a esta serie de artículos XD, Occlusión Culling también es una técnica que podemos incluir dentro de los algoritmos de visibilidad, esta técnica intenta acelerar el rendimiento gráfico evitando que los objetos que van a ser eclipsados por otros sean enviados a la gráfica. Desde la técnica del back-face culling lo que se intenta siempre es enviar solo lo imprescindible a la tarjeta gráfica, ya que el principal cuello de botella actual se encuentra en el intercambio de información entre la CPU y la GPU.

Occlusion Culling-0

Se puede apreciar en la imagen superior que tanto a la izquierda como a la derecha, el resultado del render de la escena es el mismo (parte inferior de la imagen), sin embargo si miramos en la parte superior de la imagen, en el lado izquierdo solo se está enviando a la gráfica la habitación donde se encuentra la cámara mientras que en la parte derecha vemos que a la gráfica se envía todo.

(esta imagen de ejemplo no es demasiado buena ya que quien la reconozca sabe que es de unity 3D y que ellos tienen implementado el occlusion culling mediante un algoritimo de visibilidad espacial y no como vamos a explicar en este artículo, pero es que no he encontrado una imagen mejor XD).

¿Cómo funciona el occlusion culling?, bueno pues hasta hace tiempo se intentaba conseguir mediante algoritmos espaciales tipo BSP, QuadTree, kdtree. Pero actualmente tenemos soporte a nivel de hardware de nuestras GPUs para implementar la técnica de forma más eficiente. Las tarjetas gráficas actuales tiene un conjunto de instrucciones llamadas queries, las cuales nos informan del estado actual de la GPU, como por ejemplo cuanta memoria estamos consumiendo, que test tenemos activados en nuestro pipeline etc.

Estas instrucciones son algo muy potente ya que es como si tuviésemos una base de datos rellena tras cada render de la gráfica y mediante queries podemos conocer datos internos que solo están en la GPU.

La query en la que nos apoyaremos para implementar la técnica de Occlusion Culling se llama Occlusion query, mediante esta instrucción podemos saber cuantos pixeles a nivel de raster de la imagen final han sido coloreados de un objeto concreto.

675px-Raster_graphic_fish_40X46squares_hdtv-example

Esto lo utilizaremos para determinar si un objeto ha sido eclipsado que ocurrirá cuando el número de pixeles devuelto por la query sea 0. Pero la query es algo que podemos lanzar a posteriori por lo que ¿cómo nos ayudará esto a acelerar el render? ya que nosotros necesitamos saberlo a priori. El siguiente seudocódigo nos ayudará a aclararnos un poco:

1- Ordenamos los objetos de delante hacia atrás con respecto a la posición de la cámara
2- Por cada objeto de la escena
    2.1- Occlusion Culling
        2.1.1 Iniciamos la query
        2.1.2 Desactivamos la escritura en el zbuffer y en el frame buffer, esto hace que el raster de la gráfica sea muy rápido.
        2.1.3 Renderizamos un volumen que represente una aproximación más simple del objeto (normalmente boundingbox).
        2.1.4 Preguntamos por el número de pixeles dibujados y esperamos la respuesta.
    2.2- Si el número de pixeles es mayor que 0, renderizamos el objeto.

El secreto está en que lo que renderizamos para determinar si un objeto será visible o no, es un volumen simplificado de nuestro objeto (un boundingbox), y que al haber desactivado la escritura en el zbuffer y el frame buffer el test será muy rápido, de todas formas a mayor complejidad de los modelos mayores serán los beneficios obtenidos. Si el test determina que un objeto hay que renderizarlo, activaremos la escritura en el zbuffer y en el frame buffer, de manera que esos datos quedará guardados en el zbuffer y los utilizaremos para las siguientes pasadas del test.

¿Cómo se desactiva la escritura del zbuffer y el frame buffer en XNA 4.0?

//Para desactivar la escritura en Zbuffer, permitiendo la lectura 

GraphicsDevice.DepthStencilState = DepthStencilState.DepthRead;

 

//Para desactivar la escritura en el framebuffer no existe un estado predeterminado dentro del BlendState

//Por lo que nos crearemos nuestro propio objeto blendState

BlendState blendState = new BlendState();

blendState.ColorWriteChannels = ColorWriteChannels.None;

 

//Posteriormente solo tendremos que asignarlo donde lo deseemos

GraphicsDevice.BlendState = blendState;

¿Cómo restaurar la escritura en el zbuffer y frame buffer?

GraphicsDevice.BlendState = BlendState.Opaque;

GraphicsDevice.DepthStencilState = DepthStencilState.Default;

¿Cómo se realiza una occlusion query en XNA?

//Creamos el objeto query

OcclusionQuery query = new OcclusionQuery(GraphicsDevice);

 

//Inicializamos la query

query.Begin();

 

    //Draw BoundingBox

 

query.End();

 

//Esperamos a que se realize la query

while (!query.IsComplete);

 

if(query.PixelCount > 0)

 

    //Draw object

 

Aquí os dejo un video de una implementación que realizé en XNA para XNACommunity sobre esta técnica, el código lo analizaremos en el siguiente post ya que en el se aplican varias técnicas para optimizar esta técnica.

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.

4 comentarios en “Occlusion Culling (III de IV)”

  1. Hola, Javier.

    Ante todo, buen blog. Conozco pocos recursos buenos sobre gráficos y XNA en español, pero este es uno de ellos.

    Sin embargo, me queda una duda sobre la OC usando queries. Esto está muy bien para escenas sencillas, pero para escenas de cierta complejidad, ¿no es la latencia de esperar el resultado GPU->CPU un inconveniente similar al que se intenta evitar (la comunicación por el bus)? ¿No empeora cuantos más objetos haya que comprobar?

    Y por último, ¿merece la pena hacer tantos cambios de estado cada frame?

    Sé que hay métodos que utilizan jerarquías de comprobaciones y las posponen para ocultar las latencias, pero siempre me pregunto que hasta qué punto es esto eficiente.

  2. Muchas gracias Mario por el comentario, la respuesta a todas tus preguntas las verás en el post siguiente donde se explica como hacer occlusion culling de forma eficiente, esta es como la versión teórica y sencilla para que se entienda el algoritmo y la que ayudará a la gente a comprender la optimizada.

  3. Hola Javier, muy bueno tus tutoriales, estoy de acuerdo con Mario, casi no hay recursos de XNA en español… Tienes que mostrar la parte de cómo optimizar el occlusion culling

    Edgar

Deja un comentario

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