kumak1’s blog

kumak1のイラストや技術ログ

unity shader でクォータニオンから回転行列を求める

前回に引き続き shader のベクトル演算をメモ。 shader 上で様々な処理を書いていると、 「クォータニオン(回転軸ベクトルと回転角)は容易に想像・算出できる・・が、任意のベクトルに適用・回転させるにはどうすれば・・」 という場面に出会すことが多くある。少なくとも、高校で数ⅡBまでしか履修しなかった私には頻繁にある。。 そんな初級者な私でもよく見るベクトルの回転は座標系変換だ。これならなんとなくわかる。

mul(UNITY_MATRIX_M, vector);

ベクトル vector に回転行列 UNITY_MATRIX_M を累乗計算 mul() してやると、回転後のベクトルが取得できる。シンプル、素敵。 だったらば、クォータ二オンから回転行列を簡単求めるられるようになったらサイキョーね。ということで下記関数ができました。

float3x3 QuaternionToMatrix(float3 axis, float thita) {
    float3 a = normalize(axis);
    thita *= 3.14159265;
    float s = sin(thita);
    float c = cos(thita);
    float r = 1.0 - c;
    return float3x3(
        a.x * a.x * r + c, a.y * a.x * r + a.z * s, a.z * a.x * r - a.y * s,
        a.x * a.y * r - a.z * s, a.y * a.y * r + c, a.z * a.y * r + a.x * s,
        a.x * a.z * r + a.y * s, a.y * a.z * r - a.x * s, a.z * a.z * r + c
    );
}

参考にしたのはみんな大好き wgld 。何度読み返しても「ここはこういうことか〜」という発見ができるので素敵サイト

wgld.org

利用例

カメラや照明のチルト(XZ軸回転)を表現する際は下記で求められる。 今向いている方向を movableVector と、向いている方向・向かせたい方向と同じ回転軸上にあるとわかっている方向(チルトだったらY軸) baseVector がわかっていれば、 外積 cross() で回転軸を求め、内積 dot() で基準からの角度がわかり、さらに回転させたい角度を加えてやれば良いのだ。

float3 VectorTilt(float3 baseVector, float3 movableVector, float thita) {
    return mul(
        QuaternionToMatrix(
            cross(baseVector, movableVector),
            (dot(baseVector, movableVector) * 0.01) + thita
        ),
        movableVector
    ).xyz;
}

余談

XYZの各軸単体で回転させるなら回転基準となるベクトルは不要で、回転行列だけで以下のように表現・利用できる。 PostProcess だとか、ちょっとした利用には大変便利。

float3x3 MatrixRotateX(float thita) {
    float s = sin(thita);
    float c = cos(thita);
    return float3x3(
        1, 0, 0,
        0, c, -s, 
        0, s, c
    );
}

float3x3 MatrixRotateY(float thita) {
    float s = sin(thita);
    float c = cos(thita);
    return float3x3(
        c, 0, s,
        0, 1, 0, 
        -s, 0, c
    );
}

float3x3 MatrixRotateZ(float thita) {
    float s = sin(thita);
    float c = cos(thita);
    return float3x3(
        c, -s, 0,
        s, c, 0,
        0, 0, 1
    );
}