Objetos, Líneas y Puntos 3D.

Tutorial de VRML97

Node IndexedFaceSet

Hasta el momento, sólo hemos visto las primitivas como objetos 3D para modelar nuestro entorno. Esto es evidentemente muy restrictivo en cuanto a formas y por esta razón VRML define unas estructuras para poder definir geometrías arbitrarias.

Estas estructuras se basan en una variante de lo que en gráficos 3D se conoce como modelo de fronteras y que permite definir la geometría de un objeto 3D a partir de definir sus vértices y sus caras.

Por ejemplo, un cubo unitario se puede definir a partir de 8 vértices y 6 caras, de la forma siguiente:

Lista de vértices:
0
 0.5  0.5  0.5
1
 0.5  0.5 -0.5
2
-0.5  0.5 -0.5
3
-0.5  0.5  0.5
4
 0.5 -0.5  0.5
5
 0.5 -0.5 -0.5
6
-0.5 -0.5 -0.5
7
-0.5 -0.5  0.5
      
Lista de caras:
0
 0 1 2 3
1
 0 4 5 1
2
 1 5 6 2
3
 2 6 7 3
4
 3 7 4 0
5
 4 7 6 5

La lista de caras del cubo muestra, para cada cara, los vértices que la forman indicando el índice del cada vértice según la lista de vértices. El orden en que se dan los vértices es muy importante ya que si no se respetan unas ciertas normas, los polígonos de las caras no se dibujarán correctamente. Las normas para listar los vértices de cada cara son:

En VRML esta estructura se puede definir mediante el node IndexedFaceSet con la ayuda del node Coordinate. Así pues, el cubo unitario lo definimos de la siguiente forma:

Ejemplo1: Definición por caras y vértices de un cubo unitario.


Shape { 
	appearance Appearance { material Material { diffuseColor 1 0 0 } }
	geometry
		IndexedFaceSet {
			coord Coordinate {
				point [
					 0.5  0.5  0.5,
					 0.5  0.5 -0.5,
					-0.5  0.5 -0.5,
					-0.5  0.5  0.5,
					 0.5 -0.5  0.5,
					 0.5 -0.5 -0.5,
					-0.5 -0.5 -0.5,
					-0.5 -0.5  0.5
				]
			}
			coordIndex [
					0, 1, 2, 3, -1,
					0, 4, 5, 1, -1,
					1, 5, 6, 2, -1,
					2, 6, 7, 3, -1,
					3, 7, 4, 0, -1,
					4, 7, 6, 5, -1
			]
		}
}

Analizando este código, en primer lugar encontramos que para ver un objeto definido con un node IndexedFaceSet es necesario ponerlo dentro de un node Shape, concretamente se sitúa en el field geometry.

Observando el funcionamiento del node IndexedFaceSet, en primer lugar se define la lista de vértices de forma similar a la lista de vértices de la tabla anterior. Para definir esta lista se utiliza el node Coordinate que tiene el field point donde se listan los puntos 3D que forman los vértices del objeto.

Una vez listados los vértices, ya sólo queda definir los polígonos que forman las caras. Esto se hace mediante el field coordIndex, donde se listan las caras como una sucesión de índices de vértices. Para identificar cada una de las caras, la lista de vértices de una cara se separa de la siguiente mediante un -1.

(Nota: El número mínimo de vértices por cara es lógicamente tres).

Evidentemente, no tiene mucho sentido definir un cubo cuando ya se dispone de una primitiva que lo hace directamente, pero con este sistema podemos modelar cualquier objeto facetado, es decir, formado por caras planas.

El color también se puede dar al objeto polígono a polígono mediante el field color y el field colorPerVertex. A continuación se muestra un ejemplo:

Ejemplo2: Definición por caras y vértices de un cubo unitario con cada cara de un color diferente (roja, verde, azul, magenta, amarilla y cian).


Shape { 
	geometry
		IndexedFaceSet {
			coord Coordinate {
				point [
					 0.5  0.5  0.5,
					 0.5  0.5 -0.5,
					-0.5  0.5 -0.5,
					-0.5  0.5  0.5,
					 0.5 -0.5  0.5,
					 0.5 -0.5 -0.5,
					-0.5 -0.5 -0.5,
					-0.5 -0.5  0.5
				]
			}
			coordIndex [
					0, 1, 2, 3, -1,
					0, 4, 5, 1, -1,
					1, 5, 6, 2, -1,
					2, 6, 7, 3, -1,
					3, 7, 4, 0, -1,
					4, 7, 6, 5, -1
			]
			colorPerVertex FALSE
			color Color {
				color [
					1 0 0,
					0 1 0,
					0 0 1,
					1 0 1,
					1 1 0,
					0 1 1
				]
			}
		}
}

