Sie sind auf Seite 1von 26

Taller de Juegos Electronicos:

Carga de Meshes
Javi Agenjo 2010
Universitat Pompeu Fabra
Qu es una malla (Mesh)
Entendemos por mesh a los datos que definen la topologa de un
objeto 3D.

Una mesh se compone de un numero determinado de caras
(normalmente triangulos).

Cada triangulo de la mesh est formado por tres vertices que pueden
tener informacin extra como las coordenadas de textura o la Normal.
Capas de informacin
Los datos obligatorios que debe tener una mesh son:

Vector de vertices: contiene todos los vrtices que forman la mesh. Cada
vrtice se compone de tres nmeros reales.

Luego hay otros datos opcionales que podemos necesitar si usamos texturas,
iluminacin o queremos optimizar recursos:

Vector de normales: la normal en cada vrtice de un tringulo, son tres
nmeros reales (x,y,z). Se usa para iluminar.
Vector de coordenadas de textura: indica a qu parte de la imagen
corresponde cada vrtice, como las imagen son 2D las coordenadas de
textura solo requieren dos nmeros reales (que suelen ir entre 0 y 1).
Vector de colores: indica el color RGB de cada vertice, no se suele utilizar
ya que el color se asigna mejor por textura.
Vector de caras: indica el indice de los tres vertices que forman cada
tringulo de la mesh (como son ndices se pueden guardar usando tres
enteros sin signo).
Buferes indexados
Dado que un mismo vrtice suele estar compartido por varias caras, es
habitual ahorrar memoria indexandolos.

Creamos una tabla con vertices unicos y en el vector de caras indicamos el
indice de cada vertice que forma un triangulo.

Esta manera de trabajar sin embargo tiene el inconveniente de ser menos
eficiente ya que al hacer accesos aleatorios al vector de vertices se producen
'cache-misses' (no puede usar los datos en memoria cache).

Para ello lo normal es guardar los datos 'descompromidos', es decir, sin
indexar, un solo vector de vertices que tiene tantos vertices como numero de
caras por tres.

As los accesos a memoria son consecutivos y se aprovecha de la cache,
aunque las meshes ocupan ms espacio en memoria, pero esto no suele ser
problema.
Indexing
Pintando la mesh en direct mode
Si tenemos los datos de la mesh almacenados en memoria
podemos iterar y pintar uno a uno los triangulos.

En el caso de trabajar en OpenGL lo natural sera hacer:

glBegin( GL_TRIANGLES );
for( int i = 0; i < vertices.size(); i++)
glVertex3fv( (float*)&vertices[i] );
glEnd();

Sin embargo esta manera de trabajar es poco optima ya que
est haciendo llamadas a una funcin de OpenGL para cada
vertice (que se incrementa si hay normales, texcoords, etc).
Es por eso que en las ltimas versiones de OpenGL ha sido
suprimida en favor de los Vertex Arrays
Pintando con Vertex Arrays (1/2)
Los APIs grficos como OpenGL permiten enviar todo el array directamente a
la tarjeta en una llamada:

glEnableClientState(GL_VERTEX_ARRAY); glVertexPoint
er(3, GL_FLOAT, 0, &vertices[0]
); glDrawArrays(GL_TRIANGLES, 0, vertices.size());
glDisableClientState(GL_VERTEX_ARRAY);

La primera linea le indica que le vamos a pasar un vector de vertices.

glVertexPointer le dice que los vertices tienen tres componentes, que son del
tipo float y que de un valor al siguiente no hay bytes entremedio, y seguido se
le pasa el puntero al vector de datos.

La funcion glDrawArrays pintar GL_TRIANGLES empezando desde la
posicin cero del array. Ir lanzando vertices a la tarjeta hasta llegar al tamao
total del vector.
Pintando con Vertex Arrays (2/2)
Notese que a glDrawArrays no le pasamos el numero de caras que
queremos que pinte. Le decimos cuantos vertices queremos que procese. Esto
es porque el numero de vertices necesarios para pintar depende de la primitiva
(3 si son triangulos, 2 si son lines, 1 si son points, etc).

Ademas de los vertices podemos pasarle otros buffers para el resto de la
informacin de cada vertice:

