Adaptación de Algoritmos Geométricos al uso de GPU Programable Rafael J. Segura, José Mª Noguera, Carlos Ogáyar, Antonio J. Rueda, Francisco Feito Depto. de Informática, Universidad de Jaén, España {rsegura, jnoguera, cogayar, ajrueda, ffeito}@ujaen.es Resumen En este artículo se presentan los objetivos iniciales y resultados obtenidos en el proyecto de excelencia de la Junta de Andalucía titulado "Adaptación de Algoritmos Geométricos al uso de Hardware gráfico programable. Aplicaciones". Se muestran en primer lugar los objetivos del proyecto, los principales resultados obtenidos centrados en la resolución de algoritmos geométricos, tanto en el uso de Shaders como en el uso de CUDA, y los trabajos futuros, que se centran fundamentalmente en la aplicación de algunos de los algoritmos. 1. Introducción y objetivos. La rápida evolución que se está produciendo en el hardware gráfico está permitiendo en los últimos años un salto cuantitativo y cualitativo en el rendimiento de las aplicaciones gráficas. De una parte, la inclusión de procesadores gráficos cada vez más potentes y capaces de realizar operaciones específicas utilizadas en Informática Gráfica permite la optimización de muchos de los métodos empleados. De otra parte, la posibilidad del desarrollador de añadir programas en el proceso de rendering añade la posibilidad de realizar de una manera mucho más rápida algoritmos que han de procesarse para los modelos gráficos [1]. Muchos autores han percibido la presencia de las GPUs (Graphics Processing Unit) en los equipos actuales como una posibilidad para resolver problemas de tipo genérico, o simplemente para paralelizar algunos procesos utilizando de manera conjunta la CPU y la GPU [2]. La GPU presenta mayor capacidad para realizar cálculos en coma flotante que la CPU, si bien para que un algoritmo pueda adaptarse a la GPU debe ser paralelizable. Las GPUs actuales permiten modificar el pipeline de visualización a través de vertex, geometry y fragment shaders sobre datos geométricos, pudiendo utilizarse los shaders como programas de cálculo de propósito general que serán utilizados en paralelo. Adicionalmente, las nuevas arquitecturas propuestas para el hardware gráfico [3,4] están encaminadas hacia el procesamiento masivo de datos mediante el uso de la GPU, permitiendo el aumento de la capacidad de procesos de ordenadores de sobremesa a un escaso coste adicional. Todo lo anterior dio lugar a que un grupo de investigadores del Grupo de Investigación TIC-144 “Geomática e Informática Gráfica” de la Universidad de Jaén solicitaran financiación a través de la Convocatoria de Proyectos de Excelencia de la Junta de Andalucía para el año 2006 para la realización del proyecto “Adaptación de Algoritmos Geométricos al uso de Hardware gráfico programable. Aplicaciones (P06-TIC-01403)”. El proyecto fue seleccionado para su ejecución mediante resolución de 7 de marzo de 2007 y dotado con una financiación a tres años de 157.336,30 €. El proyecto se centra la adaptación de algoritmos básicos en Informática Gráfica y Geometría Computacional hacia el uso de Hardware Gráfico Programable. Los objetivos fundamentales del proyecto se han centrado en el aprovechamiento de las potencialidades de la GPU desde una doble perspectiva: Realización de un estudio sobre el aprovechamiento de las potencialidades de la GPU para la resolución de problemas geométricos. Este estudio debe partir de la propuesta de un sistema formal de representación y especificación de soluciones que tenga en cuenta el procesamiento vectorial de la GPU. Aplicación de algoritmos basados en GPU hacia su integración en Sistemas de Información Geográfica. El interés de este tipo de sistemas hoy en día es muy alto, siendo de aplicación en múltiples ámbitos, tanto en el sector público como en el privado. Alrededor de estos dos objetivos principales del proyecto pueden formularse una serie de objetivos que pasamos a enumerar a continuación: Construcción de una plataforma software que incluya el tratamiento de modelos tridimensionales mediante el uso del hardware gráfico programable. Construcción de software para el tratamiento de modelos tridimensionales en dispositivos móviles (ordenadores de mano y teléfonos móviles), teniendo en cuenta las restricciones de los mismos. Integración de algoritmos basados en el uso de la GPU en Sistemas de Información Geográfica. Problemas como el cálculo de superficies, la determinación de inclusión, el conteo de unidades, etc. son problemas básicos en este tipo de sistemas existiendo buenas soluciones, aunque algunas de ellas siguen siendo costosas en términos computacionales. El objetivo se centra pues en la búsqueda de alternativas a estas soluciones que utilicen las capacidades de la GPU para reducir el coste. Aplicación de los algoritmos hacia sistemas de navegación. Este tipo de sistemas se ha popularizado en los últimos años gracias al abaratamiento de los costes de los receptores de GPS, así como a la disponibilidad de cartografía adecuada. Sin embargo, en la mayoría de los casos la información mostrada es escasa e inexacta, ya que se trata de información 2D o, en el mejor de los casos, un falso 3D. El objetivo que se plantea en este proyecto consiste en la búsqueda de algoritmos y métodos que permitan una visualización 3D del terreno en dispositivos móviles asegurando un tiempo de respuesta apropiado. 2. Uso de shaders para la resolución de algoritmos geométricos. En esta sección vamos a describir muy brevemente algunas de las soluciones propuestas para la resolución de algunos algoritmos geométricos mediante el uso de la GPU. 2.1. Inclusión de puntos. Uno de los tests más importantes que surgen dentro del campo de la Geometría Computacional y la Informática Gráfica es la inclusión de un punto del espacio dentro de un sólido. En [5], Ogáyar et. al. realizan una profunda revisión sobre las posibles soluciones de este problema básico, centradas todas ellas en el uso de soluciones en CPU. Dentro del ámbito del proyecto se han propuesto diferentes soluciones para resolver el problema mediante el hardware gráfico, todas ellas basadas en el algoritmo descrito en [6] y que aparece en el Algoritmo 1. Como datos de entrada del problema tomaremos un sólido definido por su frontera, y un punto. Para optimizar los resultados y dado que el hardware gráfico está diseñado especialmente para el tratamiento de triángulos, en el caso de que la frontera del sólido contenga polígonos de más de tres lados se procederá a un recubrimiento de dichos polígonos mediante triángulos tal y como se expone en [5]. A partir de estos datos de entrada, se han propuesto diferentes soluciones que podemos clasificar en dos categorías: de ping-pong. Así, en la primera pasada se estudia la inclusión del punto en cada uno de los tetraedros. En una segunda pasada se aplica la técnica de reducción para obtener un único valor resultado. Los casos degenerados son detectados enviando los puntos que los ocasionan a una posición específica del frame-buffer, que es consultada al final del proceso. Datos inclusión 0 ... T4 T3 T2 T1 Datos de tetraedros (display list, vertex array, etc.) Vertex Program 0 1 0.1 1 0.2 0 1 0 1 0 0 1 0 0 0.3 Estados de inclusión (framebuffer) Figura 1. Esquema general de la solución básica out-of-core para el problema de inclusión de puntos. La aparición de Shader Model 4 y la arquitectura de la serie 8 de Nvidia, permiten optimizar la solución inicial mediante el uso de geometry shaders. En este caso no es necesario realizar ningún tipo de codificación de la información, pudiendo enviarse directamente la malla de triángulos utilizando alguna primitiva indexada. El resultado es nuevamente almacenado en el frame-buffer, y en una segunda pasada se realiza la reducción de los resultados obtenidos. En el segundo conjunto de soluciones, se realiza una codificación de la malla de triángulos en textura, construyendo un tetra-tree [7]. Dicha estructura espacial permite dividir el espacio mediante tetraedros, clasificando en cada nodo los triángulos de la malla cuyos tetraedros del recubrimiento pasan por esa zona del espacio. Ello permite una consulta rápida sobre un reducido conjunto de triángulos. En este caso, no es necesario iterar sobre los puntos, sino sobre los triángulos de la malla, permitiendo resolver el problema, a un mínimo coste adicional, sobre un mayor número de puntos, lo que es especialmente útil en problemas de detección de colisión y en operaciones booleanas entre sólidos. Soluciones out-of-core. Soluciones in-core. En el primer tipo de soluciones, los vértices y triángulos del sólido están almacenados en la CPU y son enviados al hardware a través de algunas de las estructuras de datos disponibles (display lists, vertex arrays, …). Con el fin de optimizar el uso del ancho de banda del bus que une el sistema gráfico con el resto del sistema, dichas estructuras se completan al máximo disponible, de manera que se ha mostrado experimentalmente que para sólidos con hasta 200000 triángulos el comportamiento de la solución es aceptable. En la primera de las soluciones, los vértices de los tetraedros se codifican en algunos de los atributos de los vértices, y se envían vértices con una posición específica, con el fin de identificar los tetraedros (figura 1). El algoritmo funciona utilizando la conocida técnica Figura 2. Esquema general de la solución básica in-core para el problema de inclusión de puntos Se han diseñado diferentes soluciones para la resolución del problema de inclusión de nubes de partículas en sólidos, si bien, el esquema básico aparece en la Figura 2. La codificación del Tetra-tree del sólido se almacena utilizando una estructura similar a la de una tabla hash en una o varias texturas, para permitir el desbordamiento que pueda surgir en aquellas zonas del sólido con una alta densidad de triángulos. A partir de ese esquema básico pueden configurarse diferentes soluciones de manera que las tareas realizadas por la GPU se realicen completamente en el Fragment Shader, en Vertex Shader y Fragment Shader, en CPU+Fragment Shader, en Fragment Shader + Fragment Shader, o bien utilizando Geometry Shader + Fragment Shader. En cualquier caso, el esquema de las soluciones es similar. En una primera etapa se determina el tetra-cono en el que se encuentra cada partícula. En la siguiente etapa del pipeline, se consulta en el Tetra-tree la lista de tetraedros que se encuentran en dicho tetra-cono y se determina la inclusión de la partícula en dichos tetraedros. En la Tabla 1 se muestran algunos resultados obtenidos con los algoritmos propuestos, tanto en soluciones out-ofcore como soluciones in-core. renderiza a una loncha de la textura 3D correspondiente. Al final del proceso, se recupera la textura 3D. Figura 3. Esquema general del algoritmo de voxelización. El principal inconveniente de esta solución es que se aprovechan poco las capacidades gráficas. Podemos optimizar el resultado provocando que el cálculo de las secciones de cada triángulo se calculo en la GPU. Para ello hay que tener en cuenta que, dependiendo de la posición de los vértices del recubrimiento respecto al punto de referencia considerado a la hora de construir el recubrimiento, pueden producirse situaciones en las que la intersección del tetraedro con la loncha correspondiente genere un cuadrilátero (figura 4). Por tanto, es necesario generar para cada triángulo de la malla dos triángulos que serán rasterizados o no en función de la loncha en la que nos encontremos. Tabla 1. Tiempos de algoritmos de inclusión para 1000 puntos. 2.2. Voxelización. La representación basada en vóxeles tiene una gran importancia en aplicaciones tales como visualizaciones médicas o para visualizar objetos que difícilmente se pueden representar a partir de sus fronteras, tales como humo o fuego. Además los vóxeles son entidades tridimensionales que pueden almacenar información volumétrica, al contrario que las representaciones basadas en fronteras (tales como mallas) que tan solo describen la supercie de los objetos. Las representaciones basadas en vóxeles ofrecen información de cómo se dispone la materia en el espacio. El recubrimiento de sólidos mediante símplices proporciona un método prácticamente inmediato para obtener la descomposición espacial del sólido mediante vóxeles. Para ello basta con resolver la voxelización de cada uno de los tetraedros del recubrimiento (algoritmo 2) y posteriormente componer la solución final mediante la unión de las soluciones parciales (Figura 3). Dentro del proyecto se han diseñado diferentes soluciones que hace uso de las nuevas capacidades gráficas para obtener soluciones mejoradas de este problema. La primera solución hace uso intensivo de operaciones gráficas aceleradas. Para ello, en lugar de iterar sobre la voxelización de cada uno de los tetraedros, se calcula en CPU la intersección de cada loncha de la voxelización con todos y cada uno de los tetraedros del recubrimiento. A continuación, se dibujan los triángulos obtenidos y se Figura 4. Casos posibles en la rasterización del tetraedro. A los triángulos generados (dos por cada tetraedro), se les asocian como parámetros varying los vértices del tetraedro al que pertenecen. Además, se envía como parámetro uniforme la altura de la loncha que se desea generar. Para cada loncha ys el Vertex Shader se encarga de ordenar los vértices según su coordenada y, y de calcular la proyección de cada uno los triángulos en el plano y=ys. Aquellos tetraedros cuyas coordenadas ymax e ymin quedan fuera de la loncha ys se descartan, enviando sus coordenadas fuera del volumen de vista, con el fin de que en la siguiente etapa del proceso de visualización sean descartados por el hardware gráfico. En el Fragment Shader pueden codificarse las diferentes funciones de aspecto que se deseen (figura 5.a) Nuevamente la aparición de Shader Model 4 permite simplificar el proceso anteriormente descrito. En este caso, podemos hacer uso de las nuevas funcionalidades como la instanciación de geometría y los geometry shaders[9]. En esta implementación, tan solo se envían al cauce gráfico dos triángulos por loncha. La GPU se encarga de generar tantas copias de los triángulos como sean necesarias (nuevamente dos triángulos por tetraedro). En las coordenadas de cada vértice se indican un índice del punto (0-3) y un identificador del triángulo al que pertenece el punto (0-1). Este esquema se muestra en la figura 5.b. Nótese que todos los triángulos instanciados son iguales, por lo que no es posible pasar los tetraedros como parámetro de los vértices. En lugar de ello, se genera una textura 2D de flotantes que contiene todos los vértices de los tetraedros. Esta textura almacena consecutivamente los vértices ABCD de cada uno de los tetraedros del objeto a voxelizar, apareciendo ordenados en cada tetraedro por su coordenada y. La textura es enviada una única vez a la GPU, en donde se almacena y es reutiliza para el procesado de cada loncha. De esta forma, tan solo se ha de enviar la geometría a la GPU una vez, y no sucesivas veces por cada loncha. Una vez enviada la textura a la GPU puede liberarse su espacio de la memoria principal. Además, de esta manera es posible obtener nuevas voxelizaciones del sólido, o voxelizaciones parciales del mismo con diferente resolución, sin necesidad de volcar nuevamente la información. Gracias al procesador de geometría, los cálculos de interpolación que antes se hacían por vértice, ahora pueden optimizarse a nivel de primitiva. Esto permite que si el tetraedro no corta la loncha actual, puedan descartarse completamente los dos triángulos sin necesidad de enviar sus seis vértices fuera del volumen de vista. Tan solo se genera aquella geometría que es estrictamente necesaria, evitando cálculos innecesarios en etapas posteriores. Figura 6. Voxelizaciones obtenidas con el algoritmo descrito en la sección 2.2. Un programa de vértices recibe los puntos instanciados, así como el identificador de instancia gl_InstanceID para cada uno. Empleando este identificador como índice dentro de la textura, se extraen los vértices ABCD correspondientes y los usamos para desplazar el punto a su posición correcta. Tabla 2. Tiempo de voxelización de las implementaciones GPU del algoritmo propuestas en la figura 5. 2.3. Dibujado de polígonos curvos. Figura 5. Diferentes configuraciones para la voxelización de sólidos mediante la GPU. Sin embargo, podemos simplificar aún más el proceso anterior utilizando el procesador de geometría (figura 5.c). En esta implementación, en cada loncha tan solo es necesario enviar a la GPU el tamaño de la textura, el valor ys y un único vértice. Las coordenadas del vértice son intrascendentes. Dicho vértice se envía al hardware, indicando que se instancie una vez por cada tetraedro. Esto provoca una ejecución del procesador de geometría por cada tetraedro. Nótese que tan solo se puede acceder a la variable gl InstanceID desde el procesador de vértices, por lo que necesitamos ejecutar un pequeño programa de vértices que copie ese valor a una de las coordenadas del vértice. El programa de geometría toma como entrada un vértice, y genera como salidas: Dos triángulos, si By>ys>Cy. Un triángulo si Ay >ys> By o Cy> ys> Dy. Nada, en otro caso. Existen multitud de de trabajos centrados en el dibujado de curvas, ya sean curvas implícitas o curvas paramétricas. Sin embargo existen pocas soluciones que resuelvan el problema mediante el uso de hardware gráfico lo que restringe su uso en aplicaciones en tiempo real. A partir del modelo basado en cadenas simpliciales extendidas [10], se ha propuesto un método que permite el dibujado de polígonos definidos por curvas de Bezier de grado 3 en GPU [11]. Este método presenta varias ventajas frente a otras soluciones similares existentes: su implementación es sencilla, no requiere la triangulación previa del polígono y puede ser implementado íntegramente en GPU (no solamente la fase de rasterización). La implementación de este algoritmo requiere un geometry shader y un fragment shader (Figura 7). El primero se encarga de analizar cada curva de Bézier y descomponerla en los casos en que no son válidas para su renderización de manera directa (Figura 8). Además calcula los coeficientes de la ecuación implícita de la curva, que son pasados al fragment shader. El fragment shader evalúa esta ecuación por cada fragmento del interior del polígono de control de la curva de Bézier, descartando los píxeles situados en el exterior de la misma (Algoritmo 4). implementación tradicional basada en CPU de hasta 400X. Figura 7. Procesamiento de cada curva de Bezier mediante geometry y fragment shaders. A continuación se presentan soluciones propuestas en CUDA para dos problemas clásicos en Geometría Computacional [12]. En primer lugar, el problema ya antes citado de inclusión de puntos en sólidos. Y por otra parte la determinación de auto-intersección de triángulos en mallas de triángulos. Figura 9. Fuentes Postscript generados mediante el algoritmo de dibujado de polígonos curvos. Figura 8. Curvas de Bézier válidas (a) y casos inválidos: serpentina (b) y bucle (c). Como muestra la Figura 7, por cada curva de Bézier válida, el procesador de geometría lanza 3 triángulos: dos definen el cuadrilátero de control de la Bézier y el tercero une este cuadrilátero con un punto común para todas las curvas denominado origen. Éste triángulo adicional es necesario para la construcción de un símplice válido a partir de la curva [10]. Los símplices resultantes son rasterizados en el stencil buffer utilizando la operación G_INVERT y el contenido final es transferido al framebuffer, aplicando el color o textura que se desee (Figura 9). La integración completa del proceso de dibujado del polígono curvo en GPU tiene interesantes posibilidades: los puntos de control de las curvas pueden ser generados a partir de una textura, animados o deformados mediante un vertex shader. Esto posibilita la animación en tiempo real de modelos complejos como pelo o ropa. También pueden implementarse métodos clásicos del procesamiento de imágenes basados en curvas deformables como los contornos activos [12]. 3. Resolución de problemas geométricos mediante CUDA. La aparición de CUDA [3] a finales de 2006 ha supuesto un cambio radical en los planteamientos de las investigaciones en GPGPU. En primer lugar, porque CUDA proporciona un conjunto de librerías y extensiones que permiten que las aplicaciones sean programables en C estándar sin necesidad de conocer librerías gráficas o lenguajes de sombreado. Ello facilita al programador una curva de aprendizaje rápida. Además, CUDA es un modelo mucho más flexible que el de la programación basada en shaders, en tanto en cuanto en la mayor parte de las ocasiones no requiere de codificaciones especiales de las estructuras de datos. Y todo ello proporcionando mejoras en los tiempos de ejecución frente a una 3.1. Inclusión de puntos mediante CUDA. La solución comentada en la sección 2.1 y descrita en el Algoritmo 1 tiene como ventaja, además de su eficiencia, el que es susceptible de ser paralelizada de una manera casi directa. Figura 10. Implementación CUDA basada en matrices del test de inclusión. El modelo de programación de CUDA es especialmente útil en problemas cuya solución puede expresarse en forma matricial. En el caso de la inclusión de puntos, el problema puede plantearse construyendo una matriz en la que las filas sean los tetraedros a procesar y las columnas los puntos a procesar. Esta matriz se divide en bloques de threads, donde cada thread determina la inclusión del punto de la columna j con respecto al tetraedro de la fila i, incrementando el resultado de dicha inclusión al contador j (Figura 10). Sin embargo, esta solución requiere que las hebras accedan simultáneamente a la misma posición de la memoria global, lo que solo puede realizarse mediante operaciones atómicas. Ello puede resolverse utilizando si cada hebra almacena el resultado en la posición (i,j) de una matriz de enteros, pero dicha matriz puede tener un tamaño demasiado grande en la mayoría de las ocasiones. En definitiva, el principal inconveniente de este enfoque es que se requieren muchos accesos simultáneos a memoria, que es uno de los factores que más afectan al rendimiento de CUDA. Otra posible solución consiste en que cada hebra determine la inclusión de unos pocos puntos sobre la malla completa. En este caso cada hebra itera sobre la malla entera, copiando un triángulo de memoria global a memoria local, para determinar la inclusión de los puntos, depositando el resultado en un vector que almacena el valor de inclusión por punto (Figura 11). Podría pensarse que este enfoque produciría menos resultados en comparación con la solución anterior, ya que la carga de trabajo de cada hebra es mayor. Pero en la práctica se obtiene una ganancia de hasta 77x (Tabla 3). actualizada por cada hebra cada vez que se añade un par de triángulos en la lista. (Figura 12). Los mejores resultados de la solución propuesta se obtienen como era de esperar en mallas grandes, llegando la mejora hasta 42x (Tabla 4). Figura 12. Implementación CUDA del test de auto-intersección. Figura 11. Implementación CUDA del test de inclusión. Tabla 4. Tiempos obtenidos para el test de auto-intersección con CUDA. 4. Visualización de terrenos en dispositivos móviles. Tabla 3. Ganancia del test de inclusión en CUDA frente a la implementación CPU. A la hora de realizar la implementación deben tenerse en cuenta que el acceso de los threads a la lista de triángulos debe intercalarse para evitar accesos simultáneos. Además, el mayor beneficio se obtiene cuando se aumenta el número de puntos a testear, esto es, cuando se aumenta el número de hebras. 3.2. Test de auto-intersección en CUDA. La determinación de auto-intersecciones en mallas de triángulos es especialmente útil en muchas aplicaciones como prototipado rápido o simulación interactiva de objetos deformables. Existen diferentes soluciones a este problema para CPU, pero las soluciones basadas en GPU son más escasas, destacando las propuestas en [13,14,15]. La mejora obtenida respecto a los métodos basados en CPU llega hasta 17x en el mejor de los casos, si bien presenta algunos problemas respecto al tamaño de las mallas a tratar. Rueda y Ortega [12] han propuesto una solución basada en CUDA, centrada en calcular la intersección de pares de triángulos. Cada hebra determina la intersección de un triángulo ti con los triángulos ti+1, … tn. Los resultados pueden almacenarse en una matriz de booleanos. Sin embargo, esta solución también tiene unos altos requerimientos de memoria. En lugar de ello, generan una lista de índices con los pares de triángulos que intersectan, almacenada en un buffer. La primera posición de este buffer contiene el tamaño de la lista y es Otro de los objetivos del proyecto se centra en la visualización de terrenos en dispositivos móviles en tiempo real. Para ello es necesario, aprovechar al máximo todas las capacidades gráficas de este tipo de dispositivos, considerando las importantes restricciones de memoria de los mismos. Además, y con el fin de alcanzar el mayor número de dispositivos como sea posible, es necesario partir de una arquitectura software que limitará en parte el rendimiento de la aplicación. La capacidad 3D de los dispositivos móviles es programada mediante dos librerías estandarizadas: OpenGL-ES, a la que se accede típicamente desde C o C++, y M3G, para Java Micro Edition. OpenGL-ES [16] es una librería de bajo nivel muy ligera para generar gráficos en 2D y 3D en sistemas embebidos tales como teléfonos, videoconsolas, PDAs, etc. Es libre de royalties y multiplataforma. OpenGL-ES ha sido elegido como el API de gráficos 3D oficial para los teléfonos móviles pasados en Symbian OS, Android e iPhone. Además está disponible para teléfonos móviles basados en Windows Mobile. Consiste en un subconjunto del API de OpenGL para sistemas de escritorio y proporciona una poderosa interfaz estandarizada que permite a los desarrolladores concentrarse más en el contenido y menos en detalles concretos de la plataforma. Por su parte, M3G (JSR-184) [17] es un API gráfica y estandarizada de alto nivel para Java Mobile Edition. Está diseñado de forma que puede implementarse eficientemente sobre OpenGL-ES, de manera que OpenGL-ES proporciona a M3G funcionalidades de bajo nivel tales como transformaciones, iluminación y rasterización, mientras que M3G añade características de alto nivel tales como el grafo de escena o animación. Agradecimientos. Actualmente se está diseñando e implementando una plataforma basada en arquitectura cliente-servidor para la visualización de terrenos en dispositivos móviles. La parte de cliente se encuentra en estado muy avanzado, y se ha diseñado siguiendo los principios de eficiencia y portabilidad (Figura 13). La parte de servidor se encuentra aún en fase de diseño. Este trabajo ha sido financiado por la Junta de Andalucía y la Unión Europea a través de FEDER, bajo el proyecto de investigación P06-TIC-01403. Figura 13. Arquitectura software del visor de terrenos para dispositivos móviles. En cualquier caso, los resultados obtenidos son bastante esperanzadores, obteniendo hasta 50 fps para escenas de unos 125000 triángulos, con efectos de iluminación. Se espera que conforme aumente la disponibilidad de dispositivos móviles dotados de la implementación 2.0 de OpenGL-ES, que incluye ya pipeline programable, este rendimiento pueda ser aún mayor. Figura 14. Visor de terrenos para dispositivos móviles 5. Conclusiones y futuros trabajos En este artículo se ha presentado un resumen de los principales resultados del proyecto “Adaptación de Algoritmos Geométricos al uso de GPU Programable. Aplicaciones”. A la vista de los mismos puede concluirse que muchos de los algoritmos geométricos fundamentales pueden ser resueltos de manera efectiva mediante el uso del hardware gráfico. Además, la aparición de CUDA ha facilitado el desarrollo de nuevas soluciones que aceleran enormemente el rendimiento obtenido. Es de esperar que la integración de nuevas funcionalidades como CUDA o SLI mejoren aún más el rendimiento. El proyecto se encuentra actualmente en la fase central, si bien puede afirmarse que muchos de los objetivos propuestos se han alcanzado ya. El principal esfuerzo debe centrarse en la aplicación de las soluciones obtenidas a problemas más concretos, especialmente en Sistemas de Información Geográfica y Sistemas de Navegación. Referencias. [1] Kilgariff, E., Fernando, R., “The GeForce 6 Series GPU Architecture”, in “GPU Gems 2”, Nvidia, Addisson Wesley, 2005. [2] GPGPU, General-Purpose Computation using Graphics Hardware, http://www.gpgpu.org. [3] CUDA, Compute Unified Device Architecture, http://www.nvidia.com/object/cuda_home.html [4] AMD Stream Computing, http://ati.amd.com/technology/streamcomputing/index.html. [5] C. Ogayar, R.J. Segura, F. R. Feito. “Point in Solid Strategies”. Computers & Graphics, 29 (4), 2005. [6] R. Segura, F.R. Feito, J. Ruiz de Miras, J.C. Torres, and C. Ogayar. “An Efficient Point Classification Algorithm for Triangle Meshes”. Journal of Graphics Tools, 10 (3), 2005. [7] J. J. Jiménez, F. R. Feito, R.J. Segura, C. Ogayar, “Particle Oriented Collision Detection using Simplicial Coverings and Tetra-Tree”. Computer Graphics Forum, 26 (1), 2006. [8] C. Ogáyar, A.J. Rueda, R.J. Segura, F. Feito. “Fast and simple hardware accelerated voxelizations using simplicial coverings”, The Visual Computer, 23 (8), 2007. [9] J.M. Noguera, C. Ogáyar, A.J. Rueda, R.J. Segura. “Voxelización de sólidos mediante instanciación de geometría”, Actas del XVIII Congreso Español de Informática Gráfica, CEIG 2008, Barcelona, 2008. [10] J. Ruiz de Miras , F. Feito. “Inclusion test for curved-edge polygons”. Computers & Graphics, 21, 1997. [11] A.J. Rueda, J. Ruiz de Miras, F. Feito. “GPU-based rendering of curved polygons using simplicial coverings”, Computer & Graphics, 32 (5), 2008. [12] A.J. Rueda, L. Ortega. “Geometric algorithms on CUDA”, III International Conference on Computer Graphics Theory and Applications, GRAPP'08, Madeira, Portugal, 2008. [13] N. K. Govindaraju, I. Kabul, M. C. Lin, D. Manocha, “Fast continuous collision detection among deformable models using graphics processors”, Comput. Graph. 31 (1), 2007. [14] N. K. Govindaraju, M. C. Lin, D. Manocha, “Quick-cullide: fast inter- and intra-object collision culling using graphics hardware, SIGGRAPH ’05: ACM SIGGRAPH 2005 Courses, 2005. [15] Y. Choi, Y. J. Kim, M. Kim, “Rapid pairwise intersection tests using programmable GPUs”, The Visual Computer, 22 (2), 2006. [16] Khronos Group. OpenGL-ES - the standard for embedded accelerated 3d graphics. http://www.khronos.org/, 2004. [17] Java Community Process. Jsr 184: Mobile 3D graphics API for J2me. http://www.jcp.org/en/jsr/detail?id=184, 2005. Apéndice Inicializar los conjuntos de vértices positivos (PosVert) y negativos (NegVert) a vacío acum=0; foreach Ti=( Qi1Qi2Qi3) del recubrimiento { if (P ∈ Ti) return (TRUE); if (P∉ (OQi1Qi2Qi3)) continue; if (P está en el interior de (OQi1Qi2Qi3)) acum+=2*sign([OQi1Qi2Qi3]); else if (P está en una cara de (OQi1Qi2Qi3)) acum+=sign([OQi1Qi2Qi3]); else // P está en la arista OQij if (sign([OQi1Qi2Qi3])>0) { Tetraedro positivo if (Qij ∉ PosVert) { insertar Qij en Posvert acum+=2; } } else { // El tetraedro tiene signo negativo if (Qij ∉NegVert) { insertar Qij en NegVert; counter-=2; } } } return (acum == 2); Algoritmo1. Inclusión de puntos en sólidos. Sort ABC with respect to the y co-ordinate y=Ay Compute mAB, mAC, mBC, mOA, mOB, mOC Ai=A; mB=mAB; mC=mAC if By=y then Bi=B; mB=mOB else mB=mAB; Bi=Ai+(B-A)·mB if Cy=y then Ci=C; mC=mOC else mC=mAC; Ci=Ai+(C-A)·mC While (y>0) Rasterize2D (Ai,Bi,Ci) y--; if (y<Bi) then mB=mOB if (y<Ci) then mC=mOC if (By>y>Cy) then Di=B+(C-B)·mBC Rasterize2D(Bi,Di,Ci) Update Ai,Bi,Ci with the corresponding increment Algoritmo2. Voxelización de tetraedros. 1. Crear la textura, que almacena los vértices ABCD de cada tetraedro, y enviarla a la GPU. 2. Inicializar ys al tamaño del espacio de vóxeles. 3. Crear un array de vértices que almacene únicamente dos triángulos, P0P1P2 y P3P2P1. 4. Limpiar el framebuffer y establecer la operación lógica por píxel a GL_XOR. 5. Establecer como variables uniformes ys y el tamaño de la textura. Enviar el array de vértices a la GPU con DrawArraysInstancedEXT, indicando que se cree una instancia por cada tetraedro. 6. Copiar el resultado del framebuffer a memoria principal de la CPU. 7. Decrementar ys y volver al paso 4 hasta ys = 0 Algoritmo 3. Voxelización de sólidos mediante instaciación de geometría. #define UV gl_TexCoord[0] varying vec4 coef1, coef2; void main() { vec4 ev1= vec4(pow(UV.x,3), pow(UV.x,2), UV.x, pow(UV.y,2)); vec4 ev2=vec4(pow(UV.y,3),ev1[1]*UV.y,UV.x*ev1[3],UV.x*UV.y); if (dot(ev1, coef1) + dot(ev2,coef2) < 0.0f) discard; } Algoritmo 4. Fragment shader para el dibujado de una curva de Bézier (código GLSL).