その10 カメラの情報を取得する
FBXにはカメラの情報を含めることができます。その情報はかなり多いのですが、ゲームに必要な情報は限られています。様々なパラメータの中からゲームに(≒DirectXに)必要な情報を抜き取るのが今回の目的です。
@ DirectXに必要なカメラの情報
DirectXには「カメラ」というインターフェイスは無いのですが、それに相当する概念として「ビュー行列」と「射影変換行列」が定義されています。ビュー行列と射影変換行列に必要な情報はそれぞれ以下の通りです:
ビュー行列 ・ カメラの位置
・ 対象物の位置
・ 空ベクトル(アップベクトル)射影変換行列 ・ 視野(Field of View)≒画角
・ アスペクト比
・ near平面までの距離
・ far平面までの距離
FBXのカメラからは上の情報を直接もしくは間接的に取得可能です。ただゲームによってどのパラメータを用いるか異なると思います。3Dツールで演出したポリ劇を再現したい場合にはすべてが必要になります。
A 直接取れる情報
FBXに含まれているカメラの情報はKFbxCameraにまとめられています。KFbxCameraはKFbxSceneからノードを辿ると取得する事ができます:
カメラオブジェクトの取得 KFbxScene* scene;
KFbxNode* node;
...ノードを巡回...
KFbxNodeAttribute *pAttrib = node->GetNodeAttribute();
if ( pAttrib ) {
KFbxNodeAttribute::EAttributeType type = pAttrib->GetAttributeType();
if ( type == KFbxNodeAttribute::eCAMERA ) {
KFbxCamera* camera = (KFbxCamera*)pAttrib; // カメラオブジェクトへキャスト
}
}
ノードの巡り方についてはFBX修得編その3をご覧下さい。ノード属性がeCAMERAと判別できたらKFbxNodeAttributeポインタをKFbxCameraポインタにキャストします(ダウンキャストは保障されています)。
@の表にある項目の内KFbxCameraオブジェクトから直接取得できる物について、以下のソースで示します:
直接取得できる情報 KFbxVector4 upVector = camera->UpVector.Get(); // アップベクトル
double aspectHeight = camera->AspectHeight.Get(); // アスペクト高
double aspectWidth = camera->AspectWidth.Get(); // アスペクト幅
double nearZ = camera->GetNearPlane(); // near平面距離
double farZ = camera->GetFarPlane(); // far平面距離
double aspectRatio = aspectWidth / aspectHeight; // アスペクト比
KFbxCameraは「プロパティオブジェクト」の塊で構成されています。プロパティオブジェクトは公開されているため、上記のようにプロパティオブジェクトに直接アクセスしてGetメソッドで情報を取得しています。アスペクトは幅高で取得可能なため、最後にその比を取っています。
位置の情報(カメラ位置、対象物の位置)と画角はKFbxCameraに直接含まれていないため、少しだけ回り道をする必要があります。
B 画角の算出
画角は視野範囲を角度で示したものです。DirectXの場合はカメラ真正面の方向に対する上側限度(Y軸限度)方向の角度(Field of View in the y direction)になっています。この値はFBXに含まれていませんので算出する必要があります。
カメラは簡単に言えばレンズが切り取った空間をフィルムに焼き付けるもので、以下のような模式図になります:
これはカメラを真横から見た状態です。レンズの根元にフィルムがあり、外部の空間はレンズの中央で一度合焦して反転してフィルムに焼き付けられます。このフィルムから合焦点までの距離を合焦距離(focal length)と言います。画角は上図にある合焦点から広がる角度の事を言います。ただしDirectXの画角は上の角度の半分です。
合焦距離とフィルム高はKFbxCameraに情報が含まれていて取得可能です。直角三角形の直角を構成する2辺があれば、直角以外の2角の算出は三角関数から簡単に算出できます。上の場合は、
とtanで表せるので、画角/2は、
となります。
以上から、(DirectX用の)画角は次のように算出できます:
画角を算出 double inch_mm = 25.4; // インチ→ミリ
double filmHeight = camera->FilmHeight.Get(); // フィルム高(インチ)
double focalLength = camera->FocalLength.Get(); // 合焦距離(ミリ)
double filmHeight_mm = inch_mm * filmHeight;
double fovY = atan2( filmHeight_mm , 2.0 * focalLength );
double fovY_Degree = fovY * 180 / 3.14159265358979;
FBXではデフォルトで合焦距離はミリ単位、フィルム高はインチ単位になっています。上の計算では単位をミリに合わせています。フィルム高はKFbxCamera::FilmHeightプロパティ、合焦距離はKFbxCamera::FocalLengthプロパティからそれぞれ取得できます。後はアークタンジェントで画角を計算するだけです。上のfovYはラジアン角です。デグリー角にしたい場合は180/πを掛けて下さい。
C 位置の情報
カメラの位置情報はKFbxCamera::Positionプロパティからも取得できるのですが、時間軸での位置情報の場合はノードから取得します。これは対象物の位置も同様です。
カメラを保持しているノードはKFbxCamera::GetNodeメソッドで得られます。ノードが持っているオブジェクトの位置の取得についてはFBX修得編その8「位置情報を取得する」で取り上げているようにKFbxNode::GetGlobalFromCurrentTakeメソッドを使います。ノードは対象オブジェクトのノードも持っていて、KFbxNode::GetTargetメソッドでそれを取得できます。カメラのノードの場合、これはカメラが注視しているオブジェクトのノードとなります:
位置情報を取得 KTime curTime; // 現在の時刻(設定して下さい)
// カメラの現在位置(姿勢)
KFbxXMatrix curMat = node->GetGlobalFromCurrentTake( curTime );
// 対象物の現在位置(姿勢)
KFbxNode* target = node->GetTarget();
KFbxXMatrix targetMat;
if ( target ) {
targetMat = target->GetGlobalFromCurrentTake( curTime );
}
上で取得できるのは位置の情報(ベクトル)ではなくて行列(姿勢行列)です。行列には位置情報が含まれているので、そこからカメラの位置と対象物の位置がわかります。この2つの位置情報とアップベクトルがあれば、DirectXでのビュー行列を作成できます。
D 右手系に注意!
ビュー行列を作成する時には得たカメラの姿勢行列から位置を抽出するのですが、この時右手系座標に注意する必要があります。3Dモデリングツールが右手系の場合、カメラの位置も当然右手系座標軸で表されます。この情報を左手系であるDirectXに持っていくと、左右反転した位置取りになってしまいます。
これを防ぐには行列からカメラの位置を取得した後にZ成分の符号を反転させます。これで3Dモデリングツールでの見た目とDirectXでの見た目が一致します。
カメラの情報は必ずしもゲームで全部使うわけではありませんが、かっこいいカメラワークを演出しようと思うとやはり3Dモデリングツールでつけたモーションを流用したくなります。上のようにFBX
SDKは割と簡単にカメラの情報を提供してくれるため大変重宝します。
この章で取り上げたカメラの情報は極一部です。FBXが持つカメラのプロパティは膨大にありますので、興味のある方はFBX
SDKのマニュアルを覗いて見て下さい。