1. Intersección de Rayos en GPU
Un Enfoque en Espacio de Vista
Tutores
Dr. Miguel Katrib Mora
MSc. Ludwig Leonard Méndez
Grupo WebOO
Facultad de Matemática y Computación
Universidad de La Habana
Tesis en opción al título de
Máster en Ciencia de la Computación
Lic. Alejandro Piad Morffis
2. Intersección de Rayos
Encontrar la intersección más cercana
Enumerar todas las intersección
en orden creciente de profundidad
3. Intersección de Rayos
Aplicaciones
Síntesis de imágenes digitales Diseño asistido por computadora
Procesamiento de imágenes médicas Simulaciones físicas
Simulación de fenómenos de iluminación
Visualización de escáneres MRI o TAC
Manipulación de entidades geométricas
Colisiones entre objetos
4. Intersección de Rayos
Problemática
Intersectar millones de rayos en paralelo
Procesamiento en tiempo real
Resolver el problema de intersección de rayos en GPU
Escenas compuestas por cientos de miles de triángulos
6. Intersección de Rayos
Soluciones clásicas
Solución iterativa
Poco eficiente
Estructuras de Datos
Octree
Kd-tree
Binary space partition (BSP)
Bouding volume hierarchy
Enfoques en espacio de objetos
Difíciles de implementar en GPU
Inapropiados para el paralelismo masivo
No explotan optimizaciones en GPU
Requieren tecnologías adicionales (CUDA)
Requieren escenas estáticas
7. CPU vs GPU
CPU
Optimizado para operaciones lógicas Optimizado para operaciones aritméticas
GPU
Arquitectura de von Neumann Arquitectura SIMD
1 ALU por Núcleo 32+ ALU por Núcleo
Eficiencia por cache y pipeline Eficiencia por paralelismo masivo
Ramificaciones eficientes Evitar ramificaciones
No memoria dinámica
No pila
8. Intersección de Rayos
Del espacio de objetos al espacio de vista
Espacio de vistaEspacio de objetos
observador
observador
9. Enfoques en espacio de vista
Apropiado para implementar en GPU
Cada rayo es un punto en una textura
Aprovecha el algoritmo Z-buffer
No requiere tecnologías adicionales
Existen soluciones satisfactorias a otros
problemas basados en espacio de vista
Intersección de Rayos
Del espacio de objetos al espacio de vista
Espacio de vista
11. El proceso de intersección de rayos
Muestreo de puntos a lo largo del rayo (empleando una estructura de datos)
Cálculo del punto exacto de la intersección
12. Intersección en espacio de vista
Muestreo del rayo
Muestreo uniforme
No encuentra todas las intersecciones
13. Intersección en espacio de vista
Muestreo del rayo
Muestreo exhaustivo
Demasiado lento para ejecutar en tiempo real
14. Intersección en espacio de vista
Muestreo del rayo
Muestreo adaptativo
Aprovechar los espacios vacíos para avanzar
15. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
16. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
17. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
18. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
19. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
20. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
21. Intersección en espacio de vista
Muestreo del rayo
Jerarquía de Espacios Vacíos (E-Buffer)
Combinar los espacios vacíos en bloques de mayor tamaño
26. Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
Reducir al problema de
Intersección de intervalos en 1D
27. Construir un árbol de intervalos en GPU
(por cada entrada del A-Buffer)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
28. raíz
Árbol de intervalos
izquierdo
Árbol de intervalos
derecho
Construir un árbol de intervalos en GPU
(por cada entrada del A-Buffer)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
29. raíz
Construir un árbol de intervalos en GPU
(por cada entrada del A-Buffer)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
30. No hay recursión en GPU
Solución: construir un árbol de intervalos aleatorio
los nodos se insertan uno por uno
no hay estrategia de balance
En teoría el costo es lineal pero en la práctica es casi siempre logarítmico
Construir un árbol de intervalos en GPU
(por cada entrada del A-Buffer)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
31. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
32. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
33. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
34. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
35. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
36. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
37. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
38. (Pseudo) árbol de intervalos en GPU (A-PIT)
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
tiempo
39. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
40. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
41. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
42. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
43. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
44. Búsqueda en el A-PIT
Intersección de rayos en GPU
Encontrar los puntos exactos de intersección
En GPU se implementa mediante un recorrido en entreorden
𝑶(𝐥𝐨𝐠 𝑵 + 𝒌)
51. Conclusiones
Estrategia de intersección de rayos en GPU:
Ejecuta en tiempo real para escenas de complejidad media
Totalmente dinámica (geometría, luces, observador)
Calcula intersecciones de forma exacta
No requiere de tecnologías específicas (CUDA, OpenCL)
No existe una relación clara entre la complejidad
de la geometría de la escena y la eficiencia de las estructuras
No es fácil obtener los parámetros de configuración óptimos
52. Trabajo futuro
Adaptar a otras técnicas de visualización
Ajustar automáticamente los parámetros óptimos
Diseñar una estrategia multi-resolución
53. Intersección de Rayos en GPU
Un Enfoque en Espacio de Vista
Tutores
Dr. Miguel Katrib Mora
MSc. Ludwig Leonard Méndez
Grupo WebOO
Facultad de Matemática y Computación
Universidad de La Habana
Tesis en opción al título de
Máster en Ciencia de la Computación
Lic. Alejandro Piad Morffis
54. Pregunta No. 1
En la página 29 se comenta la estrategia utilizada para combinar bloques
adyacentes en la construcción del E-Buffer. El procedimiento se basa en una
heurística golosa y se afirma que obtiene buenos resultados intermedios.
Explique bajo qué criterio se consideran buenos estos resultados
intermedios y analice la complejidad de obtener la distribución óptima.
55. Pregunta No. 1
Nivel 1
Nivel 2
Solución
perfecta
4 𝑘 ⋅ 𝑛 bloques vacíos de nivel 𝑘
59. Pregunta No. 1
Solución perfecta
Solución óptima
Solución golosa
Solución inicial
Obtener todos los intervalos (4𝑛)
Obtener los 𝑛 intervalos más grandes entre los 4𝑛 totales
Obtener los 𝑛 intervalos más grandes entre los 2𝑛 superiores
y luego mezclar con los 𝑛 mejores de los 2𝑛 inferiores
Mezclar cada intervalo con los intervalos correspondientes
a su valor medio
61. Pregunta No. 2
El pseudo interval tree, propuesto para la construcción del A-PIT, es una
aproximación del interval tree que se ajusta para la implementación de la
estructura en GPU. Como estrategia, se acota la altura según la cantidad de
fragmentos. Analice con los ejemplos del capítulo de experimentación
cuántos y en qué situaciones los árboles construidos sobrepasan la cota.
62. Pregunta No. 2
máximo
3 ⋅ log 𝑛
iteraciones
nunca se pierden fragmentos
Las intersecciones siempre son exactas,
pero se puede perder en eficiencia…
66. Intersección de Rayos en GPU
Un Enfoque en Espacio de Vista
Tutores
Dr. Miguel Katrib Mora
MSc. Ludwig Leonard Méndez
Grupo WebOO
Facultad de Matemática y Computación
Universidad de La Habana
Tesis en opción al título de
Máster en Ciencia de la Computación
Lic. Alejandro Piad Morffis
Notas del editor
Buenos días, mi nombre es Alejandro Piad, y tengo el gusto de presentarles mi propuesta de tesis de maestría sobre Intersección de Rayos en GPU, tutoreada por el Dr. Miguel Katrib y el Máster en Ciencias Ludwig Leonard.
La intersección de rayos y elementos geométricos es una de las operaciones más importantes en la geometría computacional. Formalmente puede definirse como: dado un rayo y un conjunto de elementos geométricos, encontrar el primer punto de intersección, o de forma más general, enumerar todos las intersecciones con las fronteras de cada elemento en orden de profundidad.
Son muchas las aplicaciones que tienen su base en esta operación, desde algoritmos de visualización 3D, hasta procesamiento de imágenes médicas y simulaciones físicas. Por tal motivo, la solución de este problema ha sido tratada abudantemente en la literatura.
Sin embargo, en nuestro caso particular, nos enfrentamos a un problema de escala. Queremos intersectar no uno, sino millones de rayos con cientos de miles de objetos geométricos, y todo esto a la mayor velocidad posible, idealmente en tiempo real. Para ello, nos auxiliaremos de dispositivos de procesamiento de vídeo, también denominados GPU.
De modo que el objetivo general de esta investigación consiste en el diseño de una estrategia de intersección de rayos en GPU que funcione en tiempo real.
Para procesar un volumen considerable de rayos, es necesario emplear estructuras de datos que aceleren las operaciones de intersección, ya que una solución iterativa es demasiado ineficiente. Sin embargo, muchas de las estructuras de datos clásicas usadas para este problema nos son factibles de implementar en GPU, debido a que su formulación es generalmente recursiva, y no están diseñadas para el procesamiento en paralelo. Las implementaciones de estas estructuras en GPU emplean tecnologías de cómputo especializadas, o son construidas previamente para escenas estáticas.
Y es que implementar estructuras de datos en GPU es difícil por varios motivos. El más importante, es que el modelo de cómputo en GPU difiere sustancialmente del modelo de cómputo clásico (máquina de Turing con arquitectura de von Neumann). En particular, la CPU está optimizada para ejecutar instrucciones de ciclos y control de flujo, mientras que la GPU está optimizada para operaciones aritméticas, y deben evitarse a toda costa las ramificaciones. Además, no existe memoria dinámica ni pila, es decir, no hay aritmética de punteros ni recursividad.
Esta diferencia en los modelos de cómputo requiere un cambio fundamental de paradigma, que nosotros denominamos ir del espacio de objetos al espacio de vista. En qué consiste esto: las escenas en espacio de objeto están orientadas con respecto a unos ejes arbitrarios, mientras que en espacio de vista, el centro de coordenadas se ubica en el observador.
Esta representación es más apropiada para la GPU ya que las estructuras de datos quedan expresadas en forma de arrays bidimensionales, o texturas, que son muy eficientes de manipular en GPU, y se pueden aprovechar los algoritmos de rasterización muy optimizados que están implementados en todas las tarjetas gráficas.
Una estructura de datos en espacio de vista almacena los elementos geométricos en forma de textura. En nuestro caso, nos basamos en la estructura A-Buffer, que funciona de la siguiente forma: los elementos geométricos son rasterizados en un orden arbitrario, obteniéndose lo que denominamos un conjunto de fragmentos, que no son más que la porción de geometría que atraviesa un píxel particular. Estos fragmentos son ordenados por profundidad e insertados en listas asociadas a cada uno de los píxeles de la vista. De esta forma se puede obtener en orden logarítmico el fragmento que se encuentra a una profundidad determinada en un píxel cualquiera.
Basado en esta estructura, dividimos el problema de intersección de rayos en 2 fases: primero obtener muestras a lo largo del rayo hasta encontrar un elemento geométrico, y luego calcular la intersección exacta con este elemento.
Para la primera fase existen varias soluciones, desde un muestreo uniforme que puede perderse intersecciones, …
… hasta un muestreo exhaustivo que examina cada píxel por donde pasa el rayo, pero es demasiado lento.
Nuestra propuesta consiste en un muestreo adaptativo, que aprovecha la información de los espacios vacíos entre elementos geométricos, para avanzar en el rayo el mayor espacio posible, pero sin perder ninguna intersección.
Para ello se construye una jerarquía de espacios vacíos, donde el primer nivel se obtiene de forma directa como el complemento de los fragmentos del A-Buffer, …
… y luego se combinan bloques vacíos adyacentes para formas bloques de mayor tamaño, …
… hasta obtener una representación del espacio vacío en múltiples resoluciones.
Al procesar un rayo empleando esta estructura, …
… se toma cada muestra a la mayor distancia posible, …
… navegando de forma conveniente entre los distintos niveles de resolución, …
…, hasta llegar a un espacio ocupado por un fragmento de geometría. A esta estructura le hemos denominado E-Buffer, o búfer de espacios vacíos.
Una vez llegados a un espacio ocupado, ….
… queda el problema de obtener el punto real de intersección con la geometría.
Si analizamos la porción de rayo que atraviesa el bloque ocupado por la geometría, la consideramos como un intervalo, …
… y consideramos también los fragmentos de geometría como intervalos que ocupan cierta profundidad en el bloque, …
… podemos reducir el problema de intersección de rayos al problema de intersección de intervalos unidimensional.
Para la solución de este problema, presentamos una estructura de datos denominada árbol de intervalos, que se construye en GPU por cada píxel en el A-Buffer.
El árbol de intervalos tiene una definición recursiva: en cada nodo se calcula un valor llamado determinante, que no es más que el punto medio de todos los intervalos, y se almacenan los intervalos que intersectan con él, …
… y este proceso se repite recursivamente con los intervalos que quedan a cada lado del determinante.
El problema de esta estructura es que su naturaleza recursiva la hace infactible para implementar en GPU, por lo que diseñamos una variante no recursiva, donde los intervalos se insertan uno a uno a medida que son procesados, y no se define una estrategia de balance explícita, pero se aprovecha que los intervalos son procesados en un orden arbitrario, lo que provoca en la práctica una altura logarítmica con casi total seguridad.
La estrategia de construcción funciona de la siguiente manera:
El primer intervalo procesado se almacena en la raíz, …
… luego cada nuevo intervalo se inserta a la izquierda o a la derecha según el determinante, …
Si algún intervalo intersecta el determinante en un nodo, se almacena en ese mismo nodo, …
Este proceso se repite …
Hasta que finalmente se obtiene un árbol de intervalos completo. A esta estructura le hemos puesto del nombre de A-PIT.
Para consultar el A-PIT, se tiene el intervalo definido por un rayo, …
Este se compara con el determinante en cada nodo, y si lo intersecta, …
Se desciende recursivamente por ambos hijos, …
En caso contrario se desciende solamente por el hijo correspondiente, …
Y en cada nodo se compara con todos los intervalos almacenados en dicho nodo.
Para implementar esta estrategia en GPU, se elimina la recursión mediante el uso de un recorrido en entre-orden, donde los enlaces a seguir han sido precalculados durante la construcción del A-PIT.
Finalmente, encontramos que ambas estructuras tienen una intersección entre las operaciones de construcción, por lo que proponemos una construcción conjunta que disminuye el costo computacional. Esta consiste en construir primero el A-PIT, que no necesita ordenación, y luego obtener directamente los fragmentos ordenados para el E-Buffer mediante el recorrido en entre-orden que explicamos. A esta combinación le hemos dado el nombre de E-PIT.
Para evaluar la eficiencia de ambas estructuras diseñadas, presentamos su aplicación a uno de los algoritmos de visualización más conocidos, denominado Raytracing. Se muestran 6 escenas de variada complejidad geométrica con reflexiones y refracciones calculadas en tiempo real. Para cada una reportamos los mejores tiempos obtenidos con alguno de los parámetros probados. Aunque las imágenes no permiten apreciarlo, estas escenas están animadas, y todas los efectos ópticos se calculan en tiempo real de forma exacta a la velocidad indicada en cada cuadro.
Aquí tenemos los tiempos de procesamiento para cada estructura por separado, y para su combinación, de donde podemos obtener algunas conclusiones, …
En primer lugar, en las escenas abiertas el E-Buffer obtiene los mejores tiempos, ya que en estas escenas es más probable que existan espacios vacíos de gran tamaño que puedan ser explotados. En cambio, en las escenas interiores no parece ser tan importante un muestreo adaptativo.
Sin embargo, el resultado más positivo es que la estrategia combinada es de forma general la mejor solución en todos los casos.
Aunque para escenas particulares pueden obtenerse mejores tiempos con alguna otra combinación de parámetros.
A modo de conclusión podemos decir que hemos desarrollado la única estrategia de intersección de rayos en GPU, hasta donde conocemos, que obtiene intersecciones exactas en escenas totalmente dinámicas, donde tanto la geometría como las luces y el observador se pueden mover en cada cuadro, y todo esto en tiempo real, sin necesidad de tecnologías de cómputo especializadas. La principal deficiencia a nuestro modo de ver es que no existe una relación obvia entre la complejidad de la escena y la eficiencia de las estructuras, por lo que no es fácil obtener los parámetros de configuración óptimos. De hecho, el costo computacional parece depender más de la distribución de la geometría en la escena, que de la cantidad de triángulos.
Por tanto, nos proponemos como continuidad en la investigación, además de la implementación de otros algoritmos como Raytracing que puedan beneficiarse de una intersección de rayos eficiente, el estudio de formas de ajustar los parámetros de las estructuras de datos en cada escena. Una de estas formas consiste en una estrategia multi-resolución, donde existan diferentes instancias del E-Buffer, por ejemplo, con diferentes resoluciones, que se usen para las porciones de la escena más convenientes. En esta línea ya hemos obtenido resultados satisfactorios en una tesis de diploma recién presentada, que serán adoptados próximamente.
Eso es todo, muchas gracias por su atención
El problema a que se refiere la pregunta es el siguiente: tenemos los espacios vacíos de primer nivel, y queremos computar los del nivel superior. Hay que notar que aunque estamos usando una representación en 2 dimensiones, en realidad hay 4 listas de espacios vacíos, ubicadas en una submatriz de 2x2. Evidentemente, la estrategia perfecta, consiste en intersectar cada uno de los espacios vacíos en cada lista. El problema con esta estrategia es que se pueden obtener en el caso peor 4 veces la cantidad de espacios vacíos en el nivel superior, y esto escala de forma exponencial. Dado que no tenemos memoria dinámica, necesitamos predecir de antemano el tamaño de todas las listas, lo queremos es una estrategia que produzca a lo sumo n bloques vacíos de la mezcla de los 4n bloques de nivel inferior.
En este caso, se muestran los mejores N bloques, que son aquellos de mayor tamaño. Esta solución óptima también es infactible de implementar en GPU, pues hace falta computar todos los bloques, y ordenar, para quedarse con los mejores.
Entonces nosotros concebimos inicialmente una heurística que simplemente escogía para cada bloque de la primera lista, aquellos bloques en las demás listas que intersectan con su punto medio. Pero esta estrategia provoca que se rechacen muchos bloques.
Diseñamos entonces una estrategia golosa que encuentra la mejor combinación entre las primeras 2 listas, es decir, para cada bloque en la primera lista, aquel en la segunda lista que da la intersección más grande, y luego entre la lista resultante y la tercera, y luego con la cuarta lista. Como los intervalos están ordenados cada uno de estos pasos puede hacerse con costo lineal. Aunque esta estrategia no garantiza los mejores n bloques de los 4n, en la práctica se acerca bastante.
Tenemos entonces 4 posibles estrategias, y vamos a realizar una simulación en CPU para compararlas. Para ello generamos aleatoriamente listas de bloques vacíos, y las combinamos con cada estrategia, midiendo cuál es el porciento del espacio vacío que se recupera en cada una, con respecto a la solución perfecta.
Aquí tenemos una gráfica de este comportamiento, en función de la cantidad de capas de profundidad que existan en la escena. Las escenas presentadas tienen entre 5 y 10 capas de profundidad. Nótese que la estrategia óptima de los mejores n bloques a lo sumo puede recuperar el 90% del espacio vacío total, mientras que la solución golosa que presentamos recupera poco más del 80%, por lo que consideramos que es un resultado muy bueno considerando el costo computacional lineal. Además hay que tener en cuenta que en estos experimentos se generaron bloques vacíos aleatorios, que no necesariamente representan la geometría real de una escena. En las escenas reales las listas adyacentes son casi idénticas, ya que fragmentos cercanos corresponden casi siempre a la misma superficie. Por lo tanto aquí tenemos lo que puede considerarse el caso peor, y en la práctica el espacio recuperado debe ser más cercano al óptimo.
El algoritmo de inserción en el A-PIT que presentamos es el siguiente. Lo que sucede es que este ciclo básicamente es un while true, que como sabemos no es posible de implementar en GPU, por lo tanto se le pone una cota fija máxima a la cantidad de iteraciones, que en este caso es 3 log (n). Ahora, cuando un árbol de intervalos se pasa de la cota, no se pierden fragmentos, lo que sucede es que se almacenan en la última hoja de la rama correspondiente. Lo que puede suceder entonces es que producto de este exceso de intervalos, se pierda en eficiencia de las consultas.
Aquí se muestra para cada una de las escenas usadas en la experimentación cuáles son los píxeles donde los árboles están desbalanceados, es decir, existe un exceso de intervalos en las hojas. Como se puede ver, para una cota de 3 log N ya prácticamente es imposible que esto suceda.
Por otro lado, a medida que aumenta la cota máxima, aumenta ligeramente el costo de construcción, por lo que es deseable emplear una cota lo más ajustada posible.
Para analizar el comportamiento de esta cota de forma estadística, hacemos otra simulación Generamos 20,000 árboles aleatorios, insertamos 1000 intervalos en cada uno, y medimos la altura resultante. Como puede apreciarse, la altura tiene una distribución característica normal, con una media de 15 nodos y una varianza de 6. De modo que 3 log N en este caso es una cota de orden sigma 6, por lo que la probabilidad de generar un árbol de mayor altura que la cota es prácticamente nula.