iFlash3Dさんの記事拙訳第四弾。
iFlash3D "Flash 3D – Molehill Cameras"
前回ちらっと触れたジオメトリパイプラインのビュー(カメラ)座標変換について。
原点からの座標ではなく、カメラから見た座標を使おうという話。
これができれば一人称視点のウォークスルーはパッと作れちゃう。
---
カメラがなくては映画を作ることはできない。
同じように3Dカメラがなくては3Dシーンをレンダリングすることはできない。
しかし3Dシーンを歩き回れないと、どれだけつまらないか想像してみよう。
あなたが3Dニュースレポーターだとしよう。カメラを持ってこの3D世界を歩き回れる。この世界の住人となれるわけだ。
世界を固定された視点で見たくはないだろう。動き回りたいはずだ。そしてその場所から見えるものを見たいはずだ。
キャラクターの目を通して世界を見たいかもしれない(すなわち一人称視点、FPS)、どうにかしてキャラクターを追いかけ、常に視界に入れたいかもしれない(つまり三人称視点、TPS)。
これをコーディングするためには、ワールド座標系の視点や他の固定された視点からではなく、カメラ視点から3D世界がどのように見えるのかを写さなくてはならないだろう。
3Dシーンには3Dモデルを配置する。これらのモデルはすべてモデル固有の座標系内の頂点と面の集まりとして記述される。この空間をモデル(ローカル)スペースと呼ぶ。
こういったオブジェクトを3Dのシーンに配置すると、モデルの頂点をワールド変換行列を使って変換する。各オブジェクトはそれぞれ、自身がワールド内のどこにあるのか、どちらを向いているのかを定義したワールド行列を持っている。
この新しい座標系をワールドスペース(グローバルスペース)と呼ぶ。ワールド変換行列を各オブジェクトに関連付けることによって管理する簡単な方法だ。
ここで、原点からの世界でなく、カメラからの世界が欲しいわけだ。
ある座標(xc, yc, zc)に配置され、ある方向を向いたカメラオブジェクトを使い、そのワールド変換行列を得る。
オブジェクトをカメラ視点から見える形に変換するために、このカメラのワールド座標の逆行列を計算しなくてはならない。
viewTransform = cameraWorldTransform.clone() viewTransform.invert();
そしてカメラを通して見えるシーンを得るためには各オブジェクトをレンダリングする際、ワールド変換にビュー変換を追加する。
前回話した透視投影変換はビュー変換の直後に行う。
つまり変換の流れは以下のようになる
まず、矢印キーでカメラを移動、回転するコードを準備しておく。
protected function keyDownEventHandler(e:KeyboardEvent):void { switch (e.keyCode){ case 37: cameraRotationAcceleration = -ROTATION_ACCELERATION; break case 38: cameraLinearAcceleration = LINEAR_ACCELERATION; break case 39: cameraRotationAcceleration = ROTATION_ACCELERATION; break; case 40: cameraLinearAcceleration = -LINEAR_ACCELERATION; break; } }
protected function keyUpEventHandler(e:KeyboardEvent):void { switch (e.keyCode){ case 37: case 39: cameraRotationAcceleration = 0; break case 38: case 40: cameraLinearAcceleration = 0; break } }
protected function updateViewMatrix():void { cameraLinearVelocity.z = calculateUpdatedVelocity(cameraLinearVelocity.z, cameraLinearAcceleration, MAX_FORWARD_VELOCITY); cameraRotationVelocity = calculateUpdatedVelocity(cameraRotationVelocity, cameraRotationAcceleration, MAX_ROTATION_VELOCITY); cameraWorldTransform.appendRotation(cameraRotationVelocity, Vector3D.Y_AXIS, cameraWorldTransform.position); cameraWorldTransform.position = cameraWorldTransform.transformVector(cameraLinearVelocity); viewTransform.copyFrom(cameraWorldTransform); viewTransform.invert(); }
updateViewMatrixメソッドがキーだ。前のcameraWorldTransform行列をまず回転させ、その後に移動させて新しい行列を得る。このcameraWorldTransformの逆行列をとり、viewTransformを計算する。
calculateUpdatedVelocityはただ単にイージングで加算するメソッドだ。
protected function calculateUpdatedVelocity(curVelocity:Number, curAcceleration:Number, maxVelocity:Number):Number { var newVelocity:Number; if (curAcceleration != 0){ newVelocity = curVelocity + curAcceleration; if (newVelocity > maxVelocity){ newVelocity = maxVelocity; }else if (newVelocity < -maxVelocity) { newVelocity = – maxVelocity; } } else { newVelocity = curVelocity / DAMPING; } return newVelocity; }
毎フレームレンダリングをする前にupdateViewMatrixを呼ぶ。
そしてvertexシェーダに送る変換行列にviewTransformを含めておく。
updateViewMatrix(); var m:Matrix3D = new Matrix3D(); m.appendRotation(getTimer() / 30, Vector3D.Y_AXIS); m.appendRotation(getTimer() / 10, Vector3D.X_AXIS); m.appendTranslation(0, 0, 2); m.append(viewTransform); m.append(projectionTransform);
---
実行した結果とソースコードは元記事にリンクがある。矢印キーでカメラがイージングとともに移動、回転し、3D世界を動き回ることができる。
Matrix3D.positionでMatrix3Dが表す座標をVector3Dで得ることができる。この座標(つまりカメラ座標)を中心にappendRotationすることで、その場で回転をすることができる。
また、cameraWorldTransform.position = cameraWorldTransform.transformVector(cameraLinearVelocity);
の部分でグローバルxyz座標ではなく、カメラオブジェクトのローカルxyz座標に沿って移動をしている。
このおかげで上矢印を押すと常にカメラの前方に進むことができている。
ここまでで一通りのStage3Dの技術を学んだ。カメラを自由に操作することができれば、ライブラリを使うことなく3Dコンテンツを作成できるだろう。
Author:9ballsyn
ActionScriptについて
最近はMolehill