第 7 章 座標変換
7.7 ビュー変換
7.7.1 視点の位置の移動
(58)
図 91 ビュー変換 l ビュー変換行列を作成するメソッドの追加 (Matrix.h)
視点の位置、目標点の位置、および視野の上方向のベクトルを指定してビュー変換行列を作成 するメソッド lookat() を Matrix クラスに追加します。視点の位置は e = (ex, ey, ez)、視線上にあ る目標点の位置は g = (gx, gy, gz)、視野の上方向のベクトルを u = (ux, uy, uz) とします。上方向を y 軸方向とするなら、これに (0, 1, 0) を設定します。これもインスタンスを生成せずに呼び出せ
るように static メソッドにします。
図 92 視点の設定
// ビュー変換行列を作成する
static Matrix lookat(
GLfloat ex, GLfloat ey, GLfloat ez, // 視点の位置 GLfloat gx, GLfloat gy, GLfloat gz, // 目標点の位置 GLfloat ux, GLfloat uy, GLfloat uz) // 上方向のベクトル {
まず、視点 e = (ex, ey, ez) を原点 O = (0, 0, 0) に平行移動する変換行列 Tv を作ります。
(59)
// 平行移動の変換行列
const Matrix tv(translate(-ex, -ey, -ez));
O x
y
z
-z
O x
y
z
O x
y
z u= (ux,uy,uz)
g= (gx,gy,gz) 上方向のベクトル
目標点の位置 e視点の位置= (ex,ey,ez)
Tv = 0 BB
@
1 0 0 ex
0 1 0 ey
0 0 1 ez
0 0 0 1 1 CC A
視点の位置 e を原点に移すと、目標点の位置は目標点の位置は g – e になります。これと上方 向のベクトル u から、視点を原点とした座標系である視点座標系の軸ベクトルを求めます。これ を (r s t) とします。
図 93 視点座標系の軸ベクトル (r s t)
視点座標系における z 軸に相当するベクトル t = (tx, ty, tz) は、視点方向の逆ベクトル e - g に なります。視点座標系の x 軸に相当するベクトル r = (rx, ry, rz) は、上方向のベクトル u と t と の外積 u × t により求めます。視点の座標系の y 軸に相当するベクトル s = (sx, sy, sz) を、ベク トル t とベクトル r の外積 t × r で求めます。
(60)
// t 軸 = e - g
const GLfloat tx(ex - gx);
const GLfloat ty(ey - gy);
const GLfloat tz(ez - gz);
// r 軸 = u x t 軸
const GLfloat rx(uy * tz - uz * ty);
const GLfloat ry(uz * tx - ux * tz);
const GLfloat rz(ux * ty - uy * tx);
// s 軸 = t 軸 x r 軸
const GLfloat sx(ty * rz - tz * ry);
const GLfloat sy(tz * rx - tx * rz);
const GLfloat sz(tx * ry - ty * rx);
得られた視点座標系の軸ベクトル (r s t) をそれぞれ正規化し、式 (54) にもとづいて式 (61)
O x
y
z t
g e
t = e − g r = u × t
O x
y
z
u t r
s = t × r
O x
y
z t s
r
t=e g= 0
@ ex gx
ey gy
ez gz
1 A=
0
@ tx
ty
tz
1 A
r=u⇥t= 0
@ uytz uzty
uztx uxtz
uxty uytx
1 A=
0
@ rx
ry
rz
1 A
s=t⇥r= 0
@ tyrz tzry
tzrx txrz
txry tyrx
1 A=
0
@ sx
sy
sz
1 A
に示す回転の変換行列 Rv を作ります。
(61)
まず、ベクトル sの長さを調べます。これが 0 でなければ、ベクトル r とベクトル t の長さ は、ともに 0 ではありません。このとき、これらを正規化して、4 × 4 = 16 要素の配列変数 rv の 左上の 3 × 3 の要素に格納します。
// s 軸の長さのチェック
const GLfloat s2(sx * sx + sy * sy + sz * sz);
if (s2 == 0.0f) return tv;
// 回転の変換行列 Matrix rv;
rv.loadIdentity();
// r 軸を正規化して配列変数に格納
const GLfloat r(sqrt(rx * rx + ry * ry + rz * rz));
rv.matrix[ 0] = rx / r;
rv.matrix[ 4] = ry / r;
rv.matrix[ 8] = rz / r;
// s 軸を正規化して配列変数に格納 const GLfloat s(sqrt(s2));
rv.matrix[ 1] = sx / s;
rv.matrix[ 5] = sy / s;
rv.matrix[ 9] = sz / s;
// t 軸を正規化して配列変数に格納
const GLfloat t(sqrt(tx * tx + ty * ty + tz * tz));
rv.matrix[ 2] = tx / t;
rv.matrix[ 6] = ty / t;
rv.matrix[10] = tz / t;
平行移動の変換行列 Tv に回転の変換行列 Rv を乗じて、ビュー変換行列を求めます。
(62)
// 視点の平行移動の変換行列に視線の回転の変換行列を乗じる
return rv * tv;
}
Rv = 0 BB BB BB BB B@
rT
|r| 0
sT
|s| 0
tT
|t| 0
0 0 0 1 1 CC CC CC CC CA
= 0 BB BB BB BB B@
rx
|r| ry
|r| rz
|r| 0 sx
|s| sy
|s| sz
|s| 0 tx
|t|
ty
|t|
tz
|t| 0
0 0 0 1
1 CC CC CC CC CA
Mv =RvTv
図 94 回転の変換適用後の視点座標系の座標軸 l メインプログラム (main.cpp) の変更点
lookat() メソッドを使ってビュー変換行列を求め、Matrix 型の変数 view に格納します。これ
をモデル変換 model に乗じて合成し、Matrix 型の変数 modelview に格納します。この二つの変 換はまとめて実行されることが多く、モデルビュー変換と呼ばれます。そこでシェーダの uniform 変数の変数名も、model から modelview に変更しておきます。
int main() {
《省略》
// プログラムオブジェクトを作成する
const GLuint program(loadProgram("point.vert", "point.frag"));
// uniform 変数の場所を取得する
const GLint modelviewLoc(glGetUniformLocation(program, "modelview"));
// 図形データを作成する
std::unique_ptr<const Shape> shape(new Shape(2, 4, rectangleVertex));
// ウィンドウが開いている間繰り返す
while (window.shouldClose() const == GL_FALSE) {
《省略》
// モデル変換行列を求める
const Matrix model(translation * scaling);
// ビュー変換行列を求める
const Matrix view(Matrix::lookat(0.0f, 0.0f, 0.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, 0.0f));
// モデルビュー変換行列を求める
const Matrix modelview(view * model);
// uniform 変数に値を設定する
glUniformMatrix4fv(modelviewLoc, 1, GL_FALSE, modelview.data());
// 図形を描画する shape->draw();
(0, 0, 1) (1, 0, 0) (0, 1, 0)
O x
y
z
z
// カラーバッファを入れ替えてイベントを取り出す window.swapBuffers();
} }
この例では視点の位置は原点 (0, 0, 0) とし、そこから (-1, -1, -1) にある目標点を見ています。
視点を原点に置いているのは、この段階で視点の位置を変更すると、図形が視体積の外に出てし まう可能性があるからです。視点を任意の位置に配置するには、投影変換を行う必要があります。
l バーテックスシェーダ (point.vert の変更点
バーテックスシェーダの uniform 変数 model を modelview に変更します。
#version 150 core uniform mat4 modelview;
in vec4 position;
void main() {
gl_Position = modelview * position;
}
n サンプルプログラム step14
l 実行結果
図 95 ビュー変換を実装した実行結果
ウィンドウ上でマウスをドラッグすると、図形がこの空間の xy 平面上を移動します。