Download FUNCIONAMIENTO DE LOS ÁRBOLES BSP (Binary Space

Document related concepts

Partición binaria del espacio wikipedia , lookup

Árbol kd wikipedia , lookup

Algoritmo Fractional Cascading wikipedia , lookup

Transcript
FUNCIONAMIENTO
DE LOS ÁRBOLES BSP
(Binary Space
Partitioning)
Junio 1999
Pedro Pablo Gómez Martín
Marco Antonio Gómez Martín
Enrique Alonso Martín
Explicación árboles BSP
Árboles BSP
Los árboles BSP (Binary Space Partitioning) son unas estructuras recursivas que organizan los
polígonos del modelo de forma jerárquica respecto a su posición en el espacio. Hay muchas variedades de
árboles BSP, y algunas estructuras más complicadas que se basan en ellos. Se utilizan unas u otras en
función de la aplicación concreta que se les quiera dar. Aquí detallaremos la modalidad que hemos
utilizado nosotros.
CONSTRUCCIÓN DE UN ÁRBOL BSP
La construcción de un árbol BSP es un proceso sencillo. Inicialmente se parte del grupo de
polígonos que se quieren almacenar en el árbol. Para ello se dividen en tres grupos en función de la
relación de cada polígono con un plano. Así, tendremos un grupo de polígonos que quedará delante del
plano (en el lado en el que apunta su vector normal), otro grupo con los que queden detrás, y un último
grupo con los polígonos inscritos en el plano. En la raíz del árbol se almacena el plano (su ecuación), y
los polígonos inscritos en él. Recursivamente se obtienen cada uno de los dos hijos con los polígonos de
los otros grupos. La recursión termina cuando no quedan polígonos a ninguno de los dos lados.
Es habitual pensar, en un principio, que según esto, la recursión no terminará nunca, pues dado
un plano y los polígonos del modelo, siempre quedan polígonos a alguno de los lados, salvo que el
modelo sea excesivamente peculiar. Es importante darse cuenta que eso no influye para que la recursión
realmente termine, pues en cada paso recursivo vamos siempre quitando polígonos, por lo que en un nodo
intermedio del árbol no trataremos nunca con todos los polígonos del modelo, si no con los que hayan ido
quedando en el lado correspondiente de los planos almacenados en los nodos superiores.
Se ha dejado pasar por alto la posibilidad de que existan polígonos que corten al plano
seleccionado, con lo que el polígono no podrá ser incluido en ninguno de los tres grupos. En ese caso, el
polígono tendrá que ser "cortado" en dos mitades, de modo que cada una de ellas quede a un lado del
plano.
Nuestros polígonos siempre deben ser triángulos, por lo que dividir un polígono origina tres
nuevos polígonos, quedando dos en un lado, y uno en el otro. Puede darse el caso, no obstante, de que
solo se creen dos nuevos polígonos. Esto ocurre cuando uno de los vértices pertenece al plano.
La división del polígono no es demasiado complicada, aplicando conocimientos de geometría.
No obstante, a la hora de la implementación, hay que tener cuidado con lo que nosotros hemos
denominado "polígonos corruptos". Un polígono corrupto es aquel que posee un área nula, es decir que al
menos dos de sus vértices coinciden, o los tres vértices pertenecen a la misma recta. Éstos polígonos
aparecen por culpa de la discretización de los números reales cuando un polígono queda dividido en otros,
uno de ellos muy pequeño. Los polígonos corruptos estropean el correcto funcionamiento de las consultas
de colisiones sobre el árbol BSP, por lo que deben ser eliminados.
Explicación árboles BSP
La elección del plano en cada nodo del árbol no es una tarea trivial. Algunas modalidades de
árboles BSP eligen planos "al azar". Otra opción es coger planos a los que pertenece al menos un
polígono, que es la que utilizamos nosotros. Por lo tanto, se escoge cualquier polígono, se halla el plano
que lo contiene y se utilizar como plano separador para ese nodo del árbol.
Eligiendo siempre este tipo de planos, y obligándonos a no detener la recursión mientras queden
polígonos en alguno de los lados, nos aseguramos de que todos los polígonos del modelo inicial quedan
en algún nodo, inscritos a algún plano. Otras modalidades de árboles BSP no aseguran esto, pues detienen
la recursión antes para ahorrar espacio.
Elegir un plano u otro va a hacer variar la profundidad del árbol y el número de divisiones de los
polígonos. Interesan árboles poco profundos, para acelerar las búsquedas sobre ellos, y además que hagan
pocas divisiones de los polígonos para ahorrar espacio. Habitualmente una característica empeora cuando
la otra mejora. Nuestras aplicaciones no son críticas en espacio, pues los modelos no es excesivamente
grandes, y sí nos interesan búsquedas rápidas, para acelerar la búsqueda de colisiones. Por lo tanto,
necesitamos árboles de la menor profundidad posible.
Conseguir el árbol de menor profundidad es un problema NP-Completo, por lo que nos
conformamos con construir árboles que no sean demasiado malos. Durante la construcción del árbol, por
lo tanto, en vez de elegir un único polígono cada vez elegimos varios, y construímos el árbol con cada
uno de ellos, quedándonos con el que menor profundidad tenga. Esta técnica aumenta el tiempo de
construcción de los árboles BSP. Para evitar un tiempo excesivo de inicialización del programa pueden
construirse los árboles una única vez con una herramienta auxiliar, y luego almacenarlos en ficheros, de
donde los recogerá la aplicación, es decir “precalcular” el árbol.
BÚSQUEDA DE COLISIONES EN UN ÁRBOL BSP
Los árboles BSP pueden utilizarse para calcular colisiones. Cuando se quiere avanzar por un
mundo virtual de un punto a otro, hay que saber si hay alguna pared que nos lo impida. Para ello, se
utilizan los árboles BSP, que nos detectan si entre el segmento que une dos puntos hay algún polígono.
Ahora que sabemos la estructura de los árboles BSP nos plantearemos cómo averiguarlo.
Todos los polígonos del árbol BSP se encuentran en los nodos del árbol, inscritos en alguno de
sus planos, por lo que para que el segmento (también denominado "rayo") se cruce con algún polígono,
deberá cruzarse con algún plano.
La búsqueda de colisiones de un rayo dentro de un árbol BSP es un procedimiento recursivo, en
el que se van comparando las posiciones de los extremos del rayo respecto a los planos de los nodos. Así,
si los dos extremos están delante o detrás del plano, no habrá corte con él, por lo que se realiza la
búsqueda de colisiones de forma recursiva únicamente con el lado del plano en el que estén los extremos.
El caso interesante es cuando cada extremo está en un lado. Hay varios posibles finales:
• El rayo corta con algún polígono que está en el lado del origen.
• El rayo corta con algún polígono que está inscrito dentro del plano.
• El rayo corta con algún polígono que está en el lado del destino.
Como hay que indicar el primer polígono con el que se colisiona, habrá que empezar primero
mirando, recursivamente, si el rayo colisiona con algún polígono del lado del origen. Si es así, se
devuelve ese polígono sin hacer ninguna otra consulta.
Si no hay colisión, hay que mirar si el rayo atraviesa algún polígono inscrito en el plano. Para
ello, se halla el punto de corte entre el rayo y el plano, y se comprueba si el punto está dentro de alguno
de los polígonos inscritos en el plano. Esta consulta debe ser rápida para acelerar todo el proceso, por lo
que el algoritmo que lo realiza está especialmente optimizado, aunque no nos ocuparemos de él aquí. Sí
es interesante señalar, no obstante, que esa optimización hace que falle con los polígonos corruptos antes
citados, lo que hace especialmente importante su eliminación. Con los polígonos normales, sin embargo,
el algoritmo funciona a la perfección.
Pues bien, si el punto está dentro de alguno de los polígonos, se devuelve dicho polígono como
colisionado y se finaliza la recursión. Si no es así, se consulta las colisiones con el lado del plano donde
está el destino y se devuelve su resultado.
Se entenderá ahora la necesidad de que el árbol tenga la menor profundidad posible. Un árbol
muy profundo ocasionaría muchas llamadas recursivas en los casos en los que no se produjera ninguna
colisión, que es el caso más habitual, ralentizando toda la ejecución.
Explicación árboles BSP
BÚSQUEDA DE COLISIONES CON ESFERAS EN UN ÁRBOL BSP
La búsqueda de colisiones entre un rayo y un árbol BSP explicada es rápida y útil en muchos
casos. Sin embargo, hay veces en las que son ineficaces, si se quiere evitar el acercamiento excesivo a las
paredes por parte del usuario.
Todo lo explicado sobre árboles BSP lo aprendimos de documentos e información obtenida en
Internet, pero no encontramos nada que nos ayudara con nuestro nuevo problema, por lo que tuvimos que
desarrollar completamente la detección de colisiones con esferas. La idea es semejante a la de un rayo,
pero se complica, y ralentiza, en la detección de los cortes.
Ahora no tenemos un punto origen y otro destino que comparar con el plano de cada nodo, sino
una esfera, de la que queremos saber si colisiona con algún polígono. Lo que hacemos es hallar la
posición (y distancia) del centro de la esfera al plano. Si está a una distancia superior al radio de la esfera,
ésta no se cortará con el plano, por lo que no habrá colisión con ninguno de sus polígonos. Continuaremos
la recursión, por lo tanto, únicamente en el lado del plano en el que se encuentre el centro de la esfera.
Si el centro está más cerca, miramos no obstante si hay alguna colisión con algún polígono del
lado donde está el centro de la esfera. Si lo hay, devolvemos ese polígono. Si no, hay que hallar el círculo
de corte entre el plano y la esfera, y mirar si éste corta con alguno de los polígonos inscritos en el plano.
Para hallar dicho círculo, primero hay que calcular su centro. Éste será el punto de corte entre el
plano y un vector perpendicular a éste que pase por el centro de la esfera. Y el radio del círculo se puede
hallar fácilmente por el teorema de pitágoras, a partir del radio de la esfera y de la distancia del centro de
ésta al plano.
radio
esfera
Radio del círculo
distancia
Plano
La figura muestra la situación "de perfil". El plano es perpendicular a la hoja, y se corta con la
esfera representada. El radio del círculo de corte se obtiene usando el triángulo formado por el radio de la
esfera, la distancia del centro al plano, y el propio radio del círculo.
Una vez que hallamos las características del círculo, tenemos que averiguar si éste corta a algún
polígono. Si ocurre así, se devuelve el polígono. Si no, se realiza recursión sobre el lado del plano en el
que no está el centro de la esfera.
Averiguar si un polígono corta con un círculo no es tan sencillo, y también tuvimos que idearlo
nosotros. Hay varias posibles formas de que exista corte:
1
2
3
4
Explicación árboles BSP
En el primer caso, el centro del círculo está dentro del polígono, en el segundo, el polígono tiene
una arista que corta a la circunferencia por dos puntos, en el tercero el polígono tiene una arista (bueno,
son dos, pero es indiferente) que corta a la circunferencia en un único punto, y por último en el cuarto, el
polígono está completamente dentro del círculo. No se considera como colisión que el polígono tenga a
una arista tangente a la circunferencia.
Nuestro algoritmo de detección de colisiones va buscando alguno de esos casos, de modo que si
alguno de ellos se cumple, avisa de que se está produciendo la colisión. Se podría pensar que la
separación entre los casos segundo y tercero no es necesaria si detectamos el corte de una arista con la
circunferencia. Sin embargo, hallar la existencia o no de corte entre un segmento y una circunferencia no
es una tarea especialmente rápida, por lo que hacemos la división para utilizar otras técnicas más eficaces
para saber si se está dando cada uno de los casos.
Saber si se da la primera posible situación es sencillo, una ve z que tenemos el algoritmo para
averiguar si un punto está dentro de un polígono utilizado en la detección de colisión entre un rayo y un
árbol BSP. Bastará mirar si el centro del círculo está en el interior del polígono para comprobar si estamos
o no en este caso.
El segundo es más complicado. Hallar los cortes entre un segmento y una circunferencia es
relativamente complicado matemáticamente. Se nos ocurrió un método alternativo, al darnos cuenta de
que, como la circunferencia es cortada dos veces, la arista será cortada por alguno de sus diametros, en
particular por el diámetro perpendicular a la propia arista. Por lo tanto hallamos el segmento
perpendicular a la arista (que esté dentro del plano, pues todo esto se hace en tres dimensiones), que sea
además diámetro del círculo. Si hay corte entre los dos segmentos, el polígono cortará al circulo. Esta
técnica siempre detecta las colisiones del que hemos llamado tercer caso, de ahí que lo separáramos desde
el principio.
Los otros dos casos ya son más sencillos, y además pueden comprobarse simultáneamente si
buscamos algun vértice del polígono que esté dentro del circulo. Para averiguarlo, se halla la distancia
entre el centro del círculo y el vértice que se está estudiando, y si ésta es menor que el radio del círculo, el
vértice estará dentro.