投影の計算法

draft CG

投影の手順 では、ウィンドウとクリッピング面でビューボリュームを決め、それを正規化ビューボリュームへ移すまでの流れを見ました。
ただし、どの点がどこへ移るのかという計算そのものには触れていませんでした。

ここでは、ウィンドウまでの距離・ウィンドウの大きさ・クリッピング面までの距離をもとに、この変換を行列として組み立てていきます。

カメラ座標系
CG

投影の計算は、視点を原点にとった座標系の上で行います。カメラを基準にとることから、この座標系をカメラ座標系といい、視点座標系とも呼ばれます。

軸を右、 軸を上に向けると、残る 軸は奥行きの方向になります。ここでは、この3軸を右手系 でとることにします。右手系では 軸の正の向きが画面の手前を向くため、視線は 軸の負の向きになります。

右手系と左手系のどちらを採用するかは環境によって異なります。はじめから左手系でカメラ座標系をとる環境では、次の節で行う 軸の反転は要りません。

ビューボリュームの形と大きさは、ウィンドウとクリッピング面の取り方で決まりました。ウィンドウまでの距離・ウィンドウの大きさ・クリッピング面までの距離を、次のように記号で置いておきます。

  • ウィンドウまでの距離:
  • ウィンドウの横幅の半分:
  • ウィンドウの縦幅の半分:
  • 前方クリッピング面までの距離:
  • 後方クリッピング面までの距離:

いずれも視点を基準に測った長さなので、5つとも正の値です。ウィンドウは、視線がその中心を通るように置かれているものとします。

視線が 軸の負の向きであるため、ビューボリュームの中の点は 座標が負になります。視点から遠ざかるほど は小さくなり、ウィンドウは 、2枚のクリッピング面は と の面に来ます。

左手系への変換
CG

負の奥行きをそのまま持ち回ると、距離として与えた と、点の 座標とで符号が食い違ったままになります。奥にあるものほど が小さいという並びも、前後を比べる処理では扱いにくくなります。

そこで、投影の計算に入る前に 軸の向きだけを反転させ、左手系へ移します。向きの反転は、鏡映 のうちxy平面に関する鏡映にあたります。変わるのは 座標の符号だけなので、行列は次のようになります。

この鏡映を通したあとの座標系では、視線が 軸の正の向きになります。ウィンドウは 、前方クリッピング面は 、後方クリッピング面は の面に置かれ、視点からの距離として与えた値が、そのまま 座標になります。ウィンドウの右端は 、上端は です。

右手系のカメラ座標系と、xy平面に関する鏡映で移したあとの左手系を並べた図。視線の向きと、ウィンドウ・前方クリッピング面・後方クリッピング面がz軸上のどこに来るかを対比して示す

奥へ行くほど が大きくなる並びも、この後で効いてきます。ビューボリュームの前後を判定するクリッピングでも、重なった面のどちらが手前かを比べる処理でも、 座標の大小をそのまま前後関係として使えます。

以降の計算はすべてこの左手系で行い、鏡映を通したあとの座標を改めて と書きます。

軸の反転は投影の種類によらず必要なので、実装では、このあと求める行列と合成 して1本の行列にまとめておきます。

透視投影の計算法
CG

透視投影 のビューボリュームは、奥へ行くほど断面が大きくなる四角錐台でした。求めるのは、この四角錐台を正規化ビューボリュームへ移す変換です。

ここでは、次の直方体を正規化ビューボリュームとします。

まず、ビューボリュームの中の点 が、ウィンドウの上のどこへ写るかを求めます。視点と点を結んだ投射線がウィンドウの面と交わる位置が、その点の像です。

点と像は、視点から伸びる同じ投射線の上にあります。それぞれから 軸へ垂線を下ろすと、視点を頂点とする2つの直角三角形ができ、この2つは相似です。
奥行きの比がd : zなので、 軸に垂直な向きの座標も同じ比で縮み、像は次の点になります。

視点・空間の点・ウィンドウ上の像の位置関係を横から見た図。視点を頂点とする2つの相似な直角三角形を示し、奥行きの比 d : z が像の縮み方になることがわかるようにする

ビューボリュームの中の点は前方クリッピング面より奥にあるので、 が0になってこの割り算が破綻することはありません。視点に近すぎる点を計算の対象から外すのも、前方クリッピング面の役割です。

