Polls

Que API tiene más futuro?
 
Inicio arrow Tutoriales arrow Lo + Nuevo arrow Metaballs
Metaballs PDF Print E-mail
Written by Javier Loureiro   
Thursday, 14 December 2006

Metaballs

Jacobo Rodriguez Villar

1.¿Qué son las Metaballs?

En este tutorial pretendo explicar como se consigue el efecto de gotas de agua mezclándose, alargándose y estirándose, según se acercan unas a otras. El efecto es parecido al de una serie de esferas moviendose y deformándose como si fueran gotas de mercurio.

Ante todo, una serie de aclaraciones:  Me reservo el Copyright sobre el código que os ofrezco,  de manera que sólo pretendo que me aviseis si vais a utilizarlo en vuestros proyectos (mas que nada por curiosidad :P), pero podeis hacer con él lo que querais.

El programa que he realizado y que ilustra el efecto Metaball, no es metodológicamente exhaustivo  ni está verificado formalmente, pero no "rompe", y funciona de acuerdo con las especificaciones, así que por favor, no me envíen mails diciendo si esto se podría haber programado mejor o de otra manera, ya que estoy muy seguro de que se puede hacer mucho mejor de como yo lo he hecho, pero si acepto todas las dudas que podais tener.

P.D.: Requerimientos imprescindibles: Conocer el leguaje C y OpenGl o alguna otra API de 3D (por ejemplo Direct3D). Yo usaré OpenGl por que es la única que sé , y el compilador Visual C++ 6.0.

2. Explicacion en 2D

Para ver como se consiguen  las Metaballs en 3D vamos a explicar el proceso en 2D en primer lugar, ya que aclarará mucho la comprensión del efecto.

Imaginemos los ejes de coordenadas en el cuadrante positivo para la X y la Y. Dividimos ese plano en una malla de puntos equidistantes . Nos quedaría algo así:

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

Comentarios
AgregarnuevoBuscar
Escribir comentario
Nombre:
Email:
 
Website:
Título:
Código UBB:
[b] [i] [u] [url] [quote] [code] [img] 
 
Security Image
Por favor introduce el codigo anti-spam que puedes leer en la imagen.

Copyright (C) 2007 Alain Georgette / Copyright (C) 2006 Frantisek Hliva. All rights reserved.



menéameDigg!Del.icio.us!Google!Technorati!Yahoo!
 
< Prev   Next >

Lista de Correo

visita la lista de correo de codepixel. Es una lista abierta, asi que podrás subscribirte y preguntar tus dudas de programación, compartir tus opiniones, aportar ideas, y formar parte de la comunidad codepixelera.