glEnableClientState(GL_NORMAL_ARRAY);
glNormalPointer(GL_FLOAT, 0, &normals[0] );

glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glTexCoordPointer(2,GL_FLOAT, 0, &uvs[0] );

Notese que a glNormalPointer no le indicamos cuantos valores hay por vertice,
ya que las normales siempre tienen tres.

Es muy importante que todos los arrays tengan el mismo tamao, sino la
aplicacin se colgara al tratar de leer fuera de memoria de algun vector.
Pintado con VBOs
Existe una tercera manera de pintar las meshes mucho ms
ptima. Consiste en subir y almacenar la geometra en la
VRAM para evitar tenerla que enviar en cada frame.

Cuando queremos pintar una mesh basta indicar cual de las
meshes queremos usar y la GPU hace el resto. Similar a la
manera de trabajar con texturas.

Para ello se usa algo llamado Vertex Buffer Objects, sin
embargo la sintaxis es un poco ms complicada. Aun as si
piensas hacer uso de tu aplicacin en un entorno profesional
es requisito indispensable usar los VBOs.
Formatos de meshes (1/2)
Una vez sabemos cmo almacenar y cmo pintar la mesh solo nos falta saber cmo
cargarla.

Existen infinidad de formatos para almacenar meshes, y lo habitual es que cada aplicacin
grfica acabe por usar uno propio que se adapte a las necesidades de la propia
aplicacin.

Los formatos pueden ser:
Binarios: los arrays se guardan copiando los bytes de los buffers de la memoria RAM
directamente al fichero.
ASCII: en un archivo de texto guardamos una representacion en cadena de texto de
cada valor, con algun tipo de formato que facilite la lectura.

Los formatos binarios no podemos leerlos comodamente con un editor de texto habitual y
se prestan ms a problemas a la hora de portar a diferentes arquitecturas (big endian, little
endian...), sin embargo las cargas son mucho ms rapidas al igual que el codigo para
realizarlas.

Los formatos ASCII son cmodos de leer, pero ocupan mucho espacio y requieren parsear
el fichero, un proceso que suele ser muy lento computacionalmente.
Formatos de meshes (2/2)
Hay que hacer la distincin entre los formatos que almacenan
meshes y los que almacenan escenas.

Los de meshes solo contienen la informacion de una mesh y
su material, mientras que los formatos de escena contienen la
informacion de muchas meshes ms camaras, luces,
transformaciones e incluso animacin.

Entre los formatos en ASCII ms famosos estan:
ASE: contiene toda la escena y materiales, es un poco
pesado.
OBJ: contiene la informacion geometrica de una malla
unicamente (el material se almacena en un archivo aparte)
DAE: contiene escena, es un formato en XML, muy pesado
pero bastante soportado y estandard.


Formato ASE
El formato ASE (Ascii Scene Export) es un formato de texto que nos
permite exportar no solo las meshes, tambien la escena completa.

Una escena es un conjunto de datos que definen un entorno virtual,
se componen de Meshes, Materiales, Instancias, Camaras, Luces y
propiedades del entorno entre otras muchas cosas.

Al ser un formato ASCII resulta cmodo tanto leerlo como editarlo
como parsearlo, aunque en la prctica sea bastante lento.

No es necesario haberse leido la documentacin relacionada al
formato para entender los datos que hay dentro ya que es
autoexplicativo, aunque es recomendable hacerlo para saber qu
informacin podemos obtener:

http://wiki.beyondunreal.com/Legacy:ASE_File_Format


Formato ASE: Instancias y meshes
Es habitual en una escena tener la misma mesh en diferentes lugares,
es por eso que los formatos de escena contienen por un lado las
meshes y por otro referencias a esas meshes (como si estuvieran
indexadas).

Cada referencia (o instancia) representa una mesh especfica en un
lugar concreto del escenario. Es por eso que si tenemos la misma
mesh N veces en nuestro escenario el archivo de escena no contiene
N veces todos los vrtices de dicha mesh, nicamente cuando son
meshes diferentes.

Es importante por lo tanto diferenciar entre meshes e instancias.

Pero omitamos la informacin que no nos interese y pasemos a la
relacionada con la mesh.
El formato ASE desde dentro
Para poder entender mejor cmo se organiza un ASE lo mejor
es abrir el archivo con un editor de textos y mirar un poco por
encima los datos que hay dentro.