Veamos como funciona esto. El field colorPerVertex dice si los colores se asignan a los vértices o a las caras del objeto. Lo ponemos a FALSE para que la asignación sea por cara.

A continuación el field color contiene la lista de colores (RGB) que se asignarán a las caras a través del node Color. Se deben listar los colores en el orden correspondiente en que están listadas las caras en coordIndex, ya que el VRML va aparejando cara con color de forma consecutiva.

Nótese que esta possibilidad de asignar colores distintos a cada cara no es posible cuando se define el cubo mediante la primitiva Box.

Pero si queremos que la mitad de las caras sean de un color y la otra mitad de otro, no es necesario repetir el color para cada cara. Se puede utilizar el field colorIndex de la siguiente manera:

Ejemplo3: Definición por caras y vértices de un cubo unitario con 3 caras azules y 3 amarillas.


Shape { 
	geometry
		IndexedFaceSet {
			coord Coordinate {
				point [
					 0.5  0.5  0.5,
					 0.5  0.5 -0.5,
					-0.5  0.5 -0.5,
					-0.5  0.5  0.5,
					 0.5 -0.5  0.5,
					 0.5 -0.5 -0.5,
					-0.5 -0.5 -0.5,
					-0.5 -0.5  0.5
				]
			}
			coordIndex [
					0, 1, 2, 3, -1,
					0, 4, 5, 1, -1,
					1, 5, 6, 2, -1,
					2, 6, 7, 3, -1,
					3, 7, 4, 0, -1,
					4, 7, 6, 5, -1
			]
			colorPerVertex FALSE
			color Color {
				color [
					0 0 1,
					1 1 0
				]
			}
			colorIndex [
				0, 0, 0, 1, 1, 1
			]
		}
}

Este código especifica qué color le corresponde a cada cara a partir de un número de índice de color de la lista de colores. Por lo tanto, el VRML recorre las caras y va mirando en la lista de índices de colores del field colorIndex qué color le pertenece a cada una.

Finalmente veremos como asignar colores a los vértices de las caras. Esto puede ser interesante para crear degradados de colores o sombreados. Veamos un ejemplo:

Ejemplo4: Definición por caras y vértices de un cubo unitario con colores asignados a los vértices.


Shape { 
	geometry
		IndexedFaceSet {
			coord Coordinate {
				point [
					 0.5  0.5  0.5,
					 0.5  0.5 -0.5,
					-0.5  0.5 -0.5,
					-0.5  0.5  0.5,
					 0.5 -0.5  0.5,
					 0.5 -0.5 -0.5,
					-0.5 -0.5 -0.5,
					-0.5 -0.5  0.5
				]
			}
			coordIndex [
					0, 1, 2, 3, -1,
					0, 4, 5, 1, -1,
					1, 5, 6, 2, -1,
					2, 6, 7, 3, -1,
					3, 7, 4, 0, -1,
					4, 7, 6, 5, -1
			]
			colorPerVertex TRUE # No haría falta ponerlo porque es el valor por defecto.
			color Color {
				color [
					1 0 0,
					0 1 0,
					0 0 1,
					1 0 1,
					1 1 0,
					0 1 1,
					1 0.5 0,
					0 1 0.5
				]
			}
			colorIndex [
					0, 1, 2, 3, -1,
					0, 4, 5, 1, -1,
					1, 5, 6, 2, -1,
					2, 6, 7, 3, -1,
					3, 7, 4, 0, -1,
					4, 7, 6, 5, -1
			]
		}
}

Veamos lo que hemos definido. En primer lugar hemos puesto el field colorPerVertex a TRUE (cierto) para que la asignación de colores sea por vértice y no por cara. El incluir esta opción no es imprescindible ya que esta es la opción por defecto pero lo incluimos por claridad.

En segundo lugar, ahora el field colorIndex funciona de forma diferente. La lista que contiene, es la lista de índices de colores para cada vértice de cada cara y no para cada cara en global. Es decir, cada grupo de índices acabado en -1 forma el conjunto de colores que se le asignan a los vértices de cada cara.

Node IndexedLineSet

De forma similar a como hemos definido objetos sólidos, también podemos definir objetos llamados de "alambre", es decir, objetos formados simplemente por aristas (sin caras planas). Esto se puede obtener mediante el node IndexedLineSet.Veamos como se definiría nuestro cubo unitario con este sistema:

Lista de vértices:
0
 0.5  0.5  0.5
1
 0.5  0.5 -0.5
2
-0.5  0.5 -0.5
3
-0.5  0.5  0.5
4
 0.5 -0.5  0.5
