************************************************************************************************
– 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.
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.
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.