Veremos que es autoexplicativo, ya que cada valor viene
precedido por un nombre que nos sirve para saber a qu hace
referencia el valor.

Adems utiliza una jerarqua tabulada para facilitar la lectura, y
'scopes' ( { ... } ) que nos ayudan a entender la estructura.

Los valores numericos se separan por espacios y las cadenas
de texto vienen delimitadas por comillas.
Formato ASE: vertices y caras (1/2)
Ejemplo del inicio de la informacin de una Mesh


*MESH {

*TIMEVALUE 0
*MESH_NUMVERTEX 8
*MESH_NUMFACES 12
*MESH_VERTEX_LIST {
*MESH_VERTEX 0 -50.0000 -50.0000 -50.0000
*MESH_VERTEX 1 50.0000 -50.0000 -50.0000
*MESH_VERTEX 2 -50.0000 50.0000 -50.0000
*MESH_VERTEX 3 50.0000 50.0000 -50.0000
...
}

*MESH_FACE_LIST {
*MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1
*MESH_FACE 1: A: 3 B: 1 C: 0 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2 *MESH_MTLID 1
*MESH_FACE 2: A: 4 B: 5 C: 7 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 3 *MESH_MTLID 0
*MESH_FACE 3: A: 7 B: 6 C: 4 AB: 1 BC: 1 CA: 0
...
}
Formato ASE: vertices y caras (2/2)
En las especificaciones de la mesh podemos ver informacin
relacionada con el nmero de caras y el nmero de vrtices.

Seguido va una lista de vrtices nicos:

*MESH_VERTEX 0 -50.0000 -50.0000 -50.0000

el primer nmero indica el identificativo, los otros tres son la
coordenada X,Y,Z del vertice.

Luego viene la tabla de caras, nos centraremos en los
primeros valores:

*MESH_FACE 0: A: 0 B: 2 C: 3

Nos indica que la cara 0 usa como vertices los vertices 0, 2 y 3
Formato ASE: coordenadas de textura (1/2)
*MESH_NUMTVERTEX 12
*MESH_TVERTLIST {

*MESH_TVERT 0 0.0000 0.0000 0.0000
*MESH_TVERT 1 1.0000 0.0000 0.0000
*MESH_TVERT 2 0.0000 1.0000 0.0000
*MESH_TVERT 3 1.0000 1.0000 0.0000
*MESH_TVERT 4 0.0000 0.0000 0.0000
...

*MESH_NUMTVFACES 12
*MESH_TFACELIST {
*MESH_TFACE 0 9 11 10
*MESH_TFACE 1 10 8 9
*MESH_TFACE 2 8 9 11
*MESH_TFACE 3 11 10 8
*MESH_TFACE 4 4 5 7
*MESH_TFACE 5 7 6 4
...
Formato ASE: coordenadas de textura
(2/2)
Al igual que pasaba con los vrtices, las coordenadas de
textura vienen indexadas:

*MESH_TVERT 0 0.0000 0.0000 0.0000

Las coordenadas de textura pueden tener 3 componentes
aunque ignoramos normalmente el tercer valor (ya que
nuestras imgenes son 2D). Para cada cara nos indica las
coordenadas de textura:

*MESH_TFACE 0 9 11 10
Formato ASE: Normales
Las normales no vienen indexadas:

*MESH_FACENORMAL 0 0.0000 0.0000 -1.0000
*MESH_VERTEXNORMAL 0 0.0000 0.0000 -1.0000
*MESH_VERTEXNORMAL 2 0.0000 0.0000 -1.0000
*MESH_VERTEXNORMAL 3 0.0000 0.0000 -1.0000

Primero tenemos la normal de la cara y luego la normal para
cada vrtice.

Es importante recordar que las normales para cada vertice de
una cara no tienen por qu ser iguales ni tienen qu ser
perpendiculares a la cara, ya que estas se suelen modificar
para dar aspecto curvo a las superficies planas mediante
sombreado Phong, a esto se le llama Smoothing.
Formato ASE: Materiales
En el formato ASE no solo se nos da informacion sobre la topologia
de las meshes, tambien sobre los materiales asociados a cada mesh.

Son muchas las propiedades que nos pueden interesar saber de un
material, aunque eso depender de los algoritmos de render que
utilicemos.

Si solo queremos saber qu textura emplear buscaremos:

*BITMAP "clouds.tga"

Aun as es importante recordar que hay muchos parametros que en
combinacin con shaders pueden dar resultados ms realistas (por
ejemplo parametros como el shininess, el alpha, el selfillumination o
los colores del material).


Formato ASE: Multitextura
Por Multitextura nos referimos a emplear ms de una textura
para pintar los poligonos de la superficie, es decir, a cada
poligono le afecta ms de una textura a la hora de realizar el
render.

Para ello tenemos que tener un shader que permita renderizar
empleando ms de una textura.

Usamos diferentes texturas por poligono para definir diferentes
propiedades de cada pixel de la superficie, lo normal es usar
una unica textura para definir el color, pero podemos usar otras
texturas para definir qu zonas brillan (emissive), cuanto refleja
(specularmap), etc.

El Formato ASE permite definir materiales con varias texturas.
Formato ASE: Multitextura
En este ejemplo tenemos dos superficies, una con una unica textura
(colormap) y una con Colormap y Specularmap, definida en el shader
para modular la componente de luz especular (cuanto refleja la luz
directa).
Formato ASE: Multimaterial
Por otra parte, Multimaterial se refiere al uso de diferentes materiales para
diferentes poligonos de una misma mesh. De esta manera podemos hacer un
uso ms ptimo de las texturas asignando diferentes materiales a diferentes
zonas de la mesh y aprovechandonos del tiling (repeticion de una textura a lo
largo de una superficie). No confundir con multitextura.

El formato ASE guarda una lista de materiales al principio del archivo, estos
materiales son compartidos por todos los objetos de nuestra escena, es decir,
cada mesh tiene asignado un nico material.

Como suele ser necesario tener diferentes materiales por cara de una misma
mesh, ASE subdivide los materiales en submateriales.

Para saber cuantos submateriales tiene miramos en el ASE:
*NUMSUBMTLS 2

Y podemos parsear cada uno:
*SUBMATERIAL 0 ...

Formato ASE: Multimaterial
El formato ASE almacena para cada mesh el id de su material,
y para cada cara el id del submaterial que usa:

*MESH_FACE 0: A: 0 B: 2 C: 3 AB: 1 BC: 1 CA: 0 *MESH_SMOOTHING 2
*MESH_MTLID 1

Si parseamos la informacin de los materiales podemos saber
qu textura emplear para cada cara.

Normalmente las caras suelen venir ordenadas por submaterial
(primero las del submaterial 0, luego el 1, ...) aunque no se
puede garantizar que siempre sea as.

Es importante comprobar si los ASEs que cargamos cumplen
esta regla para evitar posibles errores.
Formato ASE: Multimaterial
Hay que tener en cuenta que a la hora de hacer render llamamos a la
funcion glDrawArray pasandole por parametro desde qu elemento
queremos empezar a procesar los array, y cuantos elementos hay que
procesar.

Si aseguramos que las caras vienen agrupadas por material ID, solo
necesitamos saber en qu vertice se produce el cambio entre un
material y el siguiente.

A la hora de hacer el render llamamos a la funcin indicandole el
offset y el nmero de vertices ajustado a los vertices que
corresponden a ese submaterial:

glDrawArrays(GL_TRIANGLES, offset, num_vertices);

Donde offset es el indice del primer vertice con ese submaterial y
num_vertices es el numero de vertices a lanzar.
Consejos finales
Es bueno aprovechar que el formato ASE te indica de antemano
cuantos elementos hay en cada lista, para poder controlar que no has
cometido ningun error mientras se parsea.

Recordad pintar siempre el mayor numero de caras posibles con el
menor numero de llamadas a OpenGL, as se acelera el render
considerablemente.

Cuidado con equivocarse con los tamaos de los vectores, si
hacemos leer a OpenGL ms elementos de los que tiene un vector se
producir un error en ejecucin dificil de debugar.

Tener presente que algunos programas exportan los coordenadas de
los vertices con los ejes cambiados (Y y Z estan intercambiados)
debido a cmo funciona cada uno de ellos.

Das könnte Ihnen auch gefallen