5
 0.5 -0.5 -0.5
6
-0.5 -0.5 -0.5
7
-0.5 -0.5  0.5
      
Lista de aristas:
0
 0 1 2 3 0
1
 4 5 6 7 4
2
 0 4
3
 1 5
4
 2 6
5
 3 7

 

Debemos tener en cuenta que ahora trabajamos con aristas (lines) y que pueden ser múltiples (polylines). Tal y como se ve en las tablas, la lista de aristas tiene dos polylines y cuatro lines. Las dos primeras producen la parte superior e inferior del cubo respectivamente. Las lines corresponden a las aristas que unen la parte superior y la inferior. Como ahora ya no hablamos de caras, debemos especificar que la primera polyline ha de ser cerrada (cosa que no és obligatoria en general) y por esta razón debemos especificar el segmento 3 0 (y 5 8 en la segunda).

Veamos como se haría en VRML:

Ejemplo5: Definición de un cubo unitario de alambre.


Shape { 
	appearance Appearance { material Material { emissiveColor 0 0.8 1 } }
	geometry
		IndexedLineSet {
			coord Coordinate {
				point [
					 0.5  0.5  0.5,
					 0.5  0.5 -0.5,
					-0.5  0.5 -0.5,
					-0.5  0.5  0.5,
					 0.5 -0.5  0.5,
					 0.5 -0.5 -0.5,
					-0.5 -0.5 -0.5,
					-0.5 -0.5  0.5
				]
			}
			coordIndex [
					0, 1, 2, 3, 0, -1,
					4, 5, 6, 7, 4, -1,
					0, 4, -1,
					1, 5, -1,
					2, 6, -1,
					3, 7, -1
			]
		}
}

También se pueden asignar colores distintos a los distintos tramos de polyline mediante el field color , field colorIndex y field colorPerVertex de forma análoga a como lo hicimos anteriormente. Se deja al lector que haga pruebas con estos fields como ejercicio.

NOTA: Al dibujar objetos de alambre, es necesario utilizar el field emissiveColor en lugar de diffuseColor en la apariencia del objeto.

A continuación damos otro ejemplo donde se utilizan las polylines para escribir las letras "VRML".

Ejemplo6: Escribir "VRML" mediante polylines.


Shape {
	geometry
		IndexedLineSet {
			coord Coordinate {
				point [
					-22 4 0,
					-18 -6 0,
					-14 4 0,
					-10 -6 0,
					-10 4 0,
					-4 4 0,
					-2 2 0,
					-2 0 0,
					-4 -2 0,
					-10 -2 0,
					-2 -6 0,
					2 -6 0,
					2 4 0,
					6 0 0,
					10 4 0,
					10 -6 0,
					14 4 0,
					14 -6 0,
					22 -6 0
				]
			}
			coordIndex [
					0, 1, 2, -1,				# V
					3, 4, 5, 6, 7, 8, 9, -1, 8, 10, -1,	# R
					11, 12, 13, 14, 15, -1,			# M
					16, 17, 18, -1				# L
			]
			colorPerVertex FALSE
			color Color {
				color [
					1 0 0, 0 1 0, 0 0 1, 1 1 0
				]
			}
			colorIndex [
				0, 1, 1, 2, 3
			]
		}
}


Node PointSet

De forma similar a como hemos definido objetos sólidos y objetos de "alambre", también se pueden obtener puntos aislados en el espacio, pero que forman un solo objeto, con el node PointSet.Veamos como se definen los puntos que forman los vértices de nuestro cubo unitario con este nuevo sistema:

Ejemplo7: Definición de puntos en el espacio correspondientes a los vértices de un cubo unitario.


Shape { 
	appearance Appearance { material Material { emissiveColor 1 1 0 } }
	geometry
		PointSet {
			coord Coordinate {
				point [
					 0.5,  0.5,  0.5,
					 0.5,  0.5, -0.5,
					-0.5,  0.5, -0.5,
					-0.5,  0.5,  0.5,
					 0.5, -0.5,  0.5,
					 0.5, -0.5, -0.5,
					-0.5, -0.5, -0.5,
					-0.5, -0.5,  0.5
				]
			}
		}
}

NOTA: Aquí también es necesario definir el material con el field emissiveColor en lugar de field diffuseColor.

Ejercicios propuestos:

Letra "U"
Definir un objeto 3D sólido que forme la letra "U".
Comentarios
  • Utilitzar el node IndexedFaceSet.
  • Mantener la forma lo más sencilla posible (no intentar hacer curvas).
Solución propuesta: u.wrl.




< Anterior | Menú ^ | Siguiente >