ActionScript 3.0で始めるオブジェクト指向スクリプティング

第48回 テクスチャに遠近法を適用する - uvt座標の指定

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

Matrix3Dオブジェクトに透視投影の変換を加える

Utils3D.projectVectors()メソッドの第1引数として渡したMatrix3Dオブジェクト(worldMatrix3D)には,Matrix3D.prependRotation()メソッドで回転は加えた。しかし,透視投影は行っていなかった。実際,スクリプト1をよく見直してみると,焦点距離の数値(nFocalLength)がどこにも使われていない。

それでは,Matrix3Dオブジェクトにどうやって透視投影を加えればよいか。今回はPerspectiveProjection.toMatrix3D()メソッドを使うことにしよう。タイムラインに置いたDisplayObjectインスタンスには,そのタイムラインのPerspectiveProjectionオブジェクトにより遠近法が適用された(第33回「遠近法の投影」PerspectiveProjectionクラスで遠近法を操作する参照⁠⁠。PerspectiveProjection.toMatrix3D()メソッドは,PerspectiveProjectionオブジェクトがもつ遠近法の設定をMatrix3Dオブジェクトにして返してくれる。

ただし,PerspectiveProjection.toMatrix3D()メソッドから得たMatrix3Dオブジェクトの遠近法の変換は,いわばカメラのレンズを決めただけだ。3次元空間座標を2次元平面に透視投影するには,カメラと座標空間との距離を定めなければならない。これは焦点距離を基本にするとよい。

具体的には,前掲スクリプト1に透視投影のMatrix3Dオブジェクト(viewMatrix3D)をつぎのように加える。タイムラインのPerspectiveProjectionオブジェクト(myPerspective)からMatrix3DオブジェクトをPerspectiveProjection.toMatrix3D()メソッドにより得たうえで,奥行きを焦点距離の分平行移動する。この移動の変換は,Matrix3D.prependTranslation()メソッドにより透視投影の前に加える。カメラからの距離を先に決めなければ,透視投影できないからだ。

// var nFocalLength:Number = transform.perspectiveProjection.focalLength;
var myPerspective:PerspectiveProjection = transform.perspectiveProjection;
// ...[中略]...
// 透視投影のMatrix3Dオブジェクトを得る
var viewMatrix3D:Matrix3D = myPerspective.toMatrix3D();
// 焦点距離分奥に平行移動
viewMatrix3D.prependTranslation(0, 0, myPerspective.focalLength); 

そして,Utils3D.projectVectors()メソッドの第1引数に渡すMatrix3Dオブジェクト(worldMatrix3D)には,この透視投影のMatrix3Dオブジェクト(viewMatrix3D)Matrix3D.append()メソッドで乗じる。ただし,もととなる3次元空間座標の変換結果は,そのままMatrix3Dオブジェクトとして残しておかなければならない。つまり,このオブジェクトを直接2次元平面に透視投影してはだめだ。そのため,Matrix3D.clone()メソッドで複製したオブジェクト(myMatrix3D)に変換を加える。

function xTransform(vertices2D:Vector.<Number>, myRotation:Number):void {
  worldMatrix3D.prependRotation(myRotation, Vector3D.Y_AXIS);
  var myMatrix3D:Matrix3D = worldMatrix3D.clone();
  myMatrix3D.append(viewMatrix3D);   // 透視投影の変換
  // Utils3D.projectVectors(worldMatrix3D, vertices, vertices2D, uvtData);
  Utils3D.projectVectors(myMatrix3D, vertices, vertices2D, uvtData);
} 

2回にわたって取組んだお題がようやく整った。これでマウスポインタの水平座標に応じて水平に回した正方形の3次元空間の頂点座標を,2次元平面に透視投影したうえでテクスチャがマッピングされる図2⁠。でき上がったフレームアクション全体を,スクリプト2に掲げる。

スクリプト2 3次元空間の回転する正方形を2次元平面に透視投影してテクスチャマッピング(完成)

// フレームアクション
var nUnit:Number = 100 / 2;
var mySprite:Sprite = new Sprite();
var myTexture:BitmapData = new Image();
var vertices:Vector.<Number> = new Vector.<Number>();
var indices:Vector.<int> = new Vector.<int>();
var uvtData:Vector.<Number> = new Vector.<Number>();
var nDeceleration:Number = 0.3;
var myGraphics:Graphics = mySprite.graphics;
var myPerspective:PerspectiveProjection = transform.perspectiveProjection;
var worldMatrix3D:Matrix3D = new Matrix3D();
var viewMatrix3D:Matrix3D = myPerspective.toMatrix3D();
viewMatrix3D.prependTranslation(0, 0, myPerspective.focalLength);
mySprite.x = stage.stageWidth / 2;
mySprite.y = stage.stageHeight / 2;
vertices.push(-nUnit, -nUnit, 0);
vertices.push(nUnit, -nUnit, 0);
vertices.push(nUnit, nUnit, 0);
vertices.push(-nUnit, nUnit, 0);
indices.push(0, 1, 3);
indices.push(1, 2, 3);
uvtData.push(0, 0, 0);
uvtData.push(1, 0, 0);
uvtData.push(1, 1, 0);
uvtData.push(0, 1, 0);
addChild(mySprite);
addEventListener(Event.ENTER_FRAME, xRotate);
function xRotate(eventObject:Event):void {
  var nRotationY:Number = mySprite.mouseX * nDeceleration;
  var vertices2D:Vector.<Number> = new Vector.<Number>();
  xTransform(vertices2D, nRotationY);
  xDraw(vertices2D);
}
function xTransform(vertices2D:Vector.<Number>, myRotation:Number):void {
  worldMatrix3D.prependRotation(myRotation, Vector3D.Y_AXIS);
  var myMatrix3D:Matrix3D = worldMatrix3D.clone();
  myMatrix3D.append(viewMatrix3D);
  Utils3D.projectVectors(myMatrix3D, vertices, vertices2D, uvtData);
}
function xDraw(vertices2D:Vector.<Number>):void {
  myGraphics.clear();
  myGraphics.beginBitmapFill(myTexture);
  myGraphics.drawTriangles(vertices2D, indices, uvtData);
  myGraphics.endFill();
}

図2 回転のMatrix3Dオブジェクトに透視(遠近法)投影の変換を加える

図2 回転のMatrix3Dオブジェクトに透視(遠近法)投影の変換を加える

画像 画像

画像 画像

今回書いたスクリプトで,3次元空間の頂点座標はひとつの平面上でなければいけない,というつくりにはしてない。そこで次回は,立方体の頂点座標をテクスチャでくるんでみよう。

今回解説した次のサンプルファイルがダウンロードできます。

著者プロフィール

野中文雄(のなかふみお)

ソフトウェアトレーナー,テクニカルライター,オーサリングエンジニア。上智大学法学部卒,慶応義塾大学大学院経営管理研究科修士課程修了(MBA)。独立系パソコン販売会社で,総務・人事,企画,外資系企業担当営業などに携わる。その後,マルチメディアコンテンツ制作会社に転職。ソフトウェアトレーニング,コンテンツ制作などの業務を担当する。2001年11月に独立。Web制作者に向けた情報発信プロジェクトF-siteにも参加する。株式会社ロクナナ取締役(非常勤)。

URLhttp://www.FumioNonaka.com/

著書