1-9 座標変換其の四
お 俺が
お お前を
こ 殺すのか
呂布奉先(@蒼天)
3Dのオブジェクトを画面に描画するための幾何学的な計算を考えてみましょう。
まず(全ての)基準となる座標系を定めます。これをワールド座標とかグローバル(大域)座標とか
絶対座標とか呼ぶようです。うちではワールド座標と呼ぶことにします。
次にオブジェクトに回転平行移動などを施してワールド座標でのオブジェクトの位置・方向を決定します。
見かたを変えてオブジェクトがオブジェクトに固有の座標系(これをローカル座標と呼びます)を基準として
作られていると考えると、オブジェクトのワールド座標から見た位置・方向というのは
ローカル座標のワールド座標から見た位置・方向であり、ワールド座標でのオブジェクトの
位置・方向を決定するというのはローカル座標からワールド座標への変換ということになります。
…わかりづれ〜な。まぁやってることはどっちも同じです。こいつをワールド変換と呼びましょう。
さてワールド座標で所定の位置にオブジェクトを配置しました。つぎはカメラ(観察者)からどのように見えるかを
計算しなければなりません。これはワールド座標からカメラを基準とした座標系(カメラ座標・ビュー座標)
への変換ということになります。これをビュー変換と呼ぶことにします。
ビュー変換が終わりました。次は射影変換(透視変換)なるものを行います。
遠近感をだすためにパースをつけたりするやつです。ひらたくいうと、遠くのものは小さく。近くのものは大きく。
お前のものは俺のもの。俺のものは俺のもの。です。
最後はスクリーン(ウィンドウ)座標への変換です。ビューポート変換とかスクリーン変換とか呼ぶみたいです。
ビューポート変換された頂点はラスタライザに渡され、スクリーンが塗りつぶされます。大体こんな感じです。
この
|
ワールド変換 → ビュー変換 → 射影変換 → ビューポート変換
|
という一連の変換の流れを、ジオメトリパイプラインと呼んだりします。
Direct3Dでは、IDirect3DDevice9::SetTransformの
D3DTS_WORLDMATRIX( n ) : ワールド変換
D3DTS_VIEW : ビュー変換
D3DTS_PROJECTION : 射影変換
でそれぞれに対応する変換行列を設定します。
なお、DirectXではビューポート変換だけ仲間はずれで、IDirect3DDevice9::SetViewportで設定されます。
ワールド変換は分かっている(よね)ので、それ以外の変換について次から簡単に説明していきます。
…骨とは関係ないな。
ビュー変換はワールド変換とほとんど同じで、変換の向きだけが異なります。
ワールド変換になぞらえていうと、ワールド座標からローカル座標への変換に相当します。
つまり、ワールド座標からカメラの位置・方向を表す座標系への変換ということです。
では試しにビュー行列を作ってみましょう。D3DXMatrixLookAtLHと同等の方法でいきます。
指定するパラメータは視点(カメラ)の位置ベクトルPと注視点の位置ベクトルE、および
カメラの上方向を表すベクトルUです。P、E、Uはワールド座標で指定します。
これらからビュー変換行列を求めるのは
1-7.3とほぼ同じです。
まずZ = Normalize( E - P )で視線方向の単位ベクトルを求めます。これをビュー座標のZ軸とします。
次にX = Normalize( U × Z )でビュー座標のX軸を求め、Y軸はY = Z × Xで求めます。
UがそのままY軸になるわけではないことに注意してください。
ワールド座標からビュー座標への変換なので、回転部はXYZを列ベクトルとした行列になります。
平行移動部は
1-7.3でも説明したように、-Pをビュー座標に変換したものになります。
まとめると

(式1-9.1)
となります。
射影変換には色々ありますが、ここではDirect3Dで標準的に用いられている方法を説明します。
まずは下の図をご覧ください。

(図1-9.1)
なんか初めて3次元の図を描いた気がする…
射影変換というのは、ビュー空間の視錘台を同次クリップ空間の半立方体(つう言葉があるかは知らんが)
に変換します。半立方体は
-1 <= x <= 1
-1 <= y <= 1
0 <= z <= 1
|
の範囲になります。近クリップ平面ではz=0、遠クリップ平面ではz=1となります。
遠くのものは相対的に小さく、近くのものは相対的に大きく変換されるのが分かるでしょうか?
視錘台は射影変換行列により定まります。
では実際に射影変換行列をつくってみましょう。
視錘台を+X方向からみると

(図1-9.2)
となります。ビュー空間でのZ座標がdの地点では、視錘台の上下面のY座標は±hとなります。
これが±1になるように変換するのですから、Y座標をd tan(θ/2)で割ってやればよいことになります。
θを(Y方向の)視野角と呼びます。θは変換対象となる点の座標には依存しないので問題ないのですが、
dは点のZ座標であるのでスケーリング行列として設定することは出来ません。そこで同次座標の出番です。
変換後のwが変換前のzと等しくなるようにすれば、wによる除算でY方向のスケーリングが完了します。
X方向のスケーリングもY方向と同様に行えますが、D3DXMatrixPerspectiveFovLHなどでは、
スクリーンのアスペクト比(幅/高さ)と遠近クリップ平面のアスペクト比を合わせるため、
X方向のスケールはY方向のスケールをアスペクト比で割ることで求めています。
ここまでの変換は次のようになります。
θはY方向の視野角、αはアスペクト比です。
次にz座標の変換を考えます。(図1-9.2)でいうとz=Nのときに0、z=Fのときに1になるような変換です。
まずz=Nのときに0にするには単純に-Nの平行移動となります。こうすると変換後のz座標を
wで除算したものは( z - N ) / z となります。これがz=Fのときに1になればよいのですから、
F / ( F - N )を掛ければよいことになります。
これらを全てまとめると、射影変換行列M
projは

(式1-9.2)
となります。
ついでに。
視錘台というのは見える領域を表しています。視錘台の外にあるオブジェクトは見えません。
つまり描画処理を行う必要はありません。なにせ見えないんですから。
そこでオブジェクトを内包する単純な形状(球など)を考え、それが視錘台の内部にあるかを判定し、
内部に無ければ描画処理を行わないようにします。これを視野カリングとか視錘台カリングとか呼びます。
可視判定の基本中の基本です。
ジオメトリパイプラインのトリを飾りますスクリーン変換です。
スクリーン座標は左上隅を原点とし、右方向を+X、下方向を+Yとすることが多いです。
同次クリップ空間とスクリーン空間を重ねると

(図1-9.3)
こんな感じ。
まず同次クリップ空間のXY平面の正方形をスクリーンと同じ大きさにするような
スケーリング(&Y軸反転)を行い、次に原点をスクリーン中央にもっていくような
平行移動をすればいいわけです。行列で表すと

(式1-9.3)
ってな感じです。D3DVIEWPORT9にはMinZとMaxZなるメンバがありますが、zの範囲を
[ MinZ, MaxZ ]にするということなので多分式1-9.3の下のやつみたいなことをやっているんでしょう。
使ったことないから知らんが。
目次に戻る
1-10骨其の壱