MALLAS POLIGONALES
- ¿Por qué usar mallas poligonales?
- Porque las tarjetas gráficas pintan únicamente triángulos y puntos.
- Son muy manejables, para calcular áreas y volúmenes.
- Con mallas poligonales se pueden representar cualquier sólido en 2D ó 3D.
Las mallas poligonales se miden por el LOD (Level Of Detail), representan sólidos a distintas resoluciones:
- Los polígonos son un conjunto finito de segmentos formados por una curva cerrada simple, sin nudos ni cruces.
- Cualquier polígono puede dividirse en triángulos, que es el polígono más sencillo.
- Para representar volúmenes se usan tetraedros, pero se usan poco.
Componentes: vértices, aristas, caras
Una malla se considera SOLIDA si es CERRADA:
- Sin aristas al aire. Cada arista debe pertenecer a dos caras y con todas las Mallas solidas:
- Con todas las caras: Cada cara comparte sus aristas con otra cara.
Malla Poligonal en en C++
struct Vec3D //Definición estructura del vector en 3D
{
double x,y,z; //Coordenadas de cada punto
double w; //Coordenada proyectiva
};
struct PolyEdge //Definición estructura de la arista
{
int i0,i1; //Coordenada de sus vértices
int ti0,ti1; //Coordenadas de sus vértices de textura
int f0,f1; //Caras a las que pertenece
};
struct PolyFace //Definición estructura de cara
{
int nVertex; //Número de vértices (puede tener >3)
int iv[MAX_FACE_VERTEX]; //Array de índices de vértices
int nEdges; //Número de aristas
int ie[MAX_FACE_VERTEX]; //Array de Índices de aristas
int iNormal; //Índice al array de normales
long int flag; //Flag de uso general, activo on/off
short imat; //Índice al material de la cara
};
struct PolyMesh //Definición estructura de malla
{
int nFaces; //Número de caras = Número de normales
PolyFace* faces; //Array estática de caras
int nEdges; //Número de aristas
PolyEdge* edges; //Array estática de aristas
int nVertex; //Número de vértices
Vec3D* vertex; //Array estática de vértices.
int nTexVertex; //Nºcoord. de textura = Nºvertices
UVWTex* texVertex; //Array estática de coord.Textura
Vec3D* Normals; //Array de normales = Nº de caras
long int flag; //Flag de uso general
}
Para hacer cálculos en 3D o hacer visualizaciones en 2D se usan coordenadas homogéneas:
| 1 0 0 0 | | x | | x' |
| 0 1 0 0 | | y | | y' |
| 0 0 1 0 | | z | = | z' |
| 0 0 0 1 | | w | | w' |
Pasan de 3D a 2D directamente.
Adyacencias: Es necesario conocer:
- Caras a las que pertenece un vertice dado
- Aristas a las que pertenece un vertice dado
- Caras vecinas de una cara dada
Estas características para cada elemento se pre-calcula una vez definida la geometría.
Adyacencia en C++
Para obtener las caras adyacentes a un vértices:
void getVertexFaces( //No devuelve nada
int iv, //Indice del vértice
PolyMesh* mesh, //Array estático a la malla
List<int>& faces //Lista de caras
)
{ //Recorre las caras de la malla
for( int i=0; i <mesh-> nFaces; i++)
{ //Recorre los vertices de cada cara
for( int j=0; j < faces[i]-> nVertex; j++)
{ //Si coincide nuestro vértice con el de la cara
if( iv== faces[i]-> vertex[j])
{ //Lo agrega a la lista de caras adyacentes y va a otra cara
faces.append(i);break;
}
}
}
}
Para obtener las aristas adyacentes a un vértice:
void getFaceFaces( int iface, PolyMesh* mesh, List<int>& faces ) {//Recorre las caras de la malla for( int i=0; i <mesh-> nFaces; i++) { bool bfound=false; //Define comprobador y fija en false //Recorre los vértices de cada cara for( int j=0; j < faces[i]-> nVertex; j++) { for( int k=0; k < mesh->faces[iface]->nVertex; k++) { if(faces[i]-> vertex[j] == mesh->faces[iface]-> vertex[k]) { faces.append(i);bfound=true; break; } } if(bfound) break; } }
Para obtener las caras adyacentes a una cara:
void getFaceFaces( int iface, PolyMesh* mesh, List<int>& faces ) { for( int i=0; i <mesh-> nFaces; i++) { bool bfound=false; for( int j=0; j < faces[i]-> nVertex; j++) { for( int k=0; k < mesh->faces[iface]->nVertex; k++) { if(faces[i]-> vertex[j] == mesh->faces[iface]-> vertex[k]) { faces.append(i);bfound=true; break; } } if(bfound) break; } }
Cálculo con mallas poligonalesEn simulación se define un valor infinitesimal equivalente al 0: epsilón = 10^-5 para
Cálculo del área- Área de la malla = suma de las áreas de todos los triángulos.
- Área de un triangulo con vértices ( a,b,c) es:
- Área = ½* ||CrossProduct(V1, V2)||
- Funciona mejor con triángulos de ángulos abiertos.
- En C++:
//Devuelve un valor double a partir de una malla
double getArea( PolyMesh* mesh)
{
double area=0.0; //Pone a cero el área al iniciar
//Recorre todos las caras de la malla
for( int i=0; i <mesh-> nFaces; i++)
{
PolyFace* face = mesh->faces[i]; //Toma una cara
Vec3D a= mesh-> vertex[face->iv[0]]; //Vertice a
Vec3D b= mesh-> vertex[face->iv[1]]; //Vertice b
Vec3D c= mesh-> vertex[face->iv[2]]; //Vertice c
area+=.5f*lenght(crossProduct(b-a, c-a)); //Area
}
return area; //Devuelve el valor del área
}
Cálculo del Volumen
Problema: los triángulos tienen volumen cero por dentro. Se rellenan de dos formas:
- Metodo NAIF: tetraelizar la malla, y sumar los volúmenes. Coste computacional muy alto.
- Teorema de la DIVERGENCIA: Iguala una integral de volumen a una integral de superficie:
en nuestro caso:
queda resolver la integral en x de la superficie de todas las caras.
Partimos de una cara F definida por tres puntos P0,P1 y P2 y por las aristas Ei=Pi-P0 con i=1,2.
Parametrizando en u y v obtenemos x en función de u y v.
Definimos la normal a la cara Nf en función de las aristas E1 y E2.
Haciendo una transformación de coordenadas obtenemos la función final.
En código C++:
//Devuelve un valor double a partir de una malla
double ComputeVolume( const PolyMesh* malla)
{
const double fOneDiv6 = (double)(1.0/6.0); //Un sexto
double volume=0.0; //Pone volumen a cero
for(int i=0; i < malla->nfaces; i++) //Recorre las caras
{
//Trianguliza las caras con el metodo fan
Vec3D p0 = malla->vertex[malla->faces[i].iv[0]];
for(int j=0; j <malla->faces[j].nVertex – 2; j++)
{
Vec3D p1 = malla->vertex[malla->faces[i].iv[j+1]];
Vec3D p2 = malla->vertex[malla->faces[i].iv[j+2]];
//Obtiene la normal con el producto Cross de las aristas.
Vec3D e1= p1 - p0;
Vec3D e2= p2 - p0;
Vec3D N = Cross( e1,e2);
//Calcula los terminos F y N de la integral
double F1x = p0.x + p1.x+ p2.x;
volume += N.x*F1x;
}
}
volume *= fOneDiv6; //divide por seis
return volume; //devuelve el volumen
}