このページの最終更新日 2020/10/25

3Dプログラミング(ワールド座標とスクリーン座標)

 このページは四元数と3Dプログラミングの続きのページです。このページでは、ワールド(World)座標とスクリーン(Screen)座標の変換について数学的に説明していくことにします。

1、スクリーンで選択したノードを赤くする

 「ワールド座標」「スクリーン座標」が何なのかを説明する前に実際に3Dモデルのサンプルを提示したいと思います。下のスクリーンに6×6×6の合計216個のノードが存在します。このスクリーン上でマウスの左ボタンを押したまま画面をドラッグするとモデルを回転させることが出来ます。右ボタンを押したままドラッグするとモデルを平行移動させます。また、マウスのホイールでモデルの拡大・縮小を行うことが出来ます。
 このノードはX,Y,Z座標の値がそれぞれ 0から5までの整数の地点に存在します。例えば、赤、緑、青のラインが交差するノードの座標は $(x, y, z) = (0, 0, 0)$です。赤のラインの先端の座標は $(x, y, z) = (0, 0, 5)$ となっています。ここで言っている座標とは「ワールド座標」のことを言っています 。

 一方、スクリーン座標というのは、その名の通り画面上の2次元平面で見たときにどの地点にいるかを示しています。ごちゃごちゃ言うよりも、まずは"物は試し"です。
1、スクリーン上で「Shift」キーを押したまま、左クリックして下さい。
2、クリックした後、マウスをスクリーン上で動かすと赤枠が表示されます。
3、216個の緑色のノードのうちの一部を赤枠で囲んで「Shift」キーを離して下さい。

 上手く動作したでしょうか。「Shift」キーを話すと赤枠で囲んだノードが赤色に変化したはずです。この赤枠は、マウスの動きに合わせて変化しますから、当然「スクリーン座標」を計算して赤枠を動かしています。一方、ノードの座標というのはモデル内部としては「ワールド座標」としてデータを持っています。
 しかし赤枠の中に存在するノードというのを、そのまま「スクリーン座標」と「ワールド座標」で比較することは出来ません。ノードの座標を「ワールド座標」ではなく「スクリーン座標」に計算し直す必要があります。


2、3次元の座標を2次元平面に投影する

 「ワールド座標」から「スクリーン座標」への変換は、遠くにある物体を小さく表示する投影投影カメラ(Perspectiveカメラ)か、遠くも近くもワールド座標で同じ大きさのものはスクリーン上でも同じ大きさで表示する正投影カメラ(OrthographicCamera)によっても扱いは異なりますし、一般的には4×4の行列として計算が行われ、予備知識なしにいきなり理解するのは少し難しいでしょう。難しいと感じた場合は、問題を分解したり簡単なレベルに戻ってそこから順に理解していくことも大切です。ここでは、3次元空間の中に適当に平面を突っ込んで、ここの平面に3次元空間にあるノードを投影したとき、平面上ではどのような座標として表されるかを考えてみます。

 まず、突っ込んだ平面上に、この平面で定義される原点を定めます。この原点は3次元空間では $\boldsymbol{r_0} = (x_0, y_0, z_0)$ と表現されるとします。次に、この平面上での水平方向と、鉛直方向はどちら向きかを定義します。
 ここで正の水平方向ベクトルを3次元空間では $\boldsymbol{e_h} = (x_h, y_h, z_h)$、正の鉛直方向のベクトルは$\boldsymbol{e_v} = (e_v, y_v, z_v)$ と表現されるものとします。また$\boldsymbol{e_h}$ベクトルと、$\boldsymbol{e_v}$ベクトルはそれぞれ互いに垂直な単位ベクトル(3次元空間で長さが1のベクトル)であり、(1)(2)(3)式を満たします。

$ \qquad (1) \qquad \boldsymbol{e_h}\cdot\boldsymbol{e_v} = 0$

$ \qquad (2) \qquad | \boldsymbol{e_h} | = 1$

$ \qquad (3) \qquad | \boldsymbol{e_v} | = 1$

 このとき、3次元空間の中の2次元平面 $\boldsymbol{r_{plane}}$ は、$\alpha, \beta$ を実数として(4)式で表すことが出来ます。

$ \qquad (4) \qquad \boldsymbol{r_{plane}} = \boldsymbol{r_0} + \alpha \boldsymbol{e_v} + \beta \boldsymbol{e_h}$


 さて、では3次元空間のある点 $\boldsymbol{r_1} = (x_1, y_1, z_1)$ を平面 $\boldsymbol{r_{plane}}$ に投影した点がどのように表されるかを考えます。平面に投影した点を $\boldsymbol{r'_1} = \boldsymbol{r_0} + \alpha_1 \boldsymbol{e_v} + \beta_1 \boldsymbol{e_h}$ と書けば、座標 ${r_1}$ と ${r'_1}$を結ぶ直線は平面に対して垂直ですから、(5)(6)式が成立します。

