- クオータニオンを使う -
3Dアニメーションには欠かせないクォータニオンを使いましょう、というコーナーです。
ここでは「使う」ことを中心としますのでクォータニオンについての詳細は詳しいHPや書籍などを参照してください。
また、四則演算はC言語での表記になってますのでご注意を。


・クォータニオンとは?
(t; x, y, z)のように表します。tは実部(実数)、そのほかは虚部(虚数)といいます。
これがクォータニオンと呼ばれるものです。

また、3次元空間の座標をクォータニオンで表すと、

(0; x, y, z)

となります。


・クォータニオンの掛け算
以下の2つのクォータニオンの掛け算をします。

Q1 = (t1; x1, y1, z1) = (t1; V1)
Q2 = (t2; x2, y2, z2) = (t2; V2)

虚部はベクトルとして考えます。このクォータニオン掛け算は、

Q1 * Q2 = (t1t2 - V1・V2; t1V2 + t2V1 + V1×V2)

となるみたいです。(「・」は内積、「×」は外積)
また、基本的には「Q1Q2≠Q2Q1」のようですので掛ける方向に注意してください。


・クォータニオンを使った回転
クォータニオンの掛け算さえできれば回転は簡単にできます。

回転させたい点を、

Q = (0; qx, qy, qz)

とおきます。

次に回転軸の方向を決めるベクトルを

Vr = (xr, yr, zr)    (|Vr| = 1)

とします。

Qを回転させるクォータニオンを作成します。θは回転角度です。

A = (cos(θ/2); xr * sin(θ/2), yr * sin(θ/2), zr * sin(θ/2))
B = (cos(θ/2); -xr * sin(θ/2), -yr * sin(θ/2), -zr * sin(θ/2))

この2つのクォータニオンを作成したら、

B * Q * A = (0; x, y, z)

という計算をします。
実部を抜いた(x, y, z)がVrを軸としてQをθ度回転した点になります。



以下はクォータニオンで回転をさせる関数のサンプルです。
筆者はOpenGL+WGLで使ってまして、C言語チックなC++言語で書いてます。


・クォータニオンを使って点の回転をするサンプルソース

#define PI 3.141592653589793

//Vector構造体
struct Vector
{
float x;
float y;
float z;
};

//Quaternion構造体
struct Quaternion
{
float t;
float x;
float y;
float z;
};


//クォータニオン(quaternion)の掛け算a*b
Quaternion MultiplyQuaternion(Quaternion a, Quaternion b)
{
Quaternion ans; //クォータニオンの掛け算の結果

ans.t = a.t * b.t - a.x * b.x - a.y * b.y - a.z * b.z;
ans.x = a.t * b.x + b.t * a.x + a.y * b.z - b.y * a.z;
ans.y = a.t * b.y + b.t * a.y + a.z * b.x - b.z * a.x;
ans.z = a.t * b.z + b.t * a.z + a.x * b.y - b.x * a.y;

return ans;
}

//axisVectorを軸にしてpointをangle度回転させる
Vector RotateByQuaternion(Vector point, Vector axisVector, int angle)
{
Quaternion calcA, calcB; //軸ベクトルから計算されるクォータニオン
Quaternion calcP; //pointをクォータニオンに置いたもの
Quaternion quaAns; //回転後のクォータニオン

Vector normalize_v; //軸になるベクトルaxisVectorを正規化したもの
Vector vecAns; //quaAnsからtを抜いたもの(回転された点の座標)

//ベクトルの正規化(ソースは省略)
normalize_v = NormalizeVector(axisVector);
if (/*…正規化できなかったら…*/)
{
//できなかったときの処理
}

//軸ベクトルからクォータニオンを生成
calcB.t = cos( (angle * PI) / (180 * 2) );
calcB.x = -normalize_v.x * sin( (angle * PI) / (180 * 2) );
calcB.y = -normalize_v.y * sin( (angle * PI) / (180 * 2) );
calcB.z = -normalize_v.z * sin( (angle * PI) / (180 * 2) ); //★

calcA.t = cos( (angle * PI) / (180 * 2) );
calcA.x = normalize_v.x * sin( (angle * PI) / (180 * 2) );
calcA.y = normalize_v.y * sin( (angle * PI) / (180 * 2) );
calcA.z = normalize_v.z * sin( (angle * PI) / (180 * 2) );

//回転させたい座標を以下のクォータニオンと置く
calcP.t = 0;
calcP.x = point.x;
calcP.y = point.y;
calcP.z = point.z;

//クォータニオンの掛け算R*P*Q
quaAns = MultiplyQuaternion(calcB, calcP);
quaAns = MultiplyQuaternion(quaAns, calcA);

//クォータニオンの実部を抜いたものが回転された点となる
vecAns.x = quaAns.x;
vecAns.y = quaAns.y;
vecAns.z = quaAns.z;

return vecAns;

}

ベクトルの正規化の関数は省略。

MultiplyQuaternion()がごちゃごちゃしていますが、これは先ほど出てきた
Q1 * Q2 = (t1t2 - V1・V2; t1V2 + t2V1 + V1×V2)
を展開しているだけです。
実際に計算してみるとソースのような式が得られます。

また、上記の処理をあるオブジェクト(OBJファイル等で書かれたもの)のすべての頂点に適用すれば、
オブジェクト自体を回転させる事ができます。


inserted by FC2 system