Astro de 3D(15) PerspectiveProjection は放棄<6>
2008/06/19 21:03 - Astro
長々と PerspectiveProjection に馴染めないという話をしてきた「PerspectiveProjection は放棄」シリーズ。
じゃあどうすんのよ、ってことで PerspectiveProjection を使わずダイレクトに focalLength 相当の変数を扱うコードを提示して、ひとまずの最終回といたしとうございます。
- 今回のソース (ZIP)
変更点は実に単純。
PerspectiveProjection クラスオブジェクトを、Number 型変数 viewPoint に差し替えただけ。
以下それぞれのクラスの変更点。
Vertex.as の変更
PerspectiveProjection 使用版コードとの併記で説明に代えます。 てゆーか説明するまでもない。
/** * 投影処理(今回の透視投影) */ public function perspective( mat:Matrix3D, viewPoint:Number ):void { (1) _projection = mat.transformVector( _local ); (2) _projection.w = viewPoint / ( viewPoint + _projection.z ); (3) _projection.project(); }
/** * 投影処理(PerspectiveProjection を使用した透視投影) */ public function perspective( mat:Matrix3D, fov:PerspectiveProjection ):void { (1) _projection = mat.transformVector( _local ); (2) _projection.w = fov.focalLength / (fov.focalLength+_projection.z); (3) _projection.project(); }
Main.as の変更
viewPoint の値を何にするかという点で説明が必要かもしれません。
viewPoint はその絶対値に最小値が存在します(つまり最大限界があります)。
なぜ絶対値かというと viewPoint は常にマイナスの値を取るからです。 なぜ常にマイナスの値かというと左手座標系を採用しているからです。 そして viewPoint の絶対値を大きくすれば大きくするほど(つまり viewPoint の値を小さくすればするほど)投影は平行投影に近づきます。 このあたりは今まで説明したとおり。
では viewPoint の限界はどこにあるのか。
今回掲示したサンプルコードに沿って具体的な値で説明します。
まずこのサンプルコードにおける 3D オブジェクトの形態を今一度確認しますよ。
このサンプルでは1辺の長さが100の正方形によって構成された立方体を扱っています。(1)
そして、その立方体は中心の座標が ( 0, 0, 0 ) になっています。(2)
(1) // 頂点間の距離 private var DIST:Number = 100;
(2) // 頂点生成 private function createVertics():void{ vecVertex = Vector.( [ new Vertex( -DIST/2, DIST/2, -DIST/2 ), new Vertex( DIST/2, DIST/2, -DIST/2 ), new Vertex( DIST/2, -DIST/2, -DIST/2 ), new Vertex( -DIST/2, -DIST/2, -DIST/2 ), new Vertex( -DIST/2, DIST/2, DIST/2 ), new Vertex( DIST/2, DIST/2, DIST/2 ), new Vertex( DIST/2, -DIST/2, DIST/2 ), new Vertex( -DIST/2, -DIST/2, DIST/2 ) ] ); }
つまり各頂点のz座標は+50か-50のいずれかになります。
ではこの頂点が原点を中心に回転するとき、各頂点のz座標が取り得る値、つまりzの最大値と最小値はそれぞれ何でしょうか。
下図は件の立方体をXZ平面に二次元投射したものです。 記した座標は ( x, z ) になります。 これでY軸回転させたときのことを考えてみます。
図1は立方体の初期配置の状態、図2は立方体が回転して z が取り得る最大値と最小値になった状態を表しています。初期配置から45度回転したときですね。
もうお分かりでしょう。
立方体を構成する正方形の対角線、すなわち図でいうと三角形ABCにおけるBCの長さを求めればよいわけです。
角Aが90度、角B、Cがそれぞれ45度の直角三角形で、辺AB、ACがそれぞれ100の場合、BCの長さは100×√2≒141.21356 になりますね。
辺BCの中央が0なんだから、z の取り得る最大値は約70、最小値が約-70です。
なぜこの値を求めたのか。ここで再び下記の透視変換の公式を思い出してください。
l / ( l + z )
今回のサンプルコードでは viewPoint にある特定の値を設定するので、l はマイナスの値を持つ定数で、z がプラスマイナス両方取り得る変数ということになります。
この公式におけるタブーは以下の2点。
- l + z = 0 になってはいけない
- l + z > 0 になってもいけない
1.は説明するまでもないですね。0で除算というのは数学的にできないからです。
2.の理由は以下のとおり。
l はマイナスの定数です。
このサンプルコードが取る z の最小値(画面上手前にオブジェクトが来ている状態)は-70です。
つまり z が最小値のとき、この公式の値は、分母分子共にマイナスなのでプラスになります。
ここで l の絶対値が z 最大値の70よりも小さくなった場合どうなるか。
例えば l が -35 で z が70のとき公式の値は -35 / ( -35 + 70 ) = -35 / 35 = -1; となり、マイナスになります。
この公式の値がマイナスになるということは、scaleX と scaleY もマイナスになるということ。
つまり奥に移動したとき、サンプルは円なので分かりづらいですが、ある時点で上下左右反転してしまうことになります(それ以前にまず消失が発生します)。
ただ、たとえ分母分子ともにマイナスになるからとはいえ、l の値を -75 とか、極端に z の最小値に近い値にすると、歪み過ぎて、逆に 3D 空間っぽく見えません。
z 最小値の2~3倍の値くらいにしておくと、それっぽく見えるようになります。
以下、viewPoint をいろいろ変えてコンパイルしたサンプル。
viewPoint のベースは -DIST / 2 * Math.SQRT2 ≒ -70.7 です。
- viewPoint ≒ -35.4(0.5倍) (require Flash Player 10)
- viewPoint ≒ -70.7(1倍) (require Flash Player 10)
- viewPoint ≒ -141.4(2倍) (require Flash Player 10)
- viewPoint ≒ -212.1(3倍) (require Flash Player 10)
- viewPoint ≒ -282.8(4倍) (require Flash Player 10)
今までクドクドとエントリーしてきましたが、透視投影に関しては以下のエントリーがとても分かりやすいから、みんな、これを見るよりも、てっく煮ブログを見ようぜ。