像がウィンドウに収まっているかどうかは、像の 座標を 、 座標を と比べればわかります。正規化ビューボリュームでは、この と にあたる位置を1に揃えるので、それぞれを と で割ります。

もとの座標にかかっているのはd / rとd / tという定数倍、つまり拡大・縮小です。これに、点ごとに値の変わる での割り算が加わります。

残るは 座標です。前方クリッピング面を0、後方クリッピング面を1へ写したいのですが、 座標の写り方だけを自由に決めることはできません。同次座標では、変換後の4つの成分をすべて同じ値で割って通常の座標へ戻すため、 座標と 座標を で割るのなら、 座標も で割られることになります。

ここには2種類の「正規化」が現れます。ビューボリュームを決まった大きさの直方体へ揃えるのがビューボリュームの正規化、変換後の同次座標をwで割って通常の座標へ戻すのが同次座標の正規化です。

そこで、 の一次式を で割った形を置き、2枚のクリッピング面での値から係数を決めます。 で が0になるには分子が に比例していればよいので、定数 を使って次のように書けます。

あとは、 のときに が1になるように を決めます。

を戻すと、 座標の写り方が決まります。

3つの式を1つの行列にまとめます。 で割る操作そのものは行列のかけ算では書けませんが、最下行を(0 0 1 0)にすれば変換後の が になり、割り算は通常の座標へ戻す段階に任せられます。

得られた同次座標を 成分の で割ると、先に求めた3つの式に戻ります。

ここで注目したいのは、最下行が(0 0 0 1)ではないという点です。最下行を一般の値まで広げた変換が射影変換 で、透視投影の行列はまさにその形をしています。点ごとに違う で割ることが、奥のものほど小さく写る縮み方をつくっているのです。

変換後のz値

座標は、 座標と 座標と同じ で割られました。この割り算のために、変換後の はもとの に比例しません。
先ほどの式を分けて書くと、 が の逆数の一次式になっていることがわかります。

が分母に入っているので、 と の関係は直線ではなく双曲線です。 が から へ動く間、 は0から1へ増えていきます。その増え方は一定ではなく、手前では急で、奥へ行くほど緩やかになります。

横軸をもとのz、縦軸を変換後のz値としたグラフ。z = n で0、z = f で1を通る双曲線を描き、等間隔に並べたzが、変換後には手前へ詰まって写ることがわかるようにする

増え方が緩やかになっても、増えることは変わりません。もとの の大小と の大小は一致するので、クリッピングでの前後の判定や、重なった面の奥行きの比較には、変換後の値をそのまま使えます。

ただし、奥では の差がごく小さくなります。限られた桁数で を保持すると、遠くにある面どうしの差が値として残らず、どちらが手前なのか判定できなくなってしまいます。前方クリッピング面を視点へ近づけるほど、この偏りは強まります。
そのため、前方クリッピング面までの距離 は、必要以上に小さくとらないようにします。

平行投影の計算法
CG

平行投影 では投射線が視線に平行なので、点は奥行きに関わらずウィンドウへまっすぐ写ります。像の 座標と 座標は、もとの座標のままです。

ビューボリュームも、どこで切っても断面が変わらない直方体でした。ウィンドウの大きさとクリッピング面の位置がそのまま範囲になり、 は から 、 は から 、 は から までがビューボリュームです。

あとは、3本の軸の目盛りを正規化ビューボリュームに合わせるだけです。 座標と 座標は と で割れば-1から1に収まり、 座標は で割って の分だけずらせば0から1に収まります。

この関係を行列にまとめると、拡大・縮小と平行移動を組み合わせた行列になります。

透視投影の行列と見比べると、違いは2か所です。ウィンドウまでの距離 がどこにも現れず、最下行が(0 0 0 1)に戻っています。

が消えるのは、平行投影では写る範囲がウィンドウの大きさだけで決まり、投影面をどこに置いても写り方が変わらないためです。

最下行が(0 0 0 1)であることは、変換後のwが1のまま、つまり座標を割る操作が要らないことを意味します。平行投影の正規化はアフィン変換の範囲に収まり、 座標も一次式で写るため、奥行きの間隔はもとの比のまま保たれます。

アフィン変換は平行性を保つので、空間で平行な直線は、像の上でも平行なままです。一方、透視投影の行列は で割る射影変換なので、平行だった直線が像の上では1点に集まります。その集まる先は消失点とn点透視 で見ていきます。