四元数的角度 - 使一个对象面向另一个对象
Angle to Quaternion - Making an object facing another object
我在 3D 世界中有两个对象,我想让一个对象面对另一个对象。我已经计算了所有的角度和东西(俯仰角和偏航角)。
问题是我没有单独设置偏航或俯仰的功能,这意味着我必须通过四元数来完成。因为我唯一的功能是:SetEnetyQuaternion(float x, float y, float z, float w)。这是我的伪代码:
float px, py, pz;
float tx, ty, tz;
float distance;
GetEnetyCoordinates(ObjectMe, &px, &py, &pz);
GetEnetyCoordinates(TargetObject, &tx, &ty, &tz);
float yaw, pitch;
float deltaX, deltaY, deltaZ;
deltaX = tx - px;
deltaY = ty - py;
deltaZ = tz - pz;
float hyp = SQRT((deltaX*deltaX) + (deltaY*deltaY) + (deltaZ*deltaZ));
yaw = (ATAN2(deltaY, deltaX));
if(yaw < 0) { yaw += 360; }
pitch = ATAN2(-deltaZ, hyp);
if (pitch < 0) { pitch += 360; }
//here is the part where i need to do a calculation to convert the angles
SetEnetyQuaternion(ObjectMe, pitch, 0, yaw, 0);
我尝试过的是从除以 2 的角度计算正弦,但这没有用 - 我认为这是欧拉角或类似的角度,但对我没有帮助。 roll(y 轴) 和 w 参数我认为可以省略,因为我不希望我的对象有一个滚动。这就是为什么我输入 0。
如果有人有任何想法,我将不胜感激。
提前谢谢你:)
假设您想要的四元数描述了玩家相对于某种参考态度的态度。那么有必要知道参考态度是什么。
此外,您需要了解物体的姿态不仅包括其 面向 - 它还包括物体围绕该面向的方向。例如,假设玩家直接面向位置坐标系的正 x 方向。这提供了许多不同的姿势,从玩家直立的姿势到他在左侧或右侧水平站立的姿势,再到他倒立的姿势,以及介于两者之间的所有姿势。
假设合适的参考姿态是面向x正方向,"up"平行z正方向 方向(我们称之为 "vertical")。我们还假设在玩家面对目标的姿态中,您想要 "up" 最接近垂直的姿态。我们可以想象所需的姿态改变分两步执行:绕坐标 y 轴旋转,然后绕坐标 z 轴旋转。我们可以为其中的每一个写一个单位四元数,而整体旋转所需的四元数就是这些四元数的哈密顿积。
围绕坐标描述的单位向量旋转角度 θ 的四元数 (x, y, z) 是 (cos θ/2, x sin θ/2, y sin θ/2, zsinθ/2)。然后考虑你想要的第一个四元数,对应于音高。你有
double semiRadius = sqrt(deltaX * deltaX + deltaY * deltaY);
double cosPitch = semiRadius / hyp;
double sinPitch = deltaZ / hyp; // but note that we don't actually need this
。但是你需要 half 那个角度的正弦和余弦。半角公式在这里派上用场:
double sinHalfPitch = sqrt((1 - cosPitch) / 2) * ((deltaZ < 0) ? -1 : 1);
double cosHalfPitch = sqrt((1 + cosPitch) / 2);
余弦总是非负的,因为俯仰角必须在第一或第四象限;如果物体在玩家上方,则正弦为正,如果在玩家下方,则正弦为负。完成所有这些后,第一个四元数是
(cosHalfPitch, 0, sinHalfPitch, 0)
类似的分析适用于第二个四元数。全旋转角度的余弦和正弦为
double cosYaw = deltaX / semiRadius;
double sinYaw = deltaY / semiRadius; // again, we don't actually need this
我们可以再次应用半角公式,但现在我们需要考虑全角在任何象限中。然而,半角只能在第 1 或第 2 象限,因此它的正弦必然是非负的:
double sinHalfYaw = sqrt((1 - cosYaw) / 2);
double cosHalfYaw = sqrt((1 + cosYaw) / 2) * ((deltaY < 0) ? -1 : 1);
这给了我们一个总的第二个四元数
(cosHalfYaw, 0, 0, sinHalfYaw)
你想要的四元数是这两个的哈密顿积,你必须注意用正确的操作数顺序(qYaw * qPitch)来计算它,因为哈密顿积是不可交换的。这两个因子中的所有零都使整体表达式比其他情况下简单得多,但是:
(cosHalfYaw * cosHalfPitch,
-sinHalfYaw * sinHalfPitch,
cosHalfYaw * sinHalfPitch,
sinHalfYaw * cosHalfPitch)
在这一点上,我提醒你,我们从一个关于四元数系统的参考姿态的假设开始,这个结果取决于那个选择。另外提醒一下,我对通缉态度做了一个假设,这也会影响这个结果。
最后,我观察到这种方法在目标对象非常接近玩家正上方或正下方(对应于 semiRadius
取非常接近于零的值)和玩家非常接近玩家的地方失效在目标之上(对应于 hyp
取一个非常接近于零的值)。如果您完全按照给定的方式使用这些公式,则导致被零除的可能性不为零,因此您需要考虑如何处理。)
我在 3D 世界中有两个对象,我想让一个对象面对另一个对象。我已经计算了所有的角度和东西(俯仰角和偏航角)。 问题是我没有单独设置偏航或俯仰的功能,这意味着我必须通过四元数来完成。因为我唯一的功能是:SetEnetyQuaternion(float x, float y, float z, float w)。这是我的伪代码:
float px, py, pz;
float tx, ty, tz;
float distance;
GetEnetyCoordinates(ObjectMe, &px, &py, &pz);
GetEnetyCoordinates(TargetObject, &tx, &ty, &tz);
float yaw, pitch;
float deltaX, deltaY, deltaZ;
deltaX = tx - px;
deltaY = ty - py;
deltaZ = tz - pz;
float hyp = SQRT((deltaX*deltaX) + (deltaY*deltaY) + (deltaZ*deltaZ));
yaw = (ATAN2(deltaY, deltaX));
if(yaw < 0) { yaw += 360; }
pitch = ATAN2(-deltaZ, hyp);
if (pitch < 0) { pitch += 360; }
//here is the part where i need to do a calculation to convert the angles
SetEnetyQuaternion(ObjectMe, pitch, 0, yaw, 0);
我尝试过的是从除以 2 的角度计算正弦,但这没有用 - 我认为这是欧拉角或类似的角度,但对我没有帮助。 roll(y 轴) 和 w 参数我认为可以省略,因为我不希望我的对象有一个滚动。这就是为什么我输入 0。
如果有人有任何想法,我将不胜感激。 提前谢谢你:)
假设您想要的四元数描述了玩家相对于某种参考态度的态度。那么有必要知道参考态度是什么。
此外,您需要了解物体的姿态不仅包括其 面向 - 它还包括物体围绕该面向的方向。例如,假设玩家直接面向位置坐标系的正 x 方向。这提供了许多不同的姿势,从玩家直立的姿势到他在左侧或右侧水平站立的姿势,再到他倒立的姿势,以及介于两者之间的所有姿势。
假设合适的参考姿态是面向x正方向,"up"平行z正方向 方向(我们称之为 "vertical")。我们还假设在玩家面对目标的姿态中,您想要 "up" 最接近垂直的姿态。我们可以想象所需的姿态改变分两步执行:绕坐标 y 轴旋转,然后绕坐标 z 轴旋转。我们可以为其中的每一个写一个单位四元数,而整体旋转所需的四元数就是这些四元数的哈密顿积。
围绕坐标描述的单位向量旋转角度 θ 的四元数 (x, y, z) 是 (cos θ/2, x sin θ/2, y sin θ/2, zsinθ/2)。然后考虑你想要的第一个四元数,对应于音高。你有
double semiRadius = sqrt(deltaX * deltaX + deltaY * deltaY);
double cosPitch = semiRadius / hyp;
double sinPitch = deltaZ / hyp; // but note that we don't actually need this
。但是你需要 half 那个角度的正弦和余弦。半角公式在这里派上用场:
double sinHalfPitch = sqrt((1 - cosPitch) / 2) * ((deltaZ < 0) ? -1 : 1);
double cosHalfPitch = sqrt((1 + cosPitch) / 2);
余弦总是非负的,因为俯仰角必须在第一或第四象限;如果物体在玩家上方,则正弦为正,如果在玩家下方,则正弦为负。完成所有这些后,第一个四元数是
(cosHalfPitch, 0, sinHalfPitch, 0)
类似的分析适用于第二个四元数。全旋转角度的余弦和正弦为
double cosYaw = deltaX / semiRadius;
double sinYaw = deltaY / semiRadius; // again, we don't actually need this
我们可以再次应用半角公式,但现在我们需要考虑全角在任何象限中。然而,半角只能在第 1 或第 2 象限,因此它的正弦必然是非负的:
double sinHalfYaw = sqrt((1 - cosYaw) / 2);
double cosHalfYaw = sqrt((1 + cosYaw) / 2) * ((deltaY < 0) ? -1 : 1);
这给了我们一个总的第二个四元数
(cosHalfYaw, 0, 0, sinHalfYaw)
你想要的四元数是这两个的哈密顿积,你必须注意用正确的操作数顺序(qYaw * qPitch)来计算它,因为哈密顿积是不可交换的。这两个因子中的所有零都使整体表达式比其他情况下简单得多,但是:
(cosHalfYaw * cosHalfPitch,
-sinHalfYaw * sinHalfPitch,
cosHalfYaw * sinHalfPitch,
sinHalfYaw * cosHalfPitch)
在这一点上,我提醒你,我们从一个关于四元数系统的参考姿态的假设开始,这个结果取决于那个选择。另外提醒一下,我对通缉态度做了一个假设,这也会影响这个结果。
最后,我观察到这种方法在目标对象非常接近玩家正上方或正下方(对应于 semiRadius
取非常接近于零的值)和玩家非常接近玩家的地方失效在目标之上(对应于 hyp
取一个非常接近于零的值)。如果您完全按照给定的方式使用这些公式,则导致被零除的可能性不为零,因此您需要考虑如何处理。)