ホームに戻る
 自由軸回転 (行列変換、マウス、キー、クォータニオン)

図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 |

となる。以上。

inserted by FC2 system