ホームに戻る
自由軸回転 (行列変換、マウス、キー、クォータニオン)
図12_00:マウスを使って回転させることができる立方体
ソースコード:glut12_rotate.c
解説
行列
行列に単位行列をセット
glLoadIdentity();
行列のモードを選ぶ
glMatrixMode を使う。
第1引数は以下より選ぶ。
GL_MODELVIEW:視野変換
GL_PROJECTION:透視変換
GL_TEXTURE:テクスチャ変換
現在の行列への書きこみ
glLoadMatrixd か glLoadMatrixf を用いる。
第1引数に読み込む配列の先頭ポインタを入れる。
要素数は 4*4 行列なので16個必要。
現在の行列の取得
glGetDoublev で 第1引数を GL_MODELVIEW_MATRIX とする。
第2引数は GLdouble 型で16要素の配列のポインタの先頭。
第2引数の配列に取得した行列の数値が得られる。
ほかに第1引数は以下のものが使える。
GL_PROJECTION_MATRIX:透視変換
GL_TEXTURE_MATRIX:テクスチャ変換
現在の変換行列への掛け算
glMultMatrixd か glMultMatrixf を用いる。
第1引数に掛ける配列の先頭ポインタを入れる。
要素数は 4*4 行列なので16個必要。
現在の行列をスタックへ PUSH する
glPushMatrix();
現在の行列をスタックから POP する
glPopMatrix();
マウス
glutMouseFunc の第1引数で関数を登録
関数は4つの引数を持つ。
第1引数はボタンの種類。
第2引数はボタンの状態。
第3引数はマウス位置のX座標。
第4引数はマウス位置のY座標。
第1引数
GLUT_LEFT_BUTTON:左ボタン
GLUT_MIDDLE_BUTTON:中央ボタン
GLUT_RIGHT_BUTTON:右ボタン
第2引数
GLUT_UP:ボタンが上げられた
GLUT_DOWN:ボタンが押された
glutMotionFunc ではマウスの移動に対する関数を登録
関数は2つの引数を持つ。
第1引数はマウス位置のX座標。
第2引数はマウス位置のY座標。
キー
glutKeyboardFunc でキー入力に関する関数を登録
第1引数はキーのアスキーコード値。
第2引数はマウス位置のX座標。
第3引数はマウス位置のY座標。
glutSpecialFunc で特殊キー入力に関する関数を登録
第1引数は下のキーコード値。
第2引数はマウス位置のX座標。
第3引数はマウス位置のY座標。
GLUT_KEY_F1
GLUT_KEY_F2
GLUT_KEY_F3
GLUT_KEY_F4
GLUT_KEY_F5
GLUT_KEY_F6
GLUT_KEY_F7
GLUT_KEY_F8
GLUT_KEY_F9
GLUT_KEY_F10
GLUT_KEY_F11
GLUT_KEY_F12
GLUT_KEY_LEFT
GLUT_KEY_UP
GLUT_KEY_RIGHT
GLUT_KEY_DOWN
GLUT_KEY_PAGE_UP
GLUT_KEY_PAGE_DOWN
GLUT_KEY_HOME
GLUT_KEY_END
GLUT_KEY_INSERT
何もしていない時に呼び出される関数
glutIdleFunc で第1引数に関数のポインタを登録
呼び出される関数には引数は無い。
関数の登録は全て NULL を登録することで無効となる。
再描画を促すとき
glutPostRedisplay();
クォータニオン
OpenGL はクォータニオンの計算をサポートしない。
実際は自力で計算し、変換行列に掛けることをする。
クォータニオンの表記
a + bi + cj + dk
クォータニオンの計算法則
i*i = j*j = k*k = i*j*k = -1
i*j = -j*i = k
j*k = -k*j = i
k*i = -i*k = j
四則演算
2つのクォータニオン q1 と q2 について
q1 = a1 + b1i + c1j + d1k
q2 = a2 + b2i + c2j + d2k
足し算
q1 + q2 = (a1+a2) + (b1+b2)i + (c1+c2)j + (d1+d2)k
引き算
q1 - q2 = (a1-a2) + (b1-b2)i + (c1-c2)j + (d1-d2)k
掛け算
q1q2 = (a1 + b1i + c1j + d1k)(a2 + b2i + c2j + d2k)
= a1a2 + a1b2i + a1c2j + a1d2k + b1a2i + b1b2ii + b1c2ij + b1d2ik
+ c1a2j + c1b2ji + c1c2jj + c1d2jk + d1a2k + d1b2ki + d1c2kj + d1d2kk
= (a1a2 - b1b2 - c1c2 - d1d2) + (a1b2 + a2b1 + c1d2 - d1c2)i
+ (a1c2 + a2c1 + d1b2 - b1d2)j + (a1d2 + a2d1 + b1c2 - c1b2)k
クォータニオンの別表記
a は実数部分、bi + cj + dk の <b, c, d> をベクトル部分とします。
q = a + bi + cj + dk
V = <b, c, d>
q = (a, V)
別表記での四則演算
( ・ は内積、× は外積の計算)
q1 + q2 = (a1 + a2, V1 + V2)
q1 - q2 = (a1 - a2, V1 - V2)
q1q2 = (a1a2 - V1・V2, a1V2 + a2V1 + V1×V2)
単位クォータニオン
(左右のどちらから掛けても元の値に変化を与えない)
(1, <0, 0, 0>)
逆クォータニオン
(a, V)(a, -V) = (a^2 + V・V, -aV + aV + V×(-V))
= a^2 + V・V
両辺を実数 (a^2 + V・V) で割る
(a, V)(a, -V)/(a^2 + V・V) = (1, <0,0,0>)
よって逆クォータニオンは以下のように定義できる。
(a, V)^-1 = (a, -V)/(a^2 + V・V)
= (a/(a^2 + V・V), -V/(a^2 + V・V))
クォータニオンの長さ
(sqrt は平方根)
sqrt(a^2 + V・V)
クォータニオンを使った回転の公式
(V はベクトル、q はクォータニオン(a, V) q'はクォータニオン(a, -V))
Vrot = qVq'
クォータニオンを使った回転の原理の説明
(V0 と V1 という間がθの2つの単位ベクトルがあり q = V0V1' と定義)
N(V0) = N(V1) = 1
q = V1V0' = ( V0・V1 , V0×V1 )
V0・V1 = |V0||V1|cosθ = cosθ
V0×V1 = |V0||V1|sinθ*Vaxis = sinθ*Vaxis
q = V1V0' = (cosθ, sinθVaxis)
ここで Vaxis は V0 と V1 に垂直な単位ベクトル
VrotV1' = (qV0q')V1'
= (qV0(V1V0')')V1'
= qV0(V0V1')V1'
= q(V0V0)(V1'V1')
= q(-1)(-1)
= q
= V1V0'
このとき 「V0 から見た V1」 と 「V1 から見た Vrot」は同じ
よって、V0 と Vrot は 2θ の角をなすといえる。
よってベクトル V を軸にφだけ回転させるクォータニオンは以下
q(φ) = (cos(φ/2) , Vsin(φ/2))
ベクトルに左と右からクォータニオンを掛ける行列
(q = w + xi + yj + zk とする)
| w -z y x |
Lmat(q) = | z w -x y |
| -y x w z |
| -x -y -z w |
| w z -y x |
Rmat(q) = | -z w x y |
| y -x w z |
| -x -y -z w |
よって
Vrot = qVq'
= q(Vq')
= q(VRmat(q'))
= (VRmat(q'))Lmat(q)
= V(Rmat(q')Lmat(q))
Rmat(q')Lmat(q)の行列は、
| ww+xx-yy-zz 2xy-2wz 2xz+2wy 0 |
Rmat(q')Lmat(q) = | 2xy+2wz ww-xx+yy-zz 2yz-2wx 0 |
| 2xz-2wy 2yz+2wx ww-xx-yy+zz 0 |
| 0 0 0 ww+xx+yy+zz |
クォータニオンの長さが 1 のとき、
| 1-2(yy+zz) 2(xy-wz) 2(xz+wy) 0 |
Qmat(q) = | 2(xy+wz) 1-2(xx+zz) 2(yz-wx) 0 |
| 2(xz-wy) 2(yz+wx) 1-2(xx+yy) 0 |
| 0 0 0 1 |
簡潔に計算手順を書くと、
あるベクトル V1 を 単位ベクトル V を中心にθだけ回転する場合、
w = cos(θ/2)
x = Vx*sin(θ/2)
y = Vy*sin(θ/2)
z = Vz*sin(θ/2)
以上を求め以下の行列を作成し V1 に右から掛け合わせる
| ww+xx-yy-zz 2xy-2wz 2xz+2wy 0 |
| 2xy+2wz ww-xx+yy-zz 2yz-2wx 0 |
| 2xz-2wy 2yz+2wx ww-xx-yy+zz 0 |
| 0 0 0 ww+xx+yy+zz |
となる。以上。