回転行列からオイラー角を求める

NやUpベクトルからなる回転行列からオイラー角を求める方法。

Houdiniのカメラにオイラー角の入力ボックスしか見当たらなかったので計算することに。

回転行列を掛け合わせた行列をつくる

XYZ軸すべての回転行列を掛けた結果の行列をつくる。Z回転、X回転、Y回転の順で掛けていきます。この順番をしっかり決めておくことが大事で、順番が変わると結果も変わる。

// Z軸でz(ラジアン)回転させる行列
Rz = {cos(z),  sin(z), 0,
      -sin(z), cos(z), 0,
      0,       0,      1}

この行列にX軸の回転行列を掛ける。

// X軸方向にx(ラジアン)回転させる行列
Rx = {1, 0,       0,
      0, cos(x),  sin(x),
      0, -sin(x), cos(x)}

結果こうなる

RzRx = {cos(z),  sin(z)*cos(x), sin(z)*sin(x),
        -sin(z), cos(z)*cos(x), cos(z)*sin(x),
        0,       -sin(x),       cos(x)}

さらにY軸方向の回転行列を掛ける。

// Y軸方向にy(ラジアン)回転させる行列
Ry = {cos(y), 0, -sin(y),
      0,      1, 0,
      sin(y), 0, cos(y)}

結果は以下

RzRxRy = {cos(z)*cos(y)+sin(z)*sin(x)*sin(y),  sin(z)*cos(x), -cos(z)sin(y)+sin(z)*sin(x)*cos(y),
          -sin(z)*cos(y)+cos(z)*sin(x)*sin(y), cos(z)*cos(x), sin(z)*sin(y)+cos(z)*sin(x)*cos(y),
          cos(x)*sin(y),                       -sin(x),       cos(x)*cos(y)}

この式に角度x,y,zを当てはめれば行列(各軸のベクトル)ができる。

ベクトルから行列をつくる

次にベクトルから行列をつくる。Houdiniでは基本的にZ軸は@N、Y軸は@upなので

// @upと@Nの外積からx軸のベクトルをつくる
vector L = cross(@up, @N)
 
RzRxRy = {L.x,  L.y,  L.z,
          up.x, up.y, up.z,
          N.x,  N.y,  N.z}

となる。このベクトルで構成した行列とZXY回転をした行列が同じ結果になるための角度x, y, zは何かを考える。

L.x = cos(z)*cos(y)+sin(z)*sin(x)*sin(y)
L.y = sin(z)*cos(x)
L.z = -cos(z)sin(y)+sin(z)*sin(x)*cos(y)
 
up.x = -sin(z)*cos(y)+cos(z)*sin(x)*sin(y)
up.y = cos(z)*cos(x)
up.z = sin(z)*sin(y)+cos(z)*sin(x)*cos(y)
 
N.x = cos(x)*sin(y)
N.y = -sin(x)
N.z = cos(x)*cos(y)

中身を比較しながら方程式を解く要領で片方の変数を求めていく。

角度x

N.yが-sin(x)に当たるので
N.y = -sin(x)
という等式が成り立つので
x = -asin(N.y)
となる。

角度z

zはL.yをup.yで割ることで求めることができる。
L.y / up.y = sin(z)*cos(x) / cos(z)*cos(x) = sin(z)/cos(z) = tan(z)

z = atan(L.y, up.y)
となる。

角度y

yも同じ要領でNxをNzで割ることで求まる。
Nx / Nz = cos(x)*sin(y) / cos(x)*cos(y) = sin(y) / cos(y) = tan(y)
y = atan(Nx, Nz)

モデルを回転して確認

// 行列を構成する3つの軸ベクトルをつくる
@N = normalize(set(1, 0, 1));
@up = normalize(set(-1, 1, 0));
@left = cross(@up, @N);
@up = cross(@N, @left); // 90度で交わるようにupベクトルを補正する
 
// オイラー角を計算する
v@rot.x = degrees(-asin(@N.y));
v@rot.y = degrees(atan2(@N.x, @N.z));
v@rot.z = degrees(atan2(v@left.y, @up.y));
 
printf('@rot:' + sprintf('%g', @rot) + '\n');
//@rot:{-0,45,35.2644}

適当にZ軸ベクトル(1, 0, 1)、Y軸ベクトル(-1, 1, 0)をもとに作った軸ベクトルから計算すると、角度はX軸が0度、Y軸が45度、Z軸が35.26度になりました。

Transform SOPに角度を入力したところ一致した。

Transform OrderはRzRxRyの順にすることを忘れずに。

カメラに角度を割り当てる

同じようにWrangleでv@rotという角度のアトリビュートをつくり

これをカメラのRotateにリンクさせます。カメラの正面の向きがZマイナス方向なので、@Nはカメラに値を渡すときにー1を掛けて反転させておきましょう。
Translateにも座標をリンクさせればカメラが移動してくれます。

タイトルとURLをコピーしました