¿Qué es Ambient Occlusion?

Ambient Occlusion es una técnica de sombreado que simula la atenuación local de la luz debido a la propia geometría de la escena. Es decir, es una aproximación bastante bruta de los métodos de iluminación global que se pueden usar en visualizaciones que no son en tiempo real. Esta técnica se ha hecho popular gracias a que los resultados que proporciona son bastante realistas (provocando una mejor percepción de las formas 3D que se intentan representar) y el coste de cálculo la hace muy interesante para, por ejemplo, visualizar prototipos de animaciones o como un método para mejorar el aspecto del render sin tener que esperar mucho tiempo para pintar la imagen.

ambient_occlusion_01.jpg

Aún así, el tiempo que se necesita para representar correctamente este efecto seguía siendo prohibitivo para juegos u otras aplicaciones interactivas, hasta ahora.

Screen-Space Ambient Occlusion

El Screen-Space Ambient Occlusion (o SSAO) es una aproximación en espacio imagen del AO “habitual”.Las ventajas de que sea en espacio imagen son evidentes:

- El coste del algoritmo es independiente de la complejidad de la escena
- A diferencia de otros intentos de hacer esta técnica en tiempo real, no necesita ningún tipo de pre-proceso.

Además se ejecuta por completo en la GPU, liberando a procesador principal de cualquier trabajo relacionado con esta parte de la renderización.

La base de todo el proceso se centra en el búfer de profundidad de la escena, donde tenemos cierta información de la geometría de la misma. De forma resumida el método consiste en comparar la profundidad del píxel que se está pintando en la pantalla con la de ciertos píxeles alrededor de éste para calcular el grado de oclusión.

Paso 1: El búfer de profundidad

Este es un sencillo primer paso en el que se renderiza la imagen normalmente, pero sacando como color el valor de profundidad lineal de cada punto. Para ello, se debe evitar la deformación provocada por la matriz de proyección, así que esta profundidad se calculará de la siguiente forma:

depth = PosicionViewScape.z / FarZ

Es decir, basta con dividir el componente Z de la posición del vértice en View Space por la distancia del plano de corte lejano de la cámara.

Paso 2: SSAO

Con el paso anterior se puede obtener una textura donde se ha guardado la información de la profundidad de cada píxel de la escena. El siguiente paso consiste en renderizar un quad que ocupe toda la pantalla, y usar la textura obtenida para calcular el grado de oclusión en cada punto.

Para ello, se reconstruye la posición del píxel en view space usando la dirección de visión. Siendo ‘depth’ la profundidad que corresponde a la coordenada de textura actual obtenida en el paso anterior, la posición en view space se obtiene de la siguiente manera:

posicionViewSpace = depth * direccionVision

Más adelante se explicará cómo calcular esta dirección de visión, ya que es uno de los puntos que quedan menos claros en los pocos documentos que hay sobre esta técnica. Ahora mismo sólo interesa saber que mediante esta operación, se recupera la posición 3D en view space del pixel actual.

A continuación se deben generar una cierta cantidad de puntos 3D. Estos puntos deberían ser aleatorios y estar contenidos dentro de una esfera de cierto radio alrededor del punto reconstruido. Por cuestiones de eficiencia se guarda una serie de vectores unitarios pre-calculados, y su número depende de la calidad que se busque y el rendimiento de la técnica. Sumando estos vectores (multiplicados por un radio determinado, el valor del cual depende de cómo sea la escena y del gusto del programador) al punto reconstruido en view space se obtienen los nuevos puntos buscados. Por lo que he podido comprobar, 16 puntos son suficientes para obtener una calidad de imagen aceptable.

vectors.jpg

Y ahora llega el verdadero punto importante de esta técnica. Se debe iterar por cada punto y volverlo a transformar a espacio imagen, es decir, multiplicarlo por la matriz de proyección. Una vez se tiene su posición en espacio imagen, se debe obtener su profundidad de la textura del primer paso y compararla con la del pixel original (se hace la diferencia). De esta diferencia sale el factor de oclusión, que depende de la distancia (cuanto más alejados estén los puntos, mayor será la atenuación del sombreado). Esta atenuación sigue una función a la que han llamado función de oclusión o de bloqueo, que también puede variar según la escena o el gusto de quien esté implementando. En el caso del ejemplo presentado se ha seguido una función de la forma 'factor = 1 / (1 diferencia^2)', pero pueden haber otras opciones válidas.

banding.jpg

Como se puede ver en la imagen, usando los puntos tal cual en el resultado final se notan unas poco estéticas bandas de color, como si el sombreado no fuera suave. Una manera sencilla y poco costosa de solucionar este problema es reflejar los vectores respecto a una cierta normal aleatoria que se puede extraer de otra textura (usando la función reflect, propia de HLSL).

reflected.jpg

El factor de oclusión se va acumulando y es el resultado que se devuelve. Basta multiplicar el factor por el color de la escena para obtener el render final.

Paso 3: El Blur

Como resultado de aplicar el mapa de normales aleatorias para evitar las bandas de color, el efecto del SSAO tiene un aspecto "granulado". Para disimular este problema, es conveniente aplicar un blur o difuminado a toda la pantalla.

Para obtener la mejor calidad de imagen posible es recomendable realizar un blur un poco "inteligente" para evitar tratar ciertas zonas de la imagen. En el primer paso del proceso, en la creación del mapa de profundidades, se puede devolver además de la profundidad de cada punto, su normal en view space. De esta manera se puede condicionar el blur a efectuarse sólo en aquellos puntos donde el ángulo entre la normal del punto actual y la del sample que se trata a la hora de difuminar es menor que un cierto valor límite.

blurred.jpg

ANEXO: Cálculo de la dirección de visión

El cálculo de la dirección de visión es sumamente sencillo si se realiza en view space. Basta con calcular la dirección desde la posición de cámara hacia las cuatro esquinas de plano de recorte lejano del frustum de visión e interpolarla a lo largo de toda la pantalla.
Por tanto, se podría calcular esta dirección en el vertex shader del Paso 2 y DirectX ya se encargaría de interpolarlo. En view space, esta dirección de visión es directamente la posición de cada vértice, pero el cálculo se puede simplificar aún más. En realidad sólo se necesita el vértice superior derecho del plano lejano, ya que las coordenadas de los vértices del quad que se dibuja en este paso son de la forma (-1, 1), (1, 1), (1, -1) y (-1, -1). Por tanto, es suficiente multiplicar estos componentes por el vértice mencionado, que se calcula de la siguiente manera:

farY = tan(cameraFOV) * cameraFarZ
farX = farY * aspectRatio
posicionVertice = (farX, farY, cameraFarZ)

Por tanto, se puede obtener este vértice una vez y pasarlo al shader como parámetro.

Referencias:

http://rgba.scenesp.org/iq/computer/articles/ssao/ssao.htm

http://www.gamedev.net/community/forums/topic.asp?topic_id=463075

http://www.gamedev.net/community/forums/topic.asp?topic_id=488598

Last edited Apr 28, 2008 at 8:13 PM by el_g0e, version 9

Comments

No comments yet.