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

第43回 Vector3Dオブジェクトの座標に遠近法を適用する

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

前回のあらすじは,3次元空間座標のVector3Dインスタンスで正方形の4頂点をつくり,y軸で水平に回してみたのだった。ところが,パースペクティブがかかっていないため,ワイヤーフレームは水平に伸び縮みする四角形にしか見えなかった第42回図7再掲)⁠2次元平面の座標に引き写すとき,どのように遠近法の投影(透視投影)を加えればよいか。これが今回のお題だ。

第42回図7 回転するワイヤーフレームの四角形にパースペクティブがかかっていない(再掲)

第42回図7 回転するワイヤーフレームの四角形にパースペクティブがかかっていない(1)

第42回図7 回転するワイヤーフレームの四角形にパースペクティブがかかっていない(2)

第42回図7 回転するワイヤーフレームの四角形にパースペクティブがかかっていない(3)

遠近法と焦点距離

第33回遠近法の投影「⁠プロパティ]インスペクタを使った3次元空間の操作」では,⁠視野角」画角」⁠をご紹介した。⁠プロパティ]インスペクタの[3D位置とビュー]セクションでは[遠近の角度]というプロパティとして示され,1から179までの数値で定める。値が大きいほど,遠近の差は広がって見えた第33回図3再掲)⁠

第33回図3 ⁠プロパティ]インスペクタの[3D位置とビュー]セクションにおける[遠近の角度]⁠再掲)

第33回図3 [プロパティ]インスペクタの[3D位置とビュー]セクションにおける[遠近の角度]

視野角に関わる大きさとして「焦点距離」がある。3次元空間の像を投影する2次元のスクリーンと視点との距離で表される図1)⁠視野角が狭いと焦点距離は長く,視野角を拡げれば焦点距離は短くなる※1)⁠遠近法の投影された座標は,この焦点距離を用いて計算できる。

図1 焦点距離

図1 焦点距離

3次元空間のオブジェクトと2次元平面に投影された像との大きさの比率を求めよう。視点を頂点とし,底辺をそれぞれ投影像ともとのオブジェクトとするふたつの三角形は相似だ。したがって,つぎの比例式が導かれる。

投影像の大きさ/オブジェクトの大きさ = 焦点距離 / (焦点距離 + z位置)
投影像の大きさ = オブジェクトの大きさ×焦点距離 / (焦点距離 + z位置)

この比例式は,オブジェクトの大きさだけでなく,3次元空間の座標すべてに対して成立つ。たとえば,横断歩道のように,同じ幅の水平線が等間隔で前方に並んでいたとする。これを2次元平面に投影すると,同じ幅の線つまり2点間の水平距離は,遠くなるほど短くなる。また,線と線との間隔も遠くにいくほど詰まって見える図2)⁠

図2 同じ幅の水平線が奥に向かって等間隔に並ぶ透視投影図

図2 同じ幅の水平線が奥に向かって等間隔に並ぶ透視投影図

このように3次元空間の座標を2次元平面のxy座標に変換するとき,上記の比例式で求められた遠近法投影(透視投影)の比率を用いればよい。

3次元空間の座標を2次元平面に透視投影する比率

焦点距離 / (焦点距離 + z位置)
※1
視野角が0から180度までなのに対して,焦点距離は無限大から0までの極めて幅広い値をとる。視野角と焦点距離の値には,つぎのグラフのような関係がある図3)⁠

図3 視野角と焦点距離

図3 視野角と焦点距離

焦点距離にもとづく透視投影の計算

3次元空間座標を2次元平面に透視投影するには,まず焦点距離を定めなければならない。もちろん,その値は自由に決められる。とはいえ,何か目安がほしい。視野角(⁠遠近の角度]⁠と異なり,焦点距離は[プロパティ]インスペクタには見当たらない(前掲第33回図3参照)⁠しかし,PerspectiveProjectionクラスのプロパティPerspectiveProjection.focalLengthとして,備わっている。

前述第33回「遠近法の投影」PerspectiveProjectionクラスで遠近法を操作するでご説明したとおり,PerspectiveProjectionオブジェクトはDisplayObject.transformプロパティの参照からTransform.perspectiveProjectionプロパティにより得られる。PerspectiveProjection.focalLengthプロパティの値は,視野角PerspectiveProjection.fieldOfViewプロパティとステージの幅Stage.stageWidthプロパティに応じて変わる図4)⁠

図4 PerspectiveProjection.focalLengthプロパティで焦点距離を調べる

図4 PerspectiveProjection.focalLengthプロパティで焦点距離を調べる

それでは,前回第42回のスクリプト3「3次元空間の頂点座標にもとづいて2次元平面にワイヤーフレームを描く」に,2次元平面への透視投影を加えてみよう。具体的には,Vector3Dオブジェクトの座標を2次元平面のPointインスタンスに換えて,Vectorオブジェクトのエレメントに納めて返す関数xGetVertices2D()を書き直す。

function xGetVertices2D(myVertices:Vector.<Vector3D>):Vector.<Point >  {
  var vertices2D:Vector.<Point> = new Vector.<Point>();
  var nLength:uint = myVertices.length;
  for (var i:uint = 0; i < nLength; i++) {
    var myVector3D:Vector3D = myVertices[i];
    // 透視投影の処理を加える
    vertices2D.push(new Point(myVector3D.x, myVector3D.y));
  }
  return vertices2D;
}

焦点距離は,デフォルトのPerspectiveProjection.focalLengthプロパティの値を使おう。この値は変数(nFocalLength)にとっておく。すると,関数xGetVertices2D()は,焦点距離とVector3Dオブジェクトのz座標値(Vector3D.zプロパティ)から透視投影の比率(前掲「3次元空間の座標を2次元平面に透視投影する比率」⁠を求め(変数nProjectionRatio)⁠xy座標に乗じればよい。関数xGetVertices2D()につぎのような手を加えれば,3次元空間座標が2次元平面に透視投影できる図5)⁠

var nFocalLength:Number = transform.perspectiveProjection.focalLength;   // 追加
function xGetVertices2D(myVertices:Vector.<Vector3D>):Vector.<Point >  {
  var vertices2D:Vector.<Point> = new Vector.<Point>();
  var nLength:uint = myVertices.length;
  for (var i:uint = 0; i < nLength; i++) {
    var myVector3D:Vector3D = myVertices[i];
    var nProjectionRatio:Number = 
      nFocalLength / (nFocalLength + myVector3D.z);   // 追加
    vertices2D.push(new Point(myVector3D.x * nProjectionRatio, 
      myVector3D.y * nProjectionRatio));   // 修正
  }
  return vertices2D;
}

図5 デフォルトの焦点距離にもとづいて3次元空間座標を2次元平面に透視投影する

図5 デフォルトの焦点距離にもとづいて3次元空間座標を2次元平面に透視投影する

この関数の書替えで,ことさら困ることはない。透視投影は正しく行われる。ただ,Vector3Dクラスをもう少し紹介するために,Vector3Dクラスのプロパティとメソッドを使って同じ処理を書いてみたい。

著者プロフィール

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

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

URLhttp://www.FumioNonaka.com/

著書

コメント

コメントの記入