!!!アフィン変換 元のオブジェクトが持つ頂点情報などは中心位置を(0, 0, 0)としたときの座標系であらわされます。これを「'''ローカル座標'''」と言います。 {{ref_image local_img_20040903.png}} ここから始まり、 すべてのオブジェクト(物体)は'''シーン'''という3D仮想空間内に配置されます。 この「シーン」を表現する座標系を「'''ワールド座標'''」と言います。 ローカル座標からワールド座標に移行する際に、オブジェクトの拡大縮小・回転・平行移動などが行われます。 ワールド座標にはオブジェクトだけでなく、光源・視点も配置されます。 {{ref_image world_img_20040903.png}} さらに、今度はこれを「視点(カメラ)位置から見たときの」座標に変換します。視点は情報として、「視点位置」と「視線方向」があります(その他、視野角度などの情報もありますが、これが使用されるのは「パースペクティブ変換」です)。これを元に変換すると、始点位置が(0, 0, 0)で視線方向がZ軸で奥に向かってZがプラスになる座標系に変換されます。これを「'''ビュー(視点)座標'''」と言います。 {{ref_image view_img_20040903.png}} ここまででは、まだ「3D」の情報です。実際は、2Dのコンピュータのスクリーンに 投影するわけですので「3D→2D」の投影処理を行う必要があります。 この2Dに変換後の座標系を「'''スクリーン座標'''」と言います(なお、この「スクリーン座標」という単語は場合によって呼び名が違うようで「'''パースペクティブ(透視)座標'''」といったりもします)。 また、ワールド座標やビュー座標からパースペクティブ座標に変換する 行為を「パースペクティブ(透視)変換」と言います。結果、スクリーンの中心が(0, 0)となります。また、2次元変換したためにZ値は不要と思われるかもしれませんが、ピクセルの前後関係を把握するために保持することが多いです(この透視座標でのZ値が「Zバッファ法」などで扱われるZ値になります)。 このままでは縦横の縮尺が実際の「スクリーン」とは異なり、また中心が(0, 0)のままのため、実際の描画領域のサイズに合わせます。このときに描画領域の左上が(0, 0)になるように変換されます。この最終的な座標を「'''ディスプレイ座標'''」または「'''デバイス座標'''」といったりします。これは、描画領域の左上を(0,0)としたときのピクセル位置を表します。 この個々の座標系の変換は、「4x4行列」で表現されます。 流れとしては *ローカル座標 *ワールド座標 *ビュー座標 *パースペクティブ座標 *ディスプレイ座標 の順に、それぞれ「ローカル座標→ワールド座標」変換行列、 「ワールド座標→ビュー座標」変換行列、 「ビュー座標→パースペクティブ座標」変換行列、 「パースペクティブ座標→ディスプレイ座標」変換(ここだけは行列を使用しない)、 の変換が行われます。行列自身は乗算できるため、あらかじめ乗算して座標系の計算負荷を省くこともできます。 このような座標変換のことを総称して「'''アフィン変換'''」と言います。 それでは、個々の変換を見ていくことにします。 まだ途中です。 !!ローカル座標→ワールド座標 ローカル座標からワールド座標への変換ですが、主に「拡大縮小」「回転」「平行移動」が行われます。順番に注意してください。 回転や拡大縮小は中心(0, 0, 0)を基点として行われます。そして、最後にワールド座標での位置に平行移動します。 回転はX/Y/Z軸中心の変換を書きますが、このような回転ではなく 「ある位置からある目標点を見たときの変換」なども回転に値しますので 以下は参考程度も考えてください。 !拡大縮小 X/Y/Z軸方向に拡大縮小します((Sx, Sy, Sz)で指定)。 1.0のときは等倍になります。 [ Sx 0 0 0 ] [X' Y' Z' W'] = [X Y Z 1] [ 0 Sy 0 0 ] [ 0 0 Sz 0 ] [ 0 0 0 1 ] X' = X * Sx; Y' = Y * Sy; Z' = Z * Sz; !X軸中心に回転 X軸を中心にθx回転させます。 [ 1 0 0 0 ] [X' Y' Z' W'] = [X Y Z 1] [ 0 cosθx sinθx 0 ] [ 0 -sinθx cosθx 0 ] [ 0 0 0 1 ] X' = X; Y' = Y * cosθx - Z * sinθx; Z' = Y * sinθx + Z * cosθx; !Y軸中心に回転 Y軸を中心にθy回転させます。 [ cosθy 0 -sinθy 0 ] [X' Y' Z' W'] = [X Y Z 1] [ 0 1 0 0 ] [ sinθy 0 cosθy 0 ] [ 0 0 0 1 ] X' = X * cosθy + Z * sinθy; Y' = Y; Z' = -X * sinθy + Z * cosθy; !Z軸中心に回転 Z軸を中心にθz回転させます。 [ cosθz sinθz 0 0 ] [X' Y' Z' W'] = [X Y Z 1] [ -sinθz cosθz 0 0 ] [ 0 0 1 0 ] [ 0 0 0 1 ] X' = X * cosθz - Y * sinθz; Y' = X * sinθz + Y * cosθz; Z' = Z; !ワールド座標に平行移動 移動後のワールド座標位置を(Px, Py, Pz)とします。 [ 1 0 0 0] [X' Y' Z' W'] = [X Y Z 1] [ 0 1 0 0] [ 0 0 1 0] [Px Py Pz 1] X' = X + Px; Y' = Y + Py; Z' = Z + Pz; これらの行列を乗算し(行列をかける順番に注意してください)、1つの4x4行列でローカルからワールド座標変換ができるように します。 !!ワールド座標→ビュー座標 視点から見たときの座標に変換します。 まずこれを求めるには、視点位置があり目標点(視点が見ている位置)があることを 理解してください。 視点位置をV(Vx, Vy, Vz)、目標点位置をH(Hx, Hy, Hz)とします。 このとき視線は、 Dx = Hx - Vx; Dy = Hy - Vy; Dz = Hz - Vz; で求まります。 cosα = -Dz / sqrt(Dx * Dx + Dz * Dz); sinα = Dx / sqrt(Dx * Dx + Dz * Dz); cosΒ = sqrt(Dx * Dx + Dz * Dz) / sqrt(Dx * Dx + Dy * Dy + Dz * Dz); sinΒ = -Dy / sqrt(Dx * Dx + Dy * Dy + Dz * Dz); [cosα 0 -sinα 0] [1 0 0 0] A = [ 0 1 0 0] [0 cosΒ sinΒ 0] [sinα 0 cosα 0] [0 -sinΒ cosΒ 0] [ 0 0 0 1] [0 0 0 1] このときの行列Aが視点から目標点を見たときの「回転行列」になります(Y軸中心にα回転させてX軸中心にΒ回転させたのに等しい)。 これだけでは「視点の位置が(0, 0, 0)になるようにする」「Z軸は奥に向かってプラス」ができていないので、それも加えると以下の式になります。 [ 1 0 0 0] T = [ 0 1 0 0] [ 0 0 1 0] [ -Vx -Vy -Vz 1] [ 1 0 0 0] U = [ 0 1 0 0] [ 0 0 -1 0] [ 0 0 0 1] [X' Y' Z' W'] = [X Y Z 1] * T * A * U T、A、Uは4x4行列の乗算になるため、順番にかけ合わせて1つの行列にまとめます。 ただし、この「視点位置」「目標点位置」で求まるビュー座標への変換行列で 求めることができない例外があります。 視点が垂直に上向き・または下向きの場合(Dx = Dz = 0.0の場合)、 cosαとsinα計算時の分母が0になるため計算できません。 !!ビュー座標→パースペクティブ座標 !!パースペクティブ座標→ディスプレイ座標 です。