$ \qquad (5) \qquad (\boldsymbol{r_1} - \boldsymbol{r'_1}) \cdot \boldsymbol{e_v} = (\boldsymbol{r_0} + \alpha_1 \boldsymbol{e_v} + \beta_1 \boldsymbol{e_h} - \boldsymbol{r_1}) \cdot \boldsymbol{e_v}= 0$

$ \qquad (6) \qquad (\boldsymbol{r_1} - \boldsymbol{r'_1}) \cdot \boldsymbol{e_h} = (\boldsymbol{r_0} + \alpha_1 \boldsymbol{e_v} + \beta_1 \boldsymbol{e_h} - \boldsymbol{r_1}) \cdot \boldsymbol{e_h}= 0$

 (1)(2)(3)式により(5)式から(7)式が、(6)式から(8)式が導けます。

$ \qquad (7) \qquad \alpha_1 = (\boldsymbol{r_1} - \boldsymbol{r_0}) \cdot \boldsymbol{e_v}$

$ \qquad (8) \qquad \beta_1 = (\boldsymbol{r_1} - \boldsymbol{r_0}) \cdot \boldsymbol{e_h}$

 $\alpha_1$ と $\beta_1$ が求まりましたので、投影した点 $\boldsymbol{r'_1}$ も求まったことになります。ところで、3Dプログラミングの教科書ではよく行列の掛け算の表現になっているのを見かけます。そこに繋げていくために(7)(8)式も行列で表現することを考えてみます。まず次の(9)式のように2行3列の行列を用いて表現出来るはずです。

$\qquad (9) \qquad \begin{pmatrix} \alpha_1 \\ \beta_1 \end{pmatrix} = \begin{pmatrix} x_v & y_v & z_v \\ x_h & y_h & z_h \end{pmatrix} \begin{pmatrix} x_1 - x_0 \\ y_1 - y_0 \\ z_1 - z_0 \end{pmatrix}$

 (9)式のように行列で表現出来ましたが、3Dプログラミングの教科書で良く見かけるのは4×4の行列表現です。そこで(9)式を(10)式のように4×4の行列に変形してみます。

$\qquad (10) \qquad \begin{pmatrix} \alpha_1 \\ \beta_1 \\ 0 \\ 1 \end{pmatrix} = \begin{pmatrix} x_v & y_v & z_v & -(x_v x_0 + y_v y_0 + z_v z_0)\\ x_h & y_h & z_h & -(x_h x_0 + y_h y_0 + z_h z_0) \\ 0 & 0 & 0 & 0 \\ 0 & 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x_1 \\ y_1 \\ z_1 \\ 1 \end{pmatrix}$


 (9)式に比べて(10)式の何が良いのでしょうか。式の中の各項目を1つずつ見ていくことにします。(9)式も(10)式も左辺の$\alpha_1, \beta_1$は計算前は未知の値であり求めるべき値です。右辺の $x_v, y_v, z_v, x_h, y_h, z_h, x_0, y_0, z_0$ の9つの値は平面が決定されればこれらの値も決定されます。(10)式では4×4の行列の中に9つ全ての値が含まれています。(9)式では、これらの9つの値が2×3の行列と3成分の縦ベクトルに散らばっています。
 実用的な計算では、平面はだた1つに決まっており、この面に投影すべき点が複数存在するという状態であることが多いです。すなわち $x_1, y_1, z_1$ の場所には様々な値が入力されることになります。これを踏まえて考えると(10)式では式が複雑なようですが4×4の行列は1回だけ計算すれば良く、右側の4成分のベクトルに与えられた座標に対してただ単に掛け算だけを行っていれば良いことになります。一方で(9)式については、与えられた $x_1, y_1, z_1$ の座標に対して引き算と掛け算の演算を行わなければなりません。

 また通例は、突っ込んだ平面に対して垂直な単位ベクトル $\boldsymbol{e_d} = (x_d, y_d, z_d)$ を考え、座標 $\boldsymbol{r_1}$ との距離を表す $\gamma_1 = (\boldsymbol{r_1} - \boldsymbol{r_0}) \cdot \boldsymbol{e_d}$ を含めた(11)式の4×4の行列が用いられることが多いようです。

$\qquad (11) \qquad \begin{pmatrix} \alpha_1 \\ \beta_1 \\ \gamma_1 \\ 1 \end{pmatrix} = \begin{pmatrix} x_v & y_v & z_v & -(x_v x_0 + y_v y_0 + z_v z_0)\\ x_h & y_h & z_h & -(x_h x_0 + y_h y_0 + z_h z_0) \\ x_d & y_d & z_d & -(x_d x_0 + y_d y_0 + z_d z_0) \\ 0 & 0 & 0 & 1 \end{pmatrix} \begin{pmatrix} x_1 \\ y_1 \\ z_1 \\ 1 \end{pmatrix}$