
Esto es discretizar el plano, pues sólo usaremos esos puntos para dibujar, ya que usar todo el plano sería computacionalmente costosísimo, pues habría que hacer cálculos para todos los puntos del plano y ¿cuántos puntos tiene un plano?, infinitos, ya que nos vamos a mover por los números reales, por eso solo usaremos unos pocos puntos
Ahora colocamos en ese plano, por ejemplo 2 metacircunferencias, y les asignamos un valor a sus coordenadas (posicionar la metacircunferencia) y a su MASA, ya que nos vamos a basar en el campo gravitatatorio para calcular las metacircunferecias. Éstas las vamos a representar con 3 valores en 2D y 4 en 3D, que serán: coordenadas espaciales y masa (en el caso de 2D , una metacircunferencia estará definida por X, Y y MASA).
Ahora debemos realizar el siguiente calculo para cada uno de puntos pertenecientes a la malla del plano: calcularemos un valor de campo en funcion de la distancia desde ese punto al centro de cada metacircunferencia y sumarlos (cada punto de esa matriz de puntos también tendrá sus coordenadas y su MASA o valor de campo). Por ejemplo, si tenemos N metacircunferencias, el calculo para cada punto, basándonos en la fórmula del cálculo del campo gravitatorio que ejerce una masa subre un punto sería:
(F=MASA/DISTANCIA_MASA_AL_PUNTO²)
aux=0.0; para cada punto de la matriz hacer:
aux = metacircunferencia1.MASA/(distancia(punto_actual.posición, metacircunferencia1.posición)²);
aux+= metacircunferencia2.MASA/(distancia(punto_actual.posición metacircunferencia2.posición)²);
.
.
.
aux+= metacircunferenciaN.MASA/(distancia(punto_actual.posición, metacircunferenciaN.posición)²);
FinPara
punto_de_matriz.MASA=aux;
Para aclarar el anterior algoritmo, tanto el punto_actual, como la metacircunferenciaX tienen la misma estructura
struct MC
{
float posición;
float MASA
};
struct posición
{
float X;
float Y;
};
La distancia entre dos puntos (p1 y p2 por ejemplo) se calcula mediante la fórmula: raiz_cuadrada((p1.x-p2.x)²+(p1.y-p2.y)²)
(Una cosa a tener en cuenta es que hay que controlar que la distancia no sea 0 ya que nos daría una excepción de división por 0)
En este punto ya tenemos la matriz totalmente calculada, ahora es cuando nos preguntamos ¿cómo de grande queremos que sea la metacircunferencia? Esto viene dado por dos valores, uno es la masa de la metacircunferencia (a mas masa, mayor será el cociente de antes) y otro es un nuevo valor, al cual llamaré CAMPO_LIMITE, y quiere decir qué valores de la matriz de puntos van a estar dentro de nuestra MC (metacircunferencia, para abreviar) y cuales fuera, de esa manera estableceremos el límite de la MC.
Ya tenemos la matriz calculada y establecido el valor límite, ahora tenemos que pintar la MC ¿cómo lo hacemos? pues para eso vamos a recorrer la matriz de puntos; en vez de hacerlo punto a punto lo haremos en grupos de cuatro (a esto en 3D lo llamaremos Marching Boxes, ya que en 3D será un "cubo" de 8 vertices, y en 2D es una celda de 4 puntos). Bien, recorriendo de esta forma la matriz podemos encontrarnos con los siguientes casos en función de que algunos de los puntos de la celda tengan un valor de MASA mayor que el de CAMPO_LIMITE (los puntos rojos), a lo que llamaré "estar dentro del campo":
Esta es la tabla de todas las combinaciones posibles de que entren vertices o de que dejen de entrar en el campo (los que están dentro del campo son, recordad, los que tuvieran más masa que el CAMPO_LIMITE, y están en color rojo). El caso 1 y el caso 16 se tratan igual (si están todos los vertices o bien fuera, o bien dentro del campo no se pinta nada) en los demas casos hay que ver como serian las líneas que se dibujarían según cada caso. Lo muestro en el siguiente gráfico:

Aunque no os lo creais, todos estos casos forman las MC, ya que los puntos rojos tienden a colocarse uniformemente alrededor de la MC. Aquí os pongo un ejemplo de cómo quedarían dos MC suficientemente acercadas como para que compartan puntos rojos:

