3Dモデルの座標変換について
内容
初めに
ゲームなどで3Dの映像をテレビ画面やディスプレイでよく見かけると思いますが、画面の中で動き回るキャラクターや建物など3D空間に存在するオブジェクトはどのようにして2Dである画面に表示させているのでしょうか?
2Dのゲームでは座標の値として「X、Y」の2次元要素しか持たず、ディスプレイも2Dであるため、X,Y の座標位置を指定してオブジェクトを描画すれば、どの位置にどのように描画されるかは直感的にわかるかと思います。
しかし、3Dの場合はそう簡単にはいきません。3Dは名前の通り「3次元(3 dimension)」で「X, Y, Z」の3つの座標情報を持ちます。2Dであるディスプレイとは座標が異なるのでそのままではオブジェクトを描画することができません。
ではどうするのかというと、「3次元の情報を2次元の情報に変換」するという作業を行います。このことを一般的に「座標変換」と呼びます。3Dプログラミングを行ううえではこの座標変換は必須なので覚えておいてください。
3Dを2Dに変換するための座標変換にはいくつかの種類がありますが、プログラマが主に処理する座標変換は「ワールド変換」「ビュー変換」「射影変換」の3つになります。ここでは座標変換に関するものを一通り説明していきます。
左手座標系と右手座標系
3Dには下図のような各座標の向きが異なる「左手座標系」と「右手座標系」の2つの座標系があります。
Direct3D では主に左手座標系が使用されていましたが、右手座標系用で計算するための関数なども用意されています。しかし、XNA では右手座標系用の計算メソッドしか用意されていません。これは、他のアプリケーションで右手座標系が多く採用されているため、それにあわせたものだと思われます。
このサイトで紹介している XNA の Tips はすべて右手座標系を使用しています。
ローカル座標系(モデル座標系)
個々のモデルには原点を中心とした座標系を持っています。モデリングソフトなどでモデルを作成するときに原点を中心にして作成するのをイメージにするとわかりやすいかと思います。
ワールド座標系
モデルを任意の場所に配置したりするのがワールド座標系です。このワールド変換で特に何もしなかった場合は、モデルがローカル座標と同じように原点に配置されます。配置とは単純に原点から移動させるだけではなく、回転や拡大縮小なども行うことができます。
ビュー座標系
ワールド座標でモデルを配置したら、今度はその3D空間を「どこから見て」「どこを見ている」のかという情報が必要になります。それを行うのが「ビュー変換」です。ビュー変換は一般的にカメラとして表現されることが多いです。
この変換に必要なパラメータは「カメラの位置」「カメラの注視点」「カメラの上方方向」です。カメラの向きはこの3つのパラメータで求められます。下の図がカメラを第3者視点から見たイメージになります。
上の図のような配置で実際にカメラの視点から見たのが下の図になります(この時点ではまだスクリーン上への座標変換をしていないのであくまでもイメージです)。
先ほどの説明ではカメラを配置させて座標変換しているように思いますが、実際の計算ではワールド座標をカメラの位置と向きに合わせて変換しています。なので原点は下の図のようにカメラの位置になります。
射影座標系
3D空間をどの位置から見るかを決めたら、今度は「遠くにあるものを小さく」「近くにあるものを大きく」表示させる処理を行います。これを「射影変換」と呼びます。射影変換には「透視射影」と「正射影」の2つの方法がありますが、一般的に使われる「透視射影」のイメージは下のようになります。
透視射影では「視野角」「アスペクト比」「前方クリップ位置」「後方クリップ位置」のパラメータを使用します。上図の「視錐台」と書いてある領域が最終的にスクリーン上に表示されます。
「視野角」はカメラから見える視野範囲を指定します。角度を減らすとズームインし、増やすとズームアウトします。視野角は視錐台の垂直方向の値になります。
「アスペクト比」は視野角が垂直方向の角度であるのに対し、水平方向の角度を求めるのに使用します。水平方向の角度は「視野角×アスペクト比」で求まり、アスペクト比は基本的に表示しようとしているスクリーンの「幅÷高さ」の値を指定するのが普通です。この値を変更すると、表示される3Dオブジェクトが横に伸びたり、縦に伸びたりするようなイメージになります。
「前方クリップ位置」と「後方クリップ位置」は前後のどの範囲内までのオブジェクトを表示するかを決めるのに指定します。コンピュータの性質上無限遠までの表示ができないので、制限を付けるようにします。この値はZバッファの精度にも影響するので、表示する必要のない範囲まで描画領域に含めるのはお勧めしません。
透視変換したオブジェクトは下のような空間に変換されます。カメラの近くにあったオブジェクトは拡大され、遠くにあったオブジェクトは縮小されます。
これをわかりやすい図で示すと下のようになります。
実際にカメラの視点から見ると下のようになります。
射影変換のもう一つの方法として正射影がありますが、下のような可視領域を射影変換します。幅や高さが奥行きに関わらず一定であるため、奥行きによってオブジェクトの大きさが変わることがありません。
スクリーン座標系
射影変換を行った後、実際のスクリーンの座標に変換します。スクリーンといってもデバイスに設定されているビューポートの設定によって表示される位置や範囲が変わります。ですが、ゲームなどであればウインドウのクライアント座標がそのままビューポートになることが多いのであまり気にすることはないと思います。
スクリーンの座標(0, 0)は射影座標の(-1, 1, z)からの変換になります。同様にスクリーン座標の(width, height)は射影座標の(1, -1 ,z)からの変換になります。