本イベントでは、ロクナナワークショップの人気講座
本稿では野中氏からの解説記事をご紹介します。
- ※サンプルファイル ダウンロード
(Flash CS4形式/約328KB)  
01:3次元座標空間の表現
Flash Player 10では、
たとえば、
コンピュータグラフィックスでこのような表現をする場合、
この機能を用いると、
02:Graphics.drawTriangles()メソッド 
複数の三角形を描いて、
drawTriangles(頂点座標:Vector.<Number>, 頂点番号:Vector.<int> = null, uvtデータ:Vector.
<Number> = null):void 
3つの引数には、
メソッドの3つの引数は、
Vectorクラス
Vectorクラスは、
- VectorクラスがArrayクラスと異なる点
 - エレメントのデータ型をひとつだけ指定。
 - エレメントのインデックスは連番。
 
Vectorクラスのコンスラクタメソッドは、
Vector.<データ型>(長さ:uint = 0, 長さの固定:Boolean = false) 
インスタンスを代入する変数宣言のデータ型にも、
var myVector:Vector.<int> = new Vector.<int>(); 
Vectorインスタンスのエレメントにアクセスするには、
塗りのビットマップの指定
Graphics.
beginBitmapFill(ビットマップ:BitmapData):void
メソッドの第1引数には、
Graphics.
var myTexture:BitmapData = new Pen(0, 0);   // ふたつの0を引数として渡す
mySprite.graphics.beginBitmapFill(myTexture);   // メソッドにBitmapDataインスタンスを渡す
生成したビットマップ
Graphics.drawTriangles()メソッドの引数 
前述のとおり、Graphics.
Graphicsインスタンス.drawTriangles(頂点座標, 頂点番号, uvtデータ) 
ここでGraphics.
Graphics.
Graphics.
ここで、
| 頂点番号 | 第1引数/頂点座標 | 第3引数/uvtデータ | 
|---|---|---|
| 0 | (0, 0) | (0, 0) | 
| 1 | (100, 0) | (1, 0) | 
| 2 | (0, 100) | (0, 1) | 
| 3 | (110, 110) | (1, 1) | 
もっとも、
さて、
// タイムライン: メイン
// 第1フレームアクション
var mySprite:Sprite = new Sprite();
var myGraphics:Graphics = mySprite.graphics;
var myTexture:BitmapData = new Pen(0, 0);
// 頂点座標のVectorインスタンス生成
var vertices:Vector.<Number> = new Vector.<Number>();
// 頂点番号のVectorインスタンス生成
var indices:Vector.<int>= new Vector.<int>();
// uvtデータのVectorインスタンス生成
var uvtData:Vector.<Number> = new Vector.<Number>();
addChild(mySprite);
// 三角形の頂点座標を加える(第1引数)
vertices.push(0, 0);   // 頂点0
vertices.push(100, 0);   // 頂点1
vertices.push(0, 100);   // 頂点2
vertices.push(110, 110);   // 頂点3
// 三角形の頂点番号の組合わせを加える(第2引数)
indices.push(0, 1, 2);   // 左上半分: 頂点0-1-2
indices.push(1, 2, 3);   // 右下半分: 頂点1-2-3
// テクスチャマッピングのuv座標を加える(第3引数)
uvtData.push(0, 0);   // 頂点0
uvtData.push(1, 0);   // 頂点1
uvtData.push(0, 1);   // 頂点2
uvtData.push(1, 1);   // 頂点3
myGraphics.beginBitmapFill(myTexture);
myGraphics.drawTriangles(vertices, indices, uvtData); 
ドラッグで変形した任意の四角形にテクスチャマッピング
Graphics.
準備として、
4隅のドラッグにより変わる値は、Graphics.
function xTransform():void {
  // 三角形の頂点座標(第1引数)
  var vertices:Vector. = new Vector.();
  vertices.push(point0_mc.x, point0_mc.y);
  vertices.push(point1_mc.x, point1_mc.y);
  vertices.push(point2_mc.x, point2_mc.y);
  vertices.push(point3_mc.x, point3_mc.y);
  myGraphics.beginBitmapFill(myTexture);
  myGraphics.drawTriangles(vertices, indices, uvtData);
}
Spriteインスタンスの生成や、Graphics.
// タイムライン: メイン
// 第1フレームアクション
var mySprite:Sprite = new Sprite();
var myGraphics:Graphics = mySprite.graphics;
var myTexture:BitmapData = new Pen(0,0);
var indices:Vector. = new Vector.();
var uvtData:Vector. = new Vector.();
addChildAt(mySprite, 0);
// 三角形の頂点番号の組合わせ(第2引数)
indices.push(0,1,2);
indices.push(1,3,2);
// テクスチャマッピングのuv座標(第3引数)
uvtData.push(0,0);
uvtData.push(1,0);
uvtData.push(0,1);
uvtData.push(1,1);
xTransform();   // ドラッグ前にテクスチャを初期位置にマッピング
function xTransform():void {
  // 三角形の頂点座標(第1引数)
  var vertices:Vector. = new Vector.();
  vertices.push(point0_mc.x, point0_mc.y);
  vertices.push(point1_mc.x, point1_mc.y);
  vertices.push(point2_mc.x, point2_mc.y);
  vertices.push(point3_mc.x, point3_mc.y);
  myGraphics.clear();   // 描画を一旦消す
  myGraphics.beginBitmapFill(myTexture);
  myGraphics.drawTriangles(vertices, indices, uvtData);
}
新たに加えた処理が3つある。第1は、
第2に、
そして第3は、
03:Matrix3Dクラス
Matrix3Dクラスを使うと、
変換のprependとappend
Matrix3Dクラスによる平行移動や拡大・
| 変換 | 変換を前に適用するメソッド | 変換を後に適用するメソッド | 
|---|---|---|
| 平行移動 | Matrix3D. | Matrix3D. | 
| 拡大・ | Matrix3D. | Matrix3D. | 
| 回転 | Matrix3D. | Matrix3D. | 
座標変換を前
Matrix3D.prependRotation()メソッド 
Matrix3D.
prependRotation(度数角:Number, 回転軸:Vector3D):void
マウスポインタの水平座標に応じた速さで、
// タイムライン: メイン
// 第1フレームアクション
var nX:Number = my_mc.x;
var nDeceleration:Number = 0.3;   // 回転スピードの調整係数
my_mc.z = 0;   // 3次元座標のプロパティを操作
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  // 3次元座標空間においてy軸でインスタンスを回転
  my_mc.transform.matrix3D.prependRotation(nRotationY, Vector3D.Y_AXIS);
}
ひとつ注意しなければならないのは、
[ムービープレビュー]を見ると、
Matrix3D.appendRotation()メソッド 
前掲スクリプト003のリスナー関数xRotate()で、
appendRotation(度数角:Number, 回転軸:Vector3D):void
書替えてみると、
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  my_mc.transform.matrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
}
実は、
座標変換が適用される順序
インスタンスのもつMatrix3Dオブジェクトに、
my_mc.transform.matrix3D = new Matrix3D()
インスタンスのDisplayObject.
変換を前
変換を後
前掲スクリプト003のリスナー関数xRotate()で、 Matrix3D.
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  var myMatrix3D:Matrix3D = my_mc.transform.matrix3D;   // もとのMatrix3Dオブジェクトを保持
  my_mc.transform.matrix3D = new Matrix3D();   // デフォルト状態に戻す
  my_mc.transform.matrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);   // 回転を先に適用
  my_mc.transform.matrix3D.append(myMatrix3D);   // もとのMatrix3Dオブジェクトを適用
}
なお、Matrix3D.
append(後から適用する変換:Matrix3D):void
複数の座標変換を組合わせる
前に適用
var nX:Number = my_mc.x;
var nY:Number = my_mc.y;
var nDeceleration:Number = 0.3;
my_mc.z = 0;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  var nRotationX:Number = (mouseY - nY)*nDeceleration;
  my_mc.transform.matrix3D.prependRotation(nRotationY, Vector3D.Y_AXIS);
  my_mc.transform.matrix3D.prependRotation(nRotationX, Vector3D.X_AXIS);
}
[ムービープレビュー]で確かめると、
後に適用
appendTranslation(x:Number, y:Number, z:Number):void
// タイムライン: メイン
// 第1フレームアクション
var nX:Number = my_mc.x;
var nY:Number = my_mc.y;
var nDeceleration:Number = 0.3;
my_mc.z = 0;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  var nRotationX:Number = (mouseY - nY)*nDeceleration;
  // 3次元座標空間でインスタンスを回転
  my_mc.transform.matrix3D.appendTranslation(-nX, -nY, 0);
  my_mc.transform.matrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  my_mc.transform.matrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
  my_mc.transform.matrix3D.appendTranslation(nX, nY, 0);
}
Spriteインスタンスに6面で立方体を作成したMatrix3Dクラスの応用サンプル
var nX:Number = stage.stageWidth / 2;
var nY:Number = stage.stageHeight / 2;
var nSensitivity:Number = 0.2;
var cubeSprite:Sprite = new Sprite();   // 中に別途立方体を作成
function rotateCube(eventObject:Event):void {
  var nRotationX:Number = -(mouseY - nY) * nSensitivity;
  var nRotationY:Number = (mouseX - nX) * nSensitivity;
  rotateSprite(cubeSprite, nRotationX, nRotationY);
}
function rotateSprite(mySprite:Sprite, nRotationX:Number, nRotationY:Number):void {
  var myMatrix3D:Matrix3D = mySprite.transform.matrix3D;
  myMatrix3D.appendTranslation(-nX, -nY, 0);
  myMatrix3D.appendRotation(nRotationX, Vector3D.X_AXIS);
  myMatrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  myMatrix3D.appendTranslation(nX, nY, 0);
}
04:Vector3Dクラス
Vector3Dクラスは、
ベクトルの内積
ベクトルの内積を計算すると、
A・B = |A||B|cosθ
Vector3D.
dotProduct(演算対象ベクトル:Vector3D):Number
| 内積の値 | なす角(θ) | cosθ | 
|---|---|---|
| 正 | 90度より小さい(鋭角) | 正 | 
| 0 | 90度(直角) | 0 | 
| 負 | 90度より大きい(鈍角) | 負 | 
内積を求める前掲の式|A||B|cosθで、
前に紹介した回転する立方体のように閉じた立体については、
以下のスクリプト005は、
// タイムライン: メイン
// 第1フレームアクション
var nX:Number = my_mc.x;
var nY:Number = my_mc.y;
var nDeceleration:Number = 0.3;
var view:Vector3D = new Vector3D(0,0,1);   // 視線のベクトル
var origin:Vector3D = new Vector3D(nX,nY,0);   // 原点のベクトル
my_mc.z = -50;
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = (mouseX - nX)*nDeceleration;
  var nRotationX:Number = (mouseY - nY)*nDeceleration;
  my_mc.transform.matrix3D.appendTranslation(-nX, -nY, 0);
  my_mc.transform.matrix3D.appendRotation(nRotationY, Vector3D.Y_AXIS);
  my_mc.transform.matrix3D.appendTranslation(nX, nY, 0);
  var myPosition:Vector3D = my_mc.transform.matrix3D.position;
  var vector:Vector3D = myPosition.subtract(origin);
  var nDotProduct:Number = view.dotProduct(vector);
  if (nDotProduct > 0) {
    my_mc.alpha = 0.5;
  } else {
    my_mc.alpha = 1;
  }
}
新たなVector3Dインスタンスは、Vector3D()コンストラクタにxyz座標を引数に渡して生成する。z軸正方向の視線のベクトル
回転するインスタンスは基準点を真ん中に定めたので、
subtract(差引くベクトル:Vector3D):Vector3D
なお、
視線のベクトル
Matrix3Dクラスの応用サンプルでも、
Transform.
getRelativeMatrix3D(基準タイムライン:DisplayObject):Matrix3D
function arrangeFaces():void {
  var faceSprite:Sprite;
  var nLastIndex:int = cubeSprite.numChildren - 1;
  var nLength:Number;
  var i:uint;
  var view:Vector3D = new Vector3D(0,0,1);
  nLength = faces_vector.length;
  for (i=0; i < nLength; ++i) {
    faceSprite = faces_vector[i];
    var myPosition:Vector3D = faceSprite.transform.getRelativeMatrix3D(this).position;
    var vector:Vector3D = myPosition.subtract(cubeSprite.transform.matrix3D.position);
    var nDotProduct:Number = view.dotProduct(myPosition);
    if (nDotProduct > 0) {
      faceSprite.visible = false;
    } else {
      faceSprite.visible = true;
    }
  }
}
透視投影
透視投影とは、
相似の三角形と辺の比の関係から、
投影像の大きさ/オリジナルの大きさ = 焦点距離/(焦点距離+z方向距離)
投影像の大きさ = オリジナルの大きさ×焦点距離/(焦点距離+z方向距離)
Vector3D.
function project():void 
Vector3Dインスタンスは、
Vector3D.
Vector3D.wプロパティ = (焦点距離 + Vector3D.zプロパティ値)/焦点距離
Vector3Dクラスの応用サンプル
function xGetProjectedPoints(myVertices:Vector.<Vector3D>):Vector.<Point> {
  var nLength:uint = myVertices.length;
  var projectedPoints:Vector.<Point> = new Vector.<Point>();
  for (var i:int = 0; i<nLength; i++) {
    var myVector:Vector3D = myVertices[i];
    var cloneVector:Vector3D = myVector.clone();
    cloneVector.w = (focalLength+myVector.z)/focalLength;
    cloneVector.project();
    projectedPoints.push(new Point(cloneVector.x, cloneVector.y));
  }
  return projectedPoints;
}
Vector3D.
clone():Vector3D
Matrix3Dクラスを用いたVector3Dの座標変換
Matrix3Dクラスとの関わりで、
transformVector(変換対象べクトル:Vector3D):Vector3D
Vector3Dクラスの応用サンプルでは、
function xTransform(myVertices:Vector.<Vector3D>, speed:Point):void {
  var myMatrix:Matrix3D = new Matrix3D();
  myMatrix.appendRotation(speed.x, Vector3D.Y_AXIS);
  myMatrix.appendRotation(speed.y, Vector3D.X_AXIS);
  var nLength:uint = myVertices.length;
  for (var i:int = 0; i<nLength; i++) {
    myVertices[i] = myMatrix.transformVector(myVertices[i]);
  }
}