Estaría bien que comprobarais con la tabla de casos como se van dibujando las rayas, comenzando a recorrer la matriz por donde querais y sin saltaros ningún punto.
Lo cerca o lejos que están las rayas de los puntos rojos se puede determinar de dos maneras: a) simplemente colocando los extremos de cada línea a medio camino entre el punto rojo y el negro (forma chapuza, pero que cuela si usamos mucha resolución en la matriz de puntos). b) interpolando en función del valor del CAMPO_LIMITE entr el punto rojo y en el punto negro, de esta manera el extremo de la raya estará proporcionalmente mas acercado al punto con mayor MASA.
Como curiosidad: ¿os acordais de las isobaras de los mapas del tiempo?, pues son aquellas lineas (curvas) que unen los puntos con igual presión atmosférica, pues las rayas que hemos dibujado son las lineas que unen los puntos con "igual" (según la resolución de puntos de malla que useis) campo gravitatorio generado por la MC. En física se las llama superfices equiescalares (cuando son superficies, en nuestro caso son lineas equiescalares), ya que unen los puntos con el mismo valor escalar de campo (sí, soy un pesado, pero yo tambiem me aburro de hablar sólo de Metaballs :P)
Con más resolución que la que yo he utilizado como ejemplo, los lados de las MC aumentarían, pareciéndose cada vez más a una circunferencia, y para realizar una animación, sólo habria que mover el centro de las MC y volver a calcularlo todo, haciendo todo eso una vez por frame.
El concepto ya está explicado, ahora vamos a por las verdaderas Metaballs, en 3D.
3. MetaBalls en 3D
Bueno, pues en 3D la cosa es exactamente igual que en 2D, excepto por que en vez de utilizar una celda de 4 puntos, vamos a usar una caja de 8 vértices, y esto complica mucho los cálculos. De mano ya no tenemos sólo 16 casos, sino 256, y además ya no son rayitas lo que tenemos que dibujar, sino uno o varios triángulos de cada vez, ¿vais viendo por donde van lo tiros?.
Para empezar ya no tenemos plano, sino espacio, el cual debemos dividir en una matriz tridimensional de puntos (a ser posible que tenga la misma dimensión de alto, ancho y profundidad). Nuestra unidad mínima de análisis de la malla serán los cubos formados por 8 vertices, de esta manera recorreremos la matriz, pero de cubo en cubo.
Ejemplo de MarchingBox
Pues vuestra malla han de ser muchas de esas marchings apiladas.
Ahora tenemos que calcular toda la matriz tridimensional (la malla) con los valores de campo, igual que en 2D, pero añadiendo en las ecuaciones la coordenada Z, que antes no la metíamos (para hacer este cálculo no hace falta que recorramos mediante marchings, ya que solo nos importa que cada vértice de la malla tenga su valor de MASA).
Una vez hecho todo esto SI deberemos recorrer Marching Box a Marching Box, pues cada marching nos dará que caso tenemos que pintar, en funcion de qué vertices estén dentro del campo y cuales fuera. Para que os hagais una idea, los casos en 3D los trato de la siguiente manera, hay 16 casos básicos, todos los demas derivan de estos, aplicandoles rotaciones, y ademas el caso básico 0 y el caso básico 255 son el mismo ya que uno tiene todos vertices fuera del campo y el otro los tiene todos dentro de él, así que no se pinta nada en ninguno de los dos casos, además hay otro caso que tambien se puede eliminar, ya que se deduce de los demás (para mas información bajaros el programa que realicé para ver los casos básicos, y que contiene un readme.txt con todo lo relacionado sobre el tratamiento de los casos. Podeis bajároslo pinchando aquí, sobre todo si quereis implementar las metaballs por vosotros mismos).

Esta es una captura de pantalla de ese programa.
Aquí no se ven muy bien, pero con el programa podreis rotar las cajas a vuestro gusto.
Creo que no me queda más que decir, todo lo necesario para realizar Metaballs que no está aqui, sino que está en el archivo readme.txt del programa que os muestra los casos básicos. Lo he incluido ahi por que es información bastante técnica, y solo le interesaría a aquiellas personas que vayan a hacer Metaballs y no a las que solo quieran echarle un vistazo general al método que he utilizado aquí.
¡Ah Sí! Una cosa más, para mover las Metaballs, solo teneis que mover su centro. Y ademas cuando hayais acabado de dibujar el último Marching Box de la malla, habreis acabado un frame.
Podeis bajaros mi programa, versión ejecutable o el código completo del proyecto para Visual C++ 6.0, el cual implementa las Metaballs; pero leed el readme.txt que viene con él (sí, otro readme.txt :P).
En este programa he utilizado normales de cara y no de vértice, ya que en este caso es bastante complicado, y costoso computacionalmente hablando, el implementarlo, por eso las metaballs no se verán como bolas, sino como bolas con caras. Si hubiera utilizado iluminación de vértice, habrían quedado perfectamente redondeadas :P.
Por último os voy a poner un algoritmo en pseudocódigo de nivel 1 sobre como hacer las metaballs. Cada llamada a ese algoritmo, generaria un frame de la animación:
calcular_valores_de_campo_de_la_matriz();
para z=1 hasta maxZ hacer
inicio
para y=1 hasta maxY hacer
inicio
para x=1 hasta maxX hacer
inicio
asignar_valores_a_los_vertices_de_la_marching_box(mBox, x, y, z);
caso=calcular_de_que_caso_se_trata_segun_los_vertices_de_la_MB_que_esten_dentro_del_campo(mBox);
caso=caso - 1; // a causa de que el caso 0 ha sido eliminado (readme.txt del programa Casos)
dibujar_caso(mBox,caso);
fin
fin
fin
actualizar_posiciones_de_las_metaballs();
Un saludo de Jacobo Rodriguez Villar (en el IRC Ffelagund)
Agradecimientos a Diego Budavari (en el IRC Iolo) :)
This e-mail address is being protected from spam bots, you need JavaScript enabled to view it