ロクナナワークショップ NEWS & REPORT

フォローアップ「Try the ActionScript 3D 野中文雄のFlash CS4で学ぶ3次元表現」

この記事を読むのに必要な時間:およそ 13 分

04:Vector3Dクラス

Vector3Dクラスは,3次元空間の座標やベクトルを表し,その処理をするために用いられる※4]⁠ベクトルとは,幾何学的には向きと大きさをもった値をいう。ここでは,ベクトルの内積と透視投影について説明する。

ベクトルの内積

ベクトルの内積を計算すると,ふたつのベクトルのなす角について調べられる。ベクトルAとBの内積はA・Bで表され,つぎの式により計算される。なお,絶対値|A|はベクトルAの大きさ(長さ)であり,θはベクトルAとBとのなす角を示す。

A・B = |A||B|cosθ

Vector3D.dotProduct()を用いると,ふたつのVector3Dインスタンスの内積が求められる※5]⁠内積の値が正負あるいは0かにより,ふたつのベクトルのなす角が鋭角・鈍角または直角か判別できる表3)⁠

dotProduct(演算対象ベクトル:Vector3D):Number

表3 ベクトルの内積となす角

内積の値なす角(θ)cosθ
90度より小さい(鋭角)
090度(直角)0
90度より大きい(鈍角)

内積を求める前掲の式|A||B|cosθで,ベクトルの絶対値は0以上なので,その積 |A||B|もまた0以上だ。したがって,cosθの値が,そのまま内積の値の正負または0かを決めることになる※6]⁠cosθは(180度までの範囲では),角度が90度未満なら正,90度のとき0,そして90度より大きければ負の値をとる図18)⁠

図18 内積の正負とベクトルのなす角度

図18 内積の正負とベクトルのなす角度

前に紹介した回転する立方体のように閉じた立体については,背後の裏返った面は隠れて見えないので,表示する必要がない。そうした面は表示しないことで,描画の負荷を軽くできる。ふたつのベクトルとして視線(通常はz軸の奥に向かうベクトル)と面の方向(面から垂直に正面に向かうベクトル)を考え,その内積を求めれば,視線に対して面が裏向きかどうかわかる。

以下のスクリプト005は,インスタンスのz座標を50ピクセル手前(-50)に移動し,z座標0(xy座標はインスタンスの基準点)を軸に水平回転させる。そのうえで,視線のベクトル(0,0,1)と面の方向(回転の中心から面の基準点まで)のベクトルで内積を計算し,裏返った面はアルファを50%(0.5)にして結果を確かめている図19)⁠

図19 水平回転したインスタンスが裏返るとアルファは50%になる

図19 水平回転したインスタンスが裏返るとアルファは50%になる

スクリプト5 視線と面の方向のベクトルの内積で裏返った面を判定する

// タイムライン: メイン
// 第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軸正方向の視線のベクトル(view)とインスタンスが回転する原点の位置ベクトル(origin)を,Vector3Dインスタンスとして初期化した。

回転するインスタンスは基準点を真ん中に定めたので,その位置ベクトル(myPosition)から原点のベクトル(origin)を差引けば,インスタンスの面の方向がベクトル(vector)として得られる。Vector3Dクラスは,ベクトルの引き算を Vector3D.subtract()メソッドとして備える。

subtract(差引くベクトル:Vector3D):Vector3D

なお,インスタンスのもつMatrix3Dオブジェクトからその基準点の位置ベクトルをVector3Dインスタンスとして得るには,Vector3D.positionプロパティを用いる。

視線のベクトル(view)とインスタンスの面の方向のベクトル(vector)との内積(nDotProduct)を求めれば,視線に対してインスタンスが表向きか裏向きかを確かめられる。内積の値が正のときは,インスタンスの面は裏返っている図18)⁠

Matrix3Dクラスの応用サンプルでも,基本的に同じ考え方によって立方体の裏側の面を非表示にしている。関数arrangeFaces()がその処理を担う。ただし,回転しているのは立方体のSpriteインスタンス(cubeSprite)であって,その中に組立てられた6面のSprite インスタンス自体は動かない。そこで,面のインスタンスの座標を,立方体が置かれたメインタイムラインから見た値に変換しなければならない。

Transform.getRelativeMatrix3D()メソッドは,インスタンスのMatrix3Dオブジェクトを,基準タイムラインの座標空間に変換する。なお,Matrix3D応用サンプルのVectorインスタンスfaces_vectorには,立方体の6面のSpriteインスタンスが納められている。

getRelativeMatrix3D(基準タイムライン:DisplayObject):Matrix3D

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;
    }
  }
}

透視投影

透視投影とは,3次元空間の座標を遠近法が適用された2次元平面に描画することをいう。3次元空間を考えたとき,視点から投影面への焦点距離と投影面からオブジェクトまでのz方向距離が投影像の大きさを決める図20)⁠

図20 透視投影を3次元空間の上から見たyz平面で考える

図20 透視投影を3次元空間の上から見たyz平面で考える

相似の三角形と辺の比の関係から,オリジナルの大きさと投影像の大きさの比は,つぎの式で表される。

投影像の大きさ/オリジナルの大きさ = 焦点距離/(焦点距離+z方向距離)
投影像の大きさ = オリジナルの大きさ×焦点距離/(焦点距離+z方向距離)

Vector3D.project()メソッドは,Vector3D.wプロパティを用いて3次元座標を透視投影する。具体的には,xyz各座標値をVector3D.wプロパティの値で割り算する。

function project():void 

Vector3Dインスタンスは,4次元のベクトルとして表される図21)⁠Vector3D.wプロパティは,xyz座標に続くベクトルの4つ目の成分(要素)である。透視投影の変換比率にかぎらず,他のパラメータを設定して使うことも想定されているようだ。

図21 Vector3Dクラスの列ベクトル表現

図21 Vector3Dクラスの列ベクトル表現

Vector3D.wプロパティに透視投影の変換比率を設定する場合,前述のとおり Vector3D.project()メソッドはこの値でVector3Dインスタンスのxyz座標値を除する。したがって,プロパティにはつぎの式の値を設定すべきことになる。

Vector3D.wプロパティ = (焦点距離 + Vector3D.zプロパティ値)/焦点距離

Vector3Dクラスの応用サンプル(adv_Vector3D_cube.fla)では,立方体の8頂点座標のみをVector3Dクラスで管理し,回転した頂点を2次元座標に透視投影したうえで,2次元の描画メソッドによりワイヤーフレームを描いた図22)⁠関数 xGetProjectedPoints()では,透視投影の変換比率をVector3D.wプロパティに設定して,Vector3d.project()メソッドで透視投影している。そして,この関数は2次元に投影した頂点のxy座標をPointインスタンスにして,それらが納められた配列を戻り値として返す。

図22 Vector3Dインスタンスの頂点座標を透視投影してワイヤーフレームを描画

図22 Vector3Dインスタンスの頂点座標を透視投影してワイヤーフレームを描画

Vector3D応用サンプル Vector3Dインスタンスの3次元座標をVector3d.project()メソッドで透視投影

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.project()メソッドでひとつ注意しておかなければならないのは,参照したVector3Dインスタンスのxyz座標値を書替えてしまうことだ。もとの3次元空間の座標値を保持しておくためには,Vector3Dインスタンスを複製して,その複製にメソッドを適用する必要がある。Vector3D.clone()メソッドは,Vector3Dインスタンスを複製して,その新しいVector3Dインスタンスを返す。

clone():Vector3D

Matrix3Dクラスを用いたVector3Dの座標変換

Matrix3Dクラスとの関わりで,ひとつ補足しておく。 Matrix3D.transformVector()メソッドを用いると,Vector3Dインスタンスの座標をMatrix3Dオブジェクトにより変換できる。メソッドには引数として,変換対象となるVector3Dインスタンスを渡す。

transformVector(変換対象べクトル:Vector3D):Vector3D

Vector3Dクラスの応用サンプルでは,関数xTransform()で Matrix3D.transformVector()メソッドによりVector3Dインスタンスに座標変換を加えた。Vector3DインスタンスはMatrixオブジェクトをもたないので,新たなMatrix3Dインスタンスを生成したうえで,回転の変換を与え,Vector3Dインスタンスに適用している。

Vector3D応用サンプル Vector3DインスタンスにMatrix3D.transformVector()メソッドで回転の変換>

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]);
  }
}
[※4]
併せて,Vector3Dクラス 」を参照してほしい。
[※5]
Vector3D.dotProduct()メソッドおよび内積について詳しくは,Vector3D.dotProduct()メソッド 」をお読みいただきたい。
[※6]
cosなどの三角関数の意義については,AdobeのTechNote角度と座標の計算 - Flash の三角関数を使うが参考になる。
(2009/6/20 野中文雄)

著者プロフィール

ロクナナワークショップ(ロクナナワークショップ)

アドビ認定トレーニングセンター ロクナナワークショップでは,Web業界の第一線で活躍中の講師陣による,実践的な講座を開講しています。全ての講座は最大6名・1日6時間で完結する,PCを操作しながらの集中トレーニングです。

また,各方面で活躍中のクリエイターをお迎えし,最新技術やアイデアをご紹介いただくイベントも開催しています。学生割引もありますので,是非一度参加してみてください。

URL:https://67.org/ws/

コメント

コメントの記入