Han pasado ya varios años desde que NVIDIA con las RTX 20 prometiera revolucionar los gráficos en los videojuegos a través del Ray Tracing, han pasado ya varios años y parece ser la eterna promesa. ¿Qué es el trazado de rayos y qué causas impiden que despegue su uso en los videojuegos? Pues eso y mucho más os vamos a contar en este artículo, donde dejaremos la propaganda de lado para explicaros la verdad sobre el Ray Tracing.
Ray Tracing versus Rasterización
Vamos a ser breves y concisos, a la hora de calcular la geometría de una escena en tres dimensiones. Ambos algoritmos son idénticos. Sin embargo, cuando llega el momento de representar el espacio 3D en una ventana en dos dimensiones, es cuando el Ray Tracing y la Rasterización divergen por completo. El algoritmo clásico utilizado en los videojuegos es el de la rasterización, por el hecho que tiene un coste computacional mucho más bajo, lo que le permite alcanzar las velocidades adecuadas para los videojuegos.
- Rasterización:
- Proyecta los triángulos en la pantalla (espacio 2D).
- Los fragmentos dentro de cada triángulo se interpolan para obtener color e iluminación.
- Se usa un búfer de profundidad para gestionar la visibilidad (lo que está más cerca sobrescribe lo que está detrás).
- Ray Tracing:
- La geometría permanece en su espacio tridimensional original.
- Se disparan rayos que recorren la escena en busca de intersecciones con los triángulo
- No necesita un búfer de profundidad porque la intersección más cercana al origen del rayo define qué se ve
El Ray Tracing conserva la geometría en su estado tridimensional original, en lugar de proyectarla y «destruirla» en una representación 2D como hace la rasterización. Esto tiene varias ventajas clave al no haber pérdida de la información espacial.
- Permite realizar sombras y efectos de oclusión sin trucos.
- Reflejos y refracciones exactas
- Iluminación global precisa.
- No depender del búfer de profundidad para la visibilidad.
- Se pueden usar las estructuras de aceleración del Ray Tracing, como el BVH, pero una mayor precisión en la simulación de la física de la escena, la detección de colisiones y otros fenómenos que requieren de la información espacial de la escena.
Todo ello convierte al Ray Tracing en una especie de panacea para los gráficos 3D. Entonces, ¿por qué no ha reemplazado a la rasterización todavía?
¿Qué marca el rendimiento en el Ray Tracing?
El marketing de NVIDIA y AMD, sobre todo la primera, ha sido muy inteligente al ocultar al gran público cuál es la medida de rendimiento en el Ray Tracing. Nos lo suelen decir en forma del número de intersecciones que pueden hacer las unidades de función fija para la intersección o, en su defecto, la potencia de cálculo equivalente, por ejemplo, los llamados RT FLOPS. Todo lleva a la falsa idea de que en el trazado de rayos los problemas de rendimiento se pueden solucionar echando más fuerza bruta.
Sin embargo, los TFLOPS solo nos indican la potencia aritmética y el mayor problema del Ray Tracing tiene que ver con los accesos a memoria, debido a que el recorrido de cada rayo por la escena requiere que la GPU haga varios accesos a VRAM. Esto no es un problema si estos accesos son secuenciales en la memoria, y por lo tanto, no ocasionan un salto a otra área cambiando los registros de contador de programa de la GPU. Esto es importante de cara a la caché, por el hecho de que esta almacena una copia de las direcciones de memoria cercanas a donde apunta la CPU en cada momento. Un salto de forma aleatoria termina por hacer que el número de aciertos en la caché disminuya.
No olvidemos que la idea de las cachés es poder llegar a un dato concreto en el menor tiempo posible. La contrapartida es que la búsqueda en los diferentes niveles de caché por un dato que no se encuentra en ellas en muchos sistemas es mucho más costosa que buscar la información directamente en la memoria. Esto se aplica a cualquier tipo de microprocesador con una RAM asociada, ya sea general o local, como la VRAM de las tarjetas gráficas.
Rayos coherentes versus incoherentes
Hemos de partir del hecho de que la construcción del árbol BVH se hace desde la perspectiva de la cámara; sin embargo, no todos los rayos en la escena, especialmente los generados de forma indirecta, se generan desde dicha perspectiva. Lo que provoca que sean incoherentes. Entonces, ¿cuáles son coherentes? Los llamados rayos primarios y que responden a la pregunta de si hay una intersección con un objeto cuando se lanza un rayo desde la cámara. Estos se originan desde un mismo punto, en este caso, la cámara, y viajan casi en la misma dirección.
Los rayos secundarios o incoherentes son aquellos que no se generan desde la perspectiva de la cámara. Dichos rayos se dispersan de forma impredecible por la escena en varias direcciones, lo que hace que los accesos a la memoria al recorrer el árbol BVH sean erráticos e ineficientes. Esto es un problema por el hecho que la ventaja del Ray Tracing sobre la Rasterización a nivel visual se encuentra precisamente en la generación de los rayos no coherentes con la cámara.
Tipo de rayo | Coherencia | Observaciones |
---|---|---|
Rayos reflectos o refractados | No | Los diferentes ángulos provocan divergencia en el recorrido del árbol BVH |
Rayos de iluminación difusa/global | No | Se dispersan en direcciones aleatorias, lo que hace que sus trayectorias de recorriido del BVH sean impredecibles. |
Rayos de sombras suaves | No | El rayo de sombra de cada píxel puede ir a una parte diferente de la luz, lo que genera más divergencia. |
Rayos con rebotes múltiples | No | Cada nuevo rebote agrega más aleatoriedad, lo que los vuelve altamente incoherentes. |
Desde el punto de vista de la memoria, los rayos coherentes acceden a la memoria de forma secuencial, lo que permite que el trazado en el BVH sea predecible, lo que lleva a más aciertos en la caché y una latencia mucho menor en el acceso a la información. Los rayos incoherentes acceden de forma aleatoria, haciendo saltos impredecibles en el acceso al árbol BVH, lo que lleva a menor rendimiento, más fallos en la caché y con ello una latencia mucho mayor.
El recorrido del árbol BVH es logarítmico
El árbol BVH es una estructura de datos en forma de árbol que contiene la información de cómo se encuentra organizada la geometría de la escena desde la perspectiva de la cámara. Dichos árboles pueden ser de 2 ramas (BVH estándar), 4 ramas (BHV4) u 8 ramas (BVH8). Existen otro tipo de estructuras de datos como los KD-Tree, pero los Bounding Volume Hierarchy son los más utilizados en videojuegos, siendo la de 4 ramas o nodos la estándar en las diferentes GPU de NVIDIA y AMD.
Por cada nodo del árbol BVH se hace una intersección para comprobar si hay un objeto en esa zona de la escena. Si lo hay, se toma un camino; si no lo hay, entonces se para el recorrido y se apunta que el rayo no impacta con nada. Esto permite eliminar a una gran cantidad de geometría sobre la que se ha de realizar la intersección con cada nivel del árbol que se recorre. Si es un BVH de 2 nodos, el 50%, si lo es de 4 nodos, el 75%. Un BVH8 descarta el 87.5% de la geometría.
El más utilizado es, en este caso, el BVH4, ya que el número de niveles en el árbol será más bajo que en un BVH con menos hijos, lo que significa que puedes llegar a las hojas del árbol con un número relativamente bajo de pasos. Por lógica, un árbol BVH8 es más rápido y descarta un mayor número de geometrías sobre la que realizar las intersecciones. Sin embargo, el uso de uno u otro dependerá del tipo de escena a renderizar.
BVH4 versus BVH8
Imaginad por un momento una escena en un videojuego, la cual es una habitación con una gran cantidad de objetos dispersos por ella de forma desordenada, como, por ejemplo, sillas, mesas, lámparas, las cuales se encuentran cercanos, pero no están agrupados en grandes volúmenes que les permitan agruparse en un BVH de gran tamaño. En este caso, un BVH4 es una mejor solución que un BVH8. Esto lo hace ideal para juegos que utilicen Ray Tracing que se basen en niveles cerrados con muchos objetos pequeños, como por ejemplo Resident Evil.
El tipo de escenas ideales donde se puede usar un BVH8 como árbol para organizar la escena es un mundo abierto, en este caso debéis imaginar escenas como un bosque denso, una ciudad futurista o cualquiera donde haya objetos muy grandes y densamente agrupados, como edificios en una ciudad o árboles y rocas en un paisaje natural. En este caso, un ejemplo claro donde esta forma de organizar la escena en el Ray Tracing sería ideal son juegos como Red Dead Redemption 2.
Incluso dentro de un mismo juego se pueden utilizar diferentes estrategias a la hora de construir el árbol BVH, para una mayor eficiencia en el rendimiento del videojuego. No olvidemos que la idea detrás del BVH es reducir al máximo la cantidad de intersecciones a calcular en la escena.
Shaders en el Ray Tracing híbrido a tiempo real.
En realidad, lo que se ha hecho es adaptar las API gráficas por el lado del software (DirectX, Vulkan…) a poder generar escenas con Ray Tracing, pero no se trata del algoritmo completo, ya que una buena parte de los píxeles se ven resueltos a través de la rasterización. Al fin y al cabo, se trata de renderizar la misma escena, pero desde los dos algoritmos y el Ray Tracing se utiliza solo para los rayos incoherentes, ya que estos no se pueden calcular desde la rasterización por la pérdida de la información en el proceso.
Tarea | Método usado | Motivo |
---|---|---|
Geometría y visibilidad | Rasterización | Los objetos principales tienen estructuras regulares y son fáciles de proyectar en 2D. |
Sombras | Rasterización para sombras duras, Ray Tracing para sombras suaves | Las sombras con mapas son rápidas, pero Ray Tracing se usa para sombras suaves o transparentes. |
Reflejos | Ray Tracing | Los reflejos en superficies curvas o espejos requieren rayos secundarios incoherentes. |
Iluminación global | Ray Tracing (limitado) o rasterización con trucos | Se usan mapas de luz precomputados y Ray Tracing solo para ajustes en tiempo real. |
Transparencias y refracciones | Ray Tracing | Los rayos deben atravesar múltiples superficies de forma impredecible. |
Sin embargo, el control sobre el Ray Tracing híbrido es total, es por ello que se utiliza una serie de shaders de computación, pero orientados al Ray Tracing:
Shader | Vulkan | DirectX 12 (DXR) | Uso |
---|---|---|---|
Ray Generation Shader | raygen | RayGeneration | Se encarga de lanzar los rayos iniciales desde la cámara o desde una fuente específica. |
Intersection Shader | intersection | Intersection | Se utiliza para determinar la intersección de un rayo con la geometría de la escena. |
Closest Hit Shader | closesthit | ClosestHit | Calcula el color o material del píxel cuando el rayo llega a la intersección más cercana. |
Any Hit Shader | anyhit | AnyHit | Se ejecuta cuando el rayo interseca cualquier objeto, pero no necesariamente el más cercano. |
Miss Shader | miss | Miss | Se ejecuta cuando un rayo no interseca ningún objeto, usado para determinar el color de fondo. |
Callable Shader | callable | Callable | Permite invocar shaders personalizados para realizar operaciones especializadas como iluminación global. |
Aquí lo más importante de cara al rendimiento es entender que en el Ray Tracing híbrido los objetos solo emiten rayos si esto se especifica de forma explícita, por lo que no debemos esperar la perfección del trazado de rayos puro y duro en los videojuegos, ya que dependiendo de las necesidades de rendimiento del juego nos podemos encontrar que ciertos problemas relacionados con la iluminación indirecta se sigan solucionando a través de técnicas de rasterización.
¿Por qué no se usa aún el Ray Tracing puro en videojuegos?
La respuesta sencilla es por el hecho que para la iluminación directa y la visibilidad a nivel computacional la rasterización es más rápida, sin embargo, el Ray Tracing tiene un oscuro secreto: el ruido. El cual se debe a que no hay suficientes muestras por píxel. ¿Qué significa esto? En el Ray Tracing para determinar la visibilidad de un píxel, se lanzan varias muestras de rayos. Cada uno de estos rayos puede tener diferentes trayectorias (es decir, una dirección diferente), lo que se conoce como muestras de píxel. Por ejemplo, se pueden lanzar 1000 rayos para un solo píxel, y estos rayos pueden explorar diferentes trayectorias (como rayos de iluminación directa, reflejos, refracciones, etc.). Cada uno de estos rayos puede interceptar un objeto de manera diferente, y la combinación de todas estas intersecciones da como resultado el color y la iluminación de ese píxel.
¿Parece sencillo, no? Pues aquí es donde empiezan los problemas, ya que en el Ray Tracing, los rayos no son deterministas, lo que significa que no siempre generarán la misma luz o interacción de la misma manera. Si no se lanzan suficientes muestras por píxel, la imagen final tiene variabilidad en cómo se calcula la iluminación o los efectos de reflexión y refracción. Esto provoca ruido visual, ya que no tenemos suficientes resultados como para tener un promedio preciso del valor de cada píxel.
De ahí a que se utilice la rasterización en los videojuegos para calcular la visibilidad desde la cámara, es mucho más rápido y solo hay que ver el coste computacional de juegos de hace veinte años relanzados con un pipeline completo dedicado al Ray Tracing. Por lo que en conclusión, todavía estamos muy lejos de llegar a los niveles de calidad del trazado de rayos en el cine.