使用GLM的基于四元数的点旋转 [英] Quaternion based point rotations using GLM

查看:250
本文介绍了使用GLM的基于四元数的点旋转的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试使用GLM中实现的四元数来旋转点.最终目标是使用此代码创建轨道摄像机,但这是帮助您理解代码背后动机的旁注.

I am trying to rotate a point using quaternions as implemented in GLM. The end goal is to use this code to create an orbital camera but this is a side note to help understand the motivation behind the code.

为了更好地理解基于四元数的旋转,我编写了一些包含两个循环的代码.第一个循环将四元数围绕X轴逐步旋转到90度,从而逐步改变四元数的方向,第二个循环将继续对Z四轴逐步进行四度旋转到90度.循环每个执行4个步骤.因此,每个回路围绕其各自的轴增量旋转90/4 = 22.5度.使用四元数乘法应用方向变化,并使用欧拉角进行跟踪.循环应以四元数结尾,该四元数将使点从(0,0,3)旋转到(3,0,0).请注意,我不仅在尝试确定将进行此轮换的四元数.目标是执行一系列增量旋转.

To understand quaternion based rotations better I have written a bit of code that contains two loops. The first loop will incrementally change the orientation of the quaternion by rotating it in steps around the X axis all the way to 90 degrees, and the second loop will continue to apply a rotation all the way to 90 degrees in steps around the Z axis. The loops do 4 steps each. So each loop incrementally rotates for 90 / 4 = 22.5 degrees around their respective axis. The change of orientation is applied using quaternion multiplication and tracked using Euler angles. The loops should end with a quaternion that will rotate a point at (0, 0, 3) to (3, 0, 0). Please note, I am not just trying to determine the quaternion that will do this rotation. The goal is to perform a series of incremental rotations.

如果我们看下面的图片,从C到I的转换发生在第一个循环中,然后在第二个循环中从I转换为R(原为稀疏点命名).

If we look at the below picture the transition from C to I happens in the first loop and then the transition from I to R in the second (pardon the sparse point naming).

点的旋转定义为(请参见此处此处):

Rotation of a point is defined as (see here and here):

v' = q * v * q^-1

其中v应该被视为纯四元数(标量项w为零),而q必须是单位四元数(长度为1).根据我的理解,需要将四元数的倒数与右手相乘,以将生成的v'保留在3D空间中,而不会以4D向量结尾.因此v'也必须是纯四元数.

where v should be considered a pure quaternion (with a zero scalar term w) and q needs to be a unit quaternion (of length 1). And from what I understand the right hand multiplication with the inverse of the quaternion is needed to keep the resulting v' in 3D space and not end up with a 4D vector. So v' needs to be a pure quaternion as well.

然后有旋转的倍增效果,其中左手乘以q贡献了所需旋转的一半,而右手乘以反函数相加了所需旋转的另一半.

Then there is the doubling effect of the rotation where the left hand multiplication with q contributes half of the desired rotation and the right hand side multiplication with the inverse adds another half of the desired rotation.

Ben Eater和Grant Sanderson对四元数进行了出色的交互式可视化和解释,我将其用作交叉引用.可以在此处找到.

There is an excellent interactive visualisation and explanation of quaternions by Ben Eater and Grant Sanderson, that I used as a cross-reference. It can be found here.

因此,我们首先需要使用围绕X轴旋转11.25度的四元数,并且GLM针对Euler角返回此四元数(使用四元数表示法[w,[x,y,z]]):

So we first need to use a quaternion that rotates by 11.25 degrees around the X axis and GLM returns this quaternion for Euler angles (quaternion notation [w, [x, y, z]] is used):

Rotation of [ 11.25, 0.00,  0.00] deg => Q: [ 0.9952, [ 0.0980,  0.0000,  0.0000]]

根据,并且由于我们仅绕X轴旋转,我们可以通过对四元数的w分量执行acos来验证GLM计算的四元数中的旋转量:

According to this, and since we are rotating purely around the X axis, we could verify the rotation amount in the GLM calculated quaternion by performing an acos on the w component of the quaternion:

float angle = acosf(q.w)

然后:

acos(0.9952) = 0.0980 rad / 5.6 degrees

哪个是所需角度的一半...而且还通过交互动画的交叉检查(请舍入)得到了确认:

Which is half the desired angle... And this is also confirmed in a cross check with the interactive animation (pardon the rounding):

因此,GLM返回的四元数为11.25度实际上旋转了所需角度的一半...如果我们看一下GLM代码,则根据欧拉角计算w分量会稍微复杂一点,因为旋转可以围绕任意角度发生旋转轴...但是,欧拉角有明显的二分之一:

So the quaternion returned by GLM for 11.25 degrees actually rotates for half the desired angle... If we look at the GLM code the calculation of the w component from Euler angles is a little more complex because rotation can happen around an arbitrary axis of rotation... But there is a distinct halving of the Euler angles:

template <typename T, precision P>
GLM_FUNC_QUALIFIER tquat<T, P>::tquat(tvec3<T, P> const & eulerAngle)
{
    tvec3<T, P> c = glm::cos(eulerAngle * T(0.5));
    tvec3<T, P> s = glm::sin(eulerAngle * T(0.5));
    
    this->w = c.x * c.y * c.z + s.x * s.y * s.z;
    this->x = s.x * c.y * c.z - c.x * s.y * s.z;
    this->y = c.x * s.y * c.z + s.x * c.y * s.z;
    this->z = c.x * c.y * s.z - s.x * s.y * c.z;
}

我的第一个问题是,GLM为什么将角度减半?

尽管期望的旋转角度有所不同,但我还是继续检查了两个循环的旋转结果.结果是...出乎意料.

Despite the difference in the desired rotation angle I went ahead to check the rotation results with the two loops. And the results were... Unexpected.

如果我使用了不正确的格式",旋转(由某些OpenGL在线教程建议),并仅通过左手乘法旋转该点(但整步为22.5度):

If I used the "incorrect form" of rotation (suggested by some OpenGL online tutorials) and rotated the point by a left hand multiplication only (but for a full step of 22.5 degrees):

v' = q * v

我得到了想要的结果.该点正确地遵循了所有中间步骤,并且从(0,0,3)变为(3,0,0).此外,在所有中间步骤中,w分量均为0.

I got the result I was hoping for. The point was following all the intermediate steps correctly and went from (0, 0, 3) to (3, 0, 0). Also the w component was 0 at all intermediate steps.

但是,如果我使用正确形式",旋转并通过左手乘以q和右手乘以q的倒数来旋转点(对于11.25度的半步,以考虑旋转的倍数):

But if I used the "correct form" of rotation and rotated the point by a left hand multiplication with q and right hand multiplication with the inverse of q (for a half step of 11.25 degrees to account for the doubling of rotation):

v' = q * v * q^-1

第二个循环开始绕Z轴旋转点时,我开始得到错误的结果.一个小的但截然不同的Z分量开始蠕变,并且旋转距离22.5度的整个步幅不足.在下图中的绿点中可见.

I start getting wrong results as soon as the second loop starts to rotate the point around the Z axis. A small but distinct Z component starts to creep in and the rotation is just short of the full step of 22.5 degrees. This is visible in the green dots in the image below.

两种旋转方式的旋转点的w分量都保持为0 ...

The w component of the rotated point remains 0 for both methods of rotation...

谁能解释为什么GLM旋转通过左侧的一个乘法就能正常工作?

这是否是某种优化,可以将操作次数减少到最少?

Is this some kind of optimisation to reduce the number of operations to a minimum?

我可以在GLM中使用v' = q * v旋转来获得所有旋转的一致且正确的结果吗?

Can I use the v' = q * v rotation in GLM to get consistent and correct results for all rotations?

代码:

const int rotSteps = 4;
// Rotate around X axis in steps to 90deg
vec3 eulerState = vec3(0.0f);
// point we want to rotate (use vec4 to track the w component during rotations)
vec4 v = vec4(0.0f, 0.0f, 3.0f, 0.0f);

// Full Euler steps for q * v rotation
quat orientF   = quat(1.0f, 0.0f, 0.0f, 0.0f);
vec3 euler     = vec3(RAD(90.0f), RAD(0.0f), RAD(0.0f));
vec3 eulerStep = euler / (float)rotSteps;
quat qEulerF   = quat(eulerStep); // GetRotQuat(eulerStep);

vec4 qa          = ToAngularForm(qEulerF);
vec3 orientEuler = eulerAngles(qEulerF);
CLogD(TAG, "Rot Full Step    Q [W, X, Y, Z]: " FMT_Q(4)  " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerF), PAR_V3(degrees(orientEuler)), PAR_QA(qa));

// Half Euler steps for q * v * q^-1 rotation
quat orientH    = quat(1.0f, 0.0f, 0.0f, 0.0f);
vec3 eulerStepH = eulerStep / 2.0f;
quat qEulerH    = quat(eulerStepH); // GetRotQuat(eulerStepH);

qa          = ToAngularForm(qEulerH);
orientEuler = eulerAngles(qEulerH);
CLogD(TAG, "Rot Half Step    Q [W, X, Y, Z]: " FMT_Q(4) " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerH), PAR_V3(degrees(orientEuler)), PAR_QA(qa));

quat qEulerHI = inverse(qEulerH);
vec4 qai      = ToAngularForm(qEulerHI);
orientEuler   = eulerAngles(qEulerHI);
CLogD(TAG, "Rot Half Step Q^-1 [W, X, Y, Z]: " FMT_Q(4) " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerHI), PAR_V3(degrees(orientEuler)), PAR_QA(qai));


for (int rotStep = 1; rotStep <= rotSteps; ++rotStep)
{
    // Track the absolute Euler rotation
    eulerState += eulerStep;
    // Rotate by incremental rotation as defined by Euler angles
    orientH = qEulerH * orientH;
    orientEuler = eulerAngles(orientH);
    CLogI(TAG, "Rot Step %d. Curr Abs Q: " FMT_Q(4) "/" FMT_V3(2) "deg, Abs Euler: " FMT_V3(2) "deg",
          rotStep, PAR_Q(orientH), PAR_V3(degrees(orientEuler)), PAR_V3(degrees(eulerState)));

    // Transform the point using the correct q * v * q^-1 rotation and multiply from Left and Right
    quat orientHI = inverse(orientH);
    qa  = ToAngularForm(orientH);
    qai = ToAngularForm(orientHI);

    vec4 rotV = orientH * v * orientHI;
    CLogD(TAG, "Rot      QL: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientH), PAR_QA(qa));
    CLogD(TAG, "Rot      QR: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientHI), PAR_QA(qai));
    CLogD(TAG, "Rot LR   -> " FMT_V4(1), PAR_V4(rotV));

    // Transform the point using the incorrect q * v rotation and multiply from Left only
    orientF = qEulerF * orientF;
    qa      = ToAngularForm(orientF);

    rotV = orientF * v;
    CLogD(TAG, "Rot      QR: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientF), PAR_QA(qa));
    CLogD(TAG, "Rot L    -> " FMT_V4(1), PAR_V4(rotV));
}


// Rotate for 90 degrees around the Z axis
// Full Euler steps for q * v rotation
euler = vec3(RAD(0.0f), RAD(0.0f), RAD(90.0f));
eulerStep = euler / (float)rotSteps;
qEulerF = quat(eulerStep); // GetRotQuat(eulerStep);

qa = ToAngularForm(qEulerF);
orientEuler = eulerAngles(qEulerF);
CLogD(TAG, "Rot Full Step    Q [W, X, Y, Z]: " FMT_Q(4)  " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerF), PAR_V3(degrees(orientEuler)), PAR_QA(qa));

// Half Euler steps for q * v * q^-1 rotation
eulerStepH = eulerStep / 2.0f;
qEulerH = quat(eulerStepH); // GetRotQuat(eulerStepH);

qa = ToAngularForm(qEulerH);
orientEuler = eulerAngles(qEulerH);
CLogD(TAG, "Rot Half Step    Q [W, X, Y, Z]: " FMT_Q(4) " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerH), PAR_V3(degrees(orientEuler)), PAR_QA(qa));

qEulerHI = inverse(qEulerH);
qai = ToAngularForm(qEulerHI);
orientEuler = eulerAngles(qEulerHI);
CLogD(TAG, "Rot Half Step Q^-1 [W, X, Y, Z]: " FMT_Q(4) " / " FMT_V3(2) "deg / " FMT_QA(2), PAR_Q(qEulerHI), PAR_V3(degrees(orientEuler)), PAR_QA(qai));


for (int rotStep = 1; rotStep <= rotSteps; ++rotStep)
{
    // Track the absolute Euler rotation
    eulerState += eulerStep;
    // Rotate by incremental rotation as defined by Euler angles
    orientH = qEulerH * orientH;
    orientEuler = eulerAngles(orientH);
    CLogI(TAG, "Rot Step %d. Curr Abs Q: " FMT_Q(4) "/" FMT_V3(2) "deg, Abs Euler: " FMT_V3(2) "deg",
        rotStep, PAR_Q(orientH), PAR_V3(degrees(orientEuler)), PAR_V3(degrees(eulerState)));

    // Transform the point using the correct q * v * q^-1 rotation and multiply from Left and Right
    quat orientHI = inverse(orientH);
    qa = ToAngularForm(orientH);
    qai = ToAngularForm(orientHI);

    vec4 rotV = orientH * v * orientHI;
    CLogD(TAG, "Rot      QL: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientH), PAR_QA(qa));
    CLogD(TAG, "Rot      QR: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientHI), PAR_QA(qai));
    CLogD(TAG, "Rot LR   -> " FMT_V4(1), PAR_V4(rotV));

    // Transform the point using the incorrect q * v rotation and multiply from Left only
    orientF = qEulerF * orientF;
    qa = ToAngularForm(orientF);

    rotV = orientF * v;
    CLogD(TAG, "Rot      QR: " FMT_Q(4) " / " FMT_QA(1), PAR_Q(orientF), PAR_QA(qa));
    CLogD(TAG, "Rot L    -> " FMT_V4(1), PAR_V4(rotV));
}

输出:

Rot Full Step    Q [W, X, Y, Z]: [ 0.9808, [ 0.1951,  0.0000,  0.0000]] / [ 22.50, -0.00,  0.00]deg / cos( 11.25) + sin( 11.25)( 1.00i +  0.00j +  0.00k)
Rot Half Step    Q [W, X, Y, Z]: [ 0.9952, [ 0.0980,  0.0000,  0.0000]] / [ 11.25, -0.00,  0.00]deg / cos( 5.63) + sin( 5.63)( 1.00i +  0.00j +  0.00k)
Rot Half Step Q^-1 [W, X, Y, Z]: [ 0.9952, [-0.0980, -0.0000, -0.0000]] / [-11.25, -0.00,  0.00]deg / cos( 5.63) + sin( 5.63)(-1.00i + -0.00j + -0.00k)
Rot Step 1. Curr Abs Q: [ 0.9952, [ 0.0980,  0.0000,  0.0000]]/[ 11.25, -0.00,  0.00]deg, Abs Euler: [ 22.50,  0.00,  0.00]deg
Rot      QL: [ 0.9952, [ 0.0980,  0.0000,  0.0000]] / cos( 5.6) + sin( 5.6)( 1.0i +  0.0j +  0.0k)
Rot      QR: [ 0.9952, [-0.0980, -0.0000, -0.0000]] / cos( 5.6) + sin( 5.6)(-1.0i + -0.0j + -0.0k)
Rot LR   -> [ 0.0, -1.1,  2.8,  0.0]
Rot      QR: [ 0.9808, [ 0.1951,  0.0000,  0.0000]] / cos( 11.3) + sin( 11.3)( 1.0i +  0.0j +  0.0k)
Rot L    -> [ 0.0, -1.1,  2.8,  0.0]
Rot Step 2. Curr Abs Q: [ 0.9808, [ 0.1951,  0.0000,  0.0000]]/[ 22.50, -0.00,  0.00]deg, Abs Euler: [ 45.00,  0.00,  0.00]deg
Rot      QL: [ 0.9808, [ 0.1951,  0.0000,  0.0000]] / cos( 11.3) + sin( 11.3)( 1.0i +  0.0j +  0.0k)
Rot      QR: [ 0.9808, [-0.1951, -0.0000, -0.0000]] / cos( 11.2) + sin( 11.2)(-1.0i + -0.0j + -0.0k)
Rot LR   -> [ 0.0, -2.1,  2.1,  0.0]
Rot      QR: [ 0.9239, [ 0.3827,  0.0000,  0.0000]] / cos( 22.5) + sin( 22.5)( 1.0i +  0.0j +  0.0k)
Rot L    -> [ 0.0, -2.1,  2.1,  0.0]
Rot Step 3. Curr Abs Q: [ 0.9569, [ 0.2903,  0.0000,  0.0000]]/[ 33.75, -0.00,  0.00]deg, Abs Euler: [ 67.50,  0.00,  0.00]deg
Rot      QL: [ 0.9569, [ 0.2903,  0.0000,  0.0000]] / cos( 16.9) + sin( 16.9)( 1.0i +  0.0j +  0.0k)
Rot      QR: [ 0.9569, [-0.2903, -0.0000, -0.0000]] / cos( 16.9) + sin( 16.9)(-1.0i + -0.0j + -0.0k)
Rot LR   -> [ 0.0, -2.8,  1.1,  0.0]
Rot      QR: [ 0.8315, [ 0.5556,  0.0000,  0.0000]] / cos( 33.8) + sin( 33.8)( 1.0i +  0.0j +  0.0k)
Rot L    -> [ 0.0, -2.8,  1.1,  0.0]
Rot Step 4. Curr Abs Q: [ 0.9239, [ 0.3827,  0.0000,  0.0000]]/[ 45.00, -0.00,  0.00]deg, Abs Euler: [ 90.00,  0.00,  0.00]deg
Rot      QL: [ 0.9239, [ 0.3827,  0.0000,  0.0000]] / cos( 22.5) + sin( 22.5)( 1.0i +  0.0j +  0.0k)
Rot      QR: [ 0.9239, [-0.3827, -0.0000, -0.0000]] / cos( 22.5) + sin( 22.5)(-1.0i + -0.0j + -0.0k)
Rot LR   -> [ 0.0, -3.0,  0.0,  0.0]
Rot      QR: [ 0.7071, [ 0.7071,  0.0000,  0.0000]] / cos( 45.0) + sin( 45.0)( 1.0i +  0.0j +  0.0k)
Rot L    -> [ 0.0, -3.0,  0.0,  0.0]

Rot Full Step    Q [W, X, Y, Z]: [ 0.9808, [ 0.0000,  0.0000,  0.1951]] / [ 0.00, -0.00,  22.50]deg / cos( 11.25) + sin( 11.25)( 0.00i +  0.00j +  1.00k)
Rot Half Step    Q [W, X, Y, Z]: [ 0.9952, [ 0.0000,  0.0000,  0.0980]] / [ 0.00, -0.00,  11.25]deg / cos( 5.63) + sin( 5.63)( 0.00i +  0.00j +  1.00k)
Rot Half Step Q^-1 [W, X, Y, Z]: [ 0.9952, [-0.0000, -0.0000, -0.0980]] / [ 0.00, -0.00, -11.25]deg / cos( 5.63) + sin( 5.63)(-0.00i + -0.00j + -1.00k)
Rot Step 1. Curr Abs Q: [ 0.9194, [ 0.3808,  0.0375,  0.0906]]/[ 45.00,  0.00,  11.25]deg, Abs Euler: [ 90.00,  0.00,  22.50]deg
Rot      QL: [ 0.9194, [ 0.3808,  0.0375,  0.0906]] / cos( 23.2) + sin( 23.2)( 1.0i +  0.1j +  0.2k)
Rot      QR: [ 0.9194, [-0.3808, -0.0375, -0.0906]] / cos( 23.2) + sin( 23.2)(-1.0i + -0.1j + -0.2k)
Rot LR   -> [ 1.0, -2.8,  0.0,  0.0]
Rot      QR: [ 0.6935, [ 0.6935,  0.1379,  0.1379]] / cos( 46.1) + sin( 46.1)( 1.0i +  0.2j +  0.2k)
Rot L    -> [ 1.1, -2.8,  0.0,  0.0]
Rot Step 2. Curr Abs Q: [ 0.9061, [ 0.3753,  0.0747,  0.1802]]/[ 45.00, -0.00,  22.50]deg, Abs Euler: [ 90.00,  0.00,  45.00]deg
Rot      QL: [ 0.9061, [ 0.3753,  0.0747,  0.1802]] / cos( 25.0) + sin( 25.0)( 0.9i +  0.2j +  0.4k)
Rot      QR: [ 0.9061, [-0.3753, -0.0747, -0.1802]] / cos( 25.0) + sin( 25.0)(-0.9i + -0.2j + -0.4k)
Rot LR   -> [ 1.9, -2.4,  0.1,  0.0]
Rot      QR: [ 0.6533, [ 0.6533,  0.2706,  0.2706]] / cos( 49.2) + sin( 49.2)( 0.9i +  0.4j +  0.4k)
Rot L    -> [ 2.1, -2.1,  0.0,  0.0]
Rot Step 3. Curr Abs Q: [ 0.8841, [ 0.3662,  0.1111,  0.2682]]/[ 45.00,  0.00,  33.75]deg, Abs Euler: [ 90.00,  0.00,  67.50]deg
Rot      QL: [ 0.8841, [ 0.3662,  0.1111,  0.2682]] / cos( 27.9) + sin( 27.9)( 0.8i +  0.2j +  0.6k)
Rot      QR: [ 0.8841, [-0.3662, -0.1111, -0.2682]] / cos( 27.9) + sin( 27.9)(-0.8i + -0.2j + -0.6k)
Rot LR   -> [ 2.5, -1.6,  0.3,  0.0]
Rot      QR: [ 0.5879, [ 0.5879,  0.3928,  0.3928]] / cos( 54.0) + sin( 54.0)( 0.7i +  0.5j +  0.5k)
Rot L    -> [ 2.8, -1.1,  0.0,  0.0]
Rot Step 4. Curr Abs Q: [ 0.8536, [ 0.3536,  0.1464,  0.3536]]/[ 45.00,  0.00,  45.00]deg, Abs Euler: [ 90.00,  0.00,  90.00]deg
Rot      QL: [ 0.8536, [ 0.3536,  0.1464,  0.3536]] / cos( 31.4) + sin( 31.4)( 0.7i +  0.3j +  0.7k)
Rot      QR: [ 0.8536, [-0.3536, -0.1464, -0.3536]] / cos( 31.4) + sin( 31.4)(-0.7i + -0.3j + -0.7k)
Rot LR   -> [ 2.9, -0.7,  0.4,  0.0]
Rot      QR: [ 0.5000, [ 0.5000,  0.5000,  0.5000]] / cos( 60.0) + sin( 60.0)( 0.6i +  0.6j +  0.6k)
Rot L    -> [ 3.0,  0.0,  0.0,  0.0]

推荐答案

我有我的问题的答案和正在运行的轨道摄像头,但是还没有时间仔细检查示例代码是否现在可以正常工作-它应该

I have the answers to my question and a working orbital camera, but haven't had the time to double check if the sample code now works correctly - it should.

第一个问题是,为什么GLM在四元数转换期间将角度减半,并且根据扩展了欧拉公式 ...必须.该部分可能需要更多调查,但由于时间不足,我不得不接受它.

First question was why GLM is halving the angle during the quaternion conversion and it looks like according to the extended Euler's formula... It has to. This part could stand a little more investigation but due to lack of time I'll have to accept it.

使用乘法运算符在GLM中实现矢量旋转.这意味着将vec3与四元数相乘不会将向量转换为四元数,然后执行乘法,它将执行

The vector rotation in GLM was implemented using the multiplication operator. This means that when multiplying a vec3 with a quaternion will not do a conversion of the the vector into a quaternion and then performing a multiplication, it will do a vector rotation instead:

template <typename T, precision P>
GLM_FUNC_QUALIFIER tvec3<T, P> operator*(tquat<T, P> const & q, tvec3<T, P> const & v)
{
    tvec3<T, P> const QuatVector(q.x, q.y, q.z);
    tvec3<T, P> const uv(glm::cross(QuatVector, v));
    tvec3<T, P> const uuv(glm::cross(QuatVector, uv));

    return v + ((uv * q.w) + uuv) * static_cast<T>(2);
}

所以,是的,使用四元数旋转矢量的正确方法是在四元数和矢量之间使用乘法运算符,如下所示:

So, yes, the correct way to rotate a vector using a quaternion is to use the multiplication operator between a quaternion and a vector like this:

v' = q * v

或在C ++中:

vec3 posOrigin;
quat rotQ;
...
vec3 posRot = rotQ * posOrigin;

此代码实际上并不执行直接四元数乘法.它进行旋转.我个人更希望GLM提供像rotate(quat, vec)这样的函数调用...但是,我确定操作员超负荷混淆是有原因的.

This code does not actually do a direct quaternion multiplication. It does a rotation. Personally I'd prefer GLM offered a function call like rotate(quat, vec)... But I am sure there is a reason for operator overloading obfuscation.

还请注意,由于向量和四元数之间的乘法定义如下,因此操作数顺序很重要:

Please also note that the operand order matters, since the multiplication between a vector and a quaternion is defined like this:

template <typename T, precision P>
GLM_FUNC_QUALIFIER tvec3<T, P> operator*(tvec3<T, P> const & v, tquat<T, P> const & q)
{
    return glm::inverse(q) * v;
}

,因此将以相反的方向旋转向量.

and will therefore rotate the vector in the inverse sense.

请注意,GLM还实现了四元数之间的乘法,但是为此,需要使用两个四元数之间的乘法运算符:

Note that GLM also implements multiplication between quaternions, but for this the multiplication operator between two quaternions needs to be used:

template <typename T, precision P>
template <typename U>
GLM_FUNC_QUALIFIER tquat<T, P> & tquat<T, P>::operator*=(tquat<U, P> const & r)
{
    tquat<T, P> const p(*this);
    tquat<T, P> const q(r);

    this->w = p.w * q.w - p.x * q.x - p.y * q.y - p.z * q.z;
    this->x = p.w * q.x + p.x * q.w + p.y * q.z - p.z * q.y;
    this->y = p.w * q.y + p.y * q.w + p.z * q.x - p.x * q.z;
    this->z = p.w * q.z + p.z * q.w + p.x * q.y - p.y * q.x;
    return *this;
}

由于GLM几乎没有我能找到的宝贵文档,因此这种运算符重载会导致错误的假设和大量的时间损失.所以我想我应该已经阅读了GLM代码,而不是假设它会做什么...

Since GLM has precious little documentation I could find, such operator overloading leads to bad assumptions and a significant loss of time. So I suppose I should have read the GLM code instead of assuming what it does...

这篇关于使用GLM的基于四元数的点旋转的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