En ActionScript, las transformaciones de mapa de bits se realizan utilizando el método
Graphics.drawTriangles()
, ya que los modelos 3D se representan mediante una colección de triángulos en el espacio. (No obstante, Flash Player y AIR no admiten un búfer de profundidad, por lo que los objetos de visualización aún son esencialmente planos o bidimensionales. Esto se describe en
Información sobre los objetos de visualización 3D en el motor de ejecución de Flash Player y AIR
.) El método
Graphics.drawTriangles()
es similar a
Graphics.drawPath()
, ya que adopta un conjunto de coordenadas para dibujar un trazado de triángulo.
Para familiarizarse con el uso de
Graphics.drawPath()
, consulte
Trazados de dibujo
.
El método
Graphics.drawTriangles()
utiliza Vector.<Number> para especificar las ubicaciones de punto para el trazado del triángulo:
drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void
El primer parámetro de
drawTriangles()
es el único necesario: el parámetro
vertices
. Este parámetro es un vector de números que define las coordenadas mediante las que se dibujan los triángulos. Cada tres conjuntos de coordenadas (seis números) se representa un trazado del triángulo. Sin el parámetro
indices
, la longitud del vector siempre debe ser un factor de seis, ya que cada triángulo requiere tres pares de coordenadas(tres conjuntos de dos valores x/y). Por ejemplo:
graphics.beginFill(0xFF8000);
graphics.drawTriangles(
Vector.<Number>([
10,10, 100,10, 10,100,
110,10, 110,100, 20,100]));
Ninguno de estos triángulos comparte puntos, pero si lo hicieran, el segundo parámetro
drawTriangles()
,
indices
, podría utilizarse para volver a usar los valores en el vector
vertices
para varios triángulos.
Al utilizar el parámetro
indices
, se debe tener en cuenta que los valores
indices
son índices de punto, y no índices que se relacionan directamente con los elementos del conjunto
vertices
. Es decir, un índice del vector
vertices
tal y como se define mediante
indices
es realmente el verdadero índice dividido por 2. Para el tercer punto de un vector
vertices
utilice, por ejemplo, un valor
indices
de 2, aunque el primer valor numérico de ese punto comience en el índice vectorial de 4.
Por ejemplo, combine dos triángulos para que compartan el borde diagonal utilizando el parámetro
indices
:
graphics.beginFill(0xFF8000);
graphics.drawTriangles(
Vector.<Number>([10,10, 100,10, 10,100, 100,100]),
Vector.<int>([0,1,2, 1,3,2]));
Se debe tener en cuenta que aunque ahora se haya dibujado un cuadrado utilizando dos triángulos, solo se especificaron cuatro puntos en el vector
vertices
. Con el uso de
indices
, los dos puntos compartidos por los dos triángulos se volverán a utilizar para cada triángulo. Con ello se reduce el recuento total de vértices de 6 (12 números) a 4 (8 números):
Cuadrado dibujado con dos triángulos utilizando el parámetro de vértices
Esta técnica resulta útil con mallas de triángulo más grandes donde varios triángulos comparten la mayoría de los puntos.
Todos los rellenos se pueden aplicar a triángulos. Los rellenos se aplican a la malla de triángulo resultante tal y como se haría en cualquier otra forma.
Transformación de mapas de bits
Las transformaciones de mapa de bits proporcionan la ilusión de perspectiva o "textura" en un objeto tridimensional. Concretamente, es posible distorsionar un mapa de bits hacia un punto de fuga de forma que la imagen parezca encogerse conforme se acerca al punto de fuga. O bien, puede utilizar un mapa de bits bidimensional para crear una superficie para un objeto tridimensional, ofreciendo la ilusión de textura o “envoltura” en dicho objeto.
Superficie bidimensional donde se utiliza un punto de fuga y tres objetos tridimensionales ajustados con un mapa de bits
Mapeado UV
Una vez que comience a trabajar con texturas, deseará utilizar el parámetro uvtData de
drawTriangles()
. Este parámetro permite configurar el mapeado UV para rellenos de mapa de bits.
El mapeado UV es un método para aplicar textura a los objetos. Se basa en dos valores: un valor U horizontal (x) y un valor V vertical (y). En lugar de basarse en valores de píxel, se basan en porcentajes. 0 U y 0 V representan la parte superior izquierda de una imagen y 1 U y 1 V la parte inferior derecha:
Ubicaciones UV 0 y 1 en una imagen de mapa de bits
A los vectores de un triángulo se les puede proporcionar coordenadas UV para asociarse con las ubicaciones respectivas en una imagen:
Coordenadas UV de un área triangular de una imagen de mapa de bits
Los valores UV coinciden con los puntos del triángulo:
Los vértices del triángulo se mueven y el mapa de bits se distorsiona para mantener igual los valores UV para un punto individual
Debido a que las transformaciones 3D de ActionScript se aplican al triángulo asociado con el mapa de bits, la imagen del mapa de bits se aplica al triángulo en función de los valores UV. Por lo tanto, en lugar de utilizar cálculos de matriz, establezca o ajuste los valores UV para crear un efecto tridimensional.
El método
Graphics.drawTriangles()
también acepta una parte de información opcional para transformaciones tridimensionales: el valor T. El valor T en uvtData representa la perspectiva 3D, o más concretamente, el factor de escala del vértice asociado. El mapeado UVT añade una corrección de la perspectiva al mapeado UV. Por ejemplo, si un objeto se coloca en un espacio 3D alejado del punto de vista de forma que parezca ser un 50% de su tamaño “original”, el valor T del objeto podría ser 0,5. Debido a que los triángulos se dibujan para representar objetos en el espacio 3D, sus ubicaciones en el eje z determinan sus valores T. La ecuación que determina el valor T es la siguiente:
T = focalLength/(focalLength + z);
En esta ecuación, focalLength representa una distancia focal o ubicación de "pantalla" calculada que determina la cantidad de perspectiva que se ofrece en la vista.
Distancia focal y valor z
-
A.
-
Punto de vista
-
B.
-
pantalla
-
C.
-
Objeto 3D
-
D.
-
valor focalLength
-
E.
-
valor z
El valor de T se utiliza para realizar un ajuste de escala de formas básicas para hacerlas parecer más alejadas en la distancia. Suele ser el valor utilizado para convertir puntos 3D en puntos 2D. En el caso de datos UVT, también se utiliza para escalar un mapa de bits entre los puntos en un triángulo con perspectiva.
Cuando se definen valores UVT, el valor T sigue directamente los valores UV definidos para un vértice. Con la inclusión de T, cada tres valores en el parámetro
uvtData
(U, V y T) coinciden con cada dos valores del parámetro
vertices
(x e y). Solo con los valores UV, uvtData.length == vertices.length. Con la inclusión de un valor T, uvtData.length = 1.5*vertices.length.
En el siguiente ejemplo se muestra un plano giratorio en un espacio 3D utilizando datos UVT. Este ejemplo utiliza una imagen llamada ocean.jpg y una clase “auxiliar”, ImageLoader, para cargar la imagen ocean.jpg de modo que se pueda asignar al objeto BitmapData.
A continuación se muestra el código fuente de la clase ImageLoader (guarde este código en un archivo denominado ImageLoader.as):
package {
import flash.display.*
import flash.events.*;
import flash.net.URLRequest;
public class ImageLoader extends Sprite {
public var url:String;
public var bitmap:Bitmap;
public function ImageLoader(loc:String = null) {
if (loc != null){
url = loc;
loadImage();
}
}
public function loadImage():void{
if (url != null){
var loader:Loader = new Loader();
loader.contentLoaderInfo.addEventListener(Event.COMPLETE, onComplete);
loader.contentLoaderInfo.addEventListener(IOErrorEvent.IO_ERROR, onIoError);
var req:URLRequest = new URLRequest(url);
loader.load(req);
}
}
private function onComplete(event:Event):void {
var loader:Loader = Loader(event.target.loader);
var info:LoaderInfo = LoaderInfo(loader.contentLoaderInfo);
this.bitmap = info.content as Bitmap;
this.dispatchEvent(new Event(Event.COMPLETE));
}
private function onIoError(event:IOErrorEvent):void {
trace("onIoError: " + event);
}
}
}
Y a continuación se muestra el código ActionScript que utiliza triángulos, mapeado UV y valores T para hacer que la imagen parezca que está rotando y encogiéndose hacia un punto de fuga. Guarde este código en un archivo denominado Spinning3dOcean.as:
package {
import flash.display.*
import flash.events.*;
import flash.utils.getTimer;
public class Spinning3dOcean extends Sprite {
// plane vertex coordinates (and t values)
var x1:Number = -100, y1:Number = -100, z1:Number = 0, t1:Number = 0;
var x2:Number = 100, y2:Number = -100, z2:Number = 0, t2:Number = 0;
var x3:Number = 100, y3:Number = 100, z3:Number = 0, t3:Number = 0;
var x4:Number = -100, y4:Number = 100, z4:Number = 0, t4:Number = 0;
var focalLength:Number = 200;
// 2 triangles for 1 plane, indices will always be the same
var indices:Vector.<int>;
var container:Sprite;
var bitmapData:BitmapData; // texture
var imageLoader:ImageLoader;
public function Spinning3dOcean():void {
indices = new Vector.<int>();
indices.push(0,1,3, 1,2,3);
container = new Sprite(); // container to draw triangles in
container.x = 200;
container.y = 200;
addChild(container);
imageLoader = new ImageLoader("ocean.jpg");
imageLoader.addEventListener(Event.COMPLETE, onImageLoaded);
}
function onImageLoaded(event:Event):void {
bitmapData = imageLoader.bitmap.bitmapData;
// animate every frame
addEventListener(Event.ENTER_FRAME, rotatePlane);
}
function rotatePlane(event:Event):void {
// rotate vertices over time
var ticker = getTimer()/400;
z2 = z3 = -(z1 = z4 = 100*Math.sin(ticker));
x2 = x3 = -(x1 = x4 = 100*Math.cos(ticker));
// calculate t values
t1 = focalLength/(focalLength + z1);
t2 = focalLength/(focalLength + z2);
t3 = focalLength/(focalLength + z3);
t4 = focalLength/(focalLength + z4);
// determine triangle vertices based on t values
var vertices:Vector.<Number> = new Vector.<Number>();
vertices.push(x1*t1,y1*t1, x2*t2,y2*t2, x3*t3,y3*t3, x4*t4,y4*t4);
// set T values allowing perspective to change
// as each vertex moves around in z space
var uvtData:Vector.<Number> = new Vector.<Number>();
uvtData.push(0,0,t1, 1,0,t2, 1,1,t3, 0,1,t4);
// draw
container.graphics.clear();
container.graphics.beginBitmapFill(bitmapData);
container.graphics.drawTriangles(vertices, indices, uvtData);
}
}
}
Para probar este ejemplo, guarde los dos archivos de clase en el mismo directorio que una imagen llamada “ocean.jpg”. Puede ver cómo se transforma el mapa de bits original para que parezca que se aleja en la distancia y gira en un espacio 3D.
Técnica "culling"
La técnica "culling" es el proceso que determina qué superficies de un objeto tridimensional no debe representar el procesador porque están ocultas desde el punto de vista actual. En el espacio 3D, la superficie de la parte “posterior” de un objeto tridimensional queda oculta desde el punto de vista:
La parte posterior de un objeto 3D se oculta desde el punto de vista.
-
A.
-
Punto de vista
-
B.
-
Objeto 3D
-
C.
-
Parte posterior de un objeto tridimensional
Los triángulos siempre se representan independientemente de su tamaño, forma o posición. La técnica "culling" garantiza que Flash Player o AIR representen los objetos 3D correctamente. Asimismo, para guardar los ciclos de representación, a veces se desea que el procesador omita algunos triángulos. Pongamos como ejemplo un cubo giratorio en el espacio. En un momento dado, nunca verá más de tres caras del cubo, ya que las caras ocultas estarán frente a la otra dirección del otro lado del cubo. Debido a que estas caras no se van a ver, el procesador no debe dibujarlas. Sin "culling", Flash o AIR representan tanto las caras frontales como las posteriores.
Un cubo tiene caras no visibles desde el punto de vista actual
Por lo tanto, el método
Graphics.drawTriangles()
tiene un cuarto parámetro para establecer un valor de "culling":
public function drawTriangles(vertices:Vector.<Number>, indices:Vector.<int> = null, uvtData:Vector.<Number> = null, culling:String = "none"):void
El parámetro de "culling" es un valor de la clase de enumeración
TriangleCulling
:
TriangleCulling.NONE
,
TriangleCulling.POSITIVE
y
TriangleCulling.NEGATIVE
. Estos valores son dependientes de la dirección del trazado del triángulo que define la superficie del objeto. La API de ActionScript para determinar el valor "culling" da por hecho que todos los triángulos exteriores de una forma 3D se dibujan con la misma dirección de trazado. Cuando una cara del triángulo cambia de dirección, la dirección del trazado también se modifica. En este momento, al triángulo se le puede aplicar el proceso de "culling" (eliminarse de la representación).
Por lo tanto, un valor
TriangleCulling
de
POSITIVE
elimina triángulos con una dirección de trazado positiva (en el sentido de las agujas del reloj). Un valor
TriangleCulling
de
NEGATIVE
elimina triángulos con una dirección de trazado negativa (en sentido contrario a las agujas del reloj). En el caso de un cubo, mientras que las caras frontales tienen una dirección de trazado positiva, las posteriores cuentan con una dirección de trazado negativa:
Cubo “sin ajustar” para mostrar la dirección de trazado. Cuando se “ajusta”, se invierte la dirección de trazado de la cara posterior
Para ver cómo funciona la técnica "culling", comience con el ejemplo anterior de
Mapeado UV
, establezca el parámetro de "culling" del método
drawTriangles()
en
TriangleCulling.NEGATIVE
:
container.graphics.drawTriangles(vertices, indices, uvtData, TriangleCulling.NEGATIVE);
Observe que la parte “posterior” de la imagen no se procesa conforme gira el objeto.
|
|
|