c - 与四元数的角度 - 使一个对象面向另一个对象

标签 c angle

我在 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 来计算正弦,但这不起作用 - 我认为这是针对欧拉角或类似的东西,但对我没有帮助。我认为滚动(y 轴)和 w 参数可以省略,因为我不希望我的对象滚动。这就是为什么我输入 0。

如果有人有任何想法,我将非常感谢帮助。 预先感谢您:)

最佳答案

假设您想要的四元数描述了玩家相对于某些引用态度的态度。那么了解引用态度是什么就很重要了。

此外,您需要了解对象的姿态不仅仅包含其面向,还包含对象围绕该面向的方向。例如,假设玩家直接面向位置坐标系的正 x 方向。这提供了许多不同的态度,从玩家笔直站立的态度到他左侧或右侧水平站立的态度,到他倒立的态度,以及所有介于两者之间的态度。

假设适当的引用姿态是平行于正 x 方向,“向上”平行于正 z 方向(我们将将此称为“垂直”)。我们还假设,在玩家面对目标的各种态度中,您希望“向上”的态度最接近垂直。我们可以想象想要的姿态改变分两步执行:绕坐标y轴旋转,然后绕坐标z轴旋转。我们可以为每个四元数编写一个单位四元数,并且整体旋转所需的四元数是这些四元数的汉密尔顿乘积。

围绕由坐标 (x, y, z) 描述的单位 vector 旋转角度 θ 的四元数为 (cos θ/2,x sin θ/2,y sin θ/2,z sin θ/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

。但是您需要该角度的一半的正弦和余弦。半角公式在这里派上用场:

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)

您想要的四元数是这两个的 Hamilton 乘积,并且您必须注意使用正确的操作数顺序 (qYaw * qPitch) 来计算它,因为 Hamilton 乘积不可交换。然而,两个因子中的所有零使得整体表达式比其他情况简单得多:

(cosHalfYaw * cosHalfPitch,
-sinHalfYaw * sinHalfPitch,
 cosHalfYaw * sinHalfPitch,
 sinHalfYaw * cosHalfPitch)

此时我提醒您,我们一开始就对四元数系统的引用态度进行了假设,而这个结果取决于该选择。我还提醒您,我对想要的态度做出了假设,这也会影响这个结果。

最后,我观察到,当目标对象非常接近玩家的正上方或正下方(对应于 semiRadius 取非常接近零的值)以及玩家非常接近的位置时,这种方法就会失效。几乎在目标之上(对应于hyp取非常接近零的值)。如果您完全按照给定的方式使用这些公式,则导致除以零的可能性非零,因此您需要考虑如何处理该问题。)

关于c - 与四元数的角度 - 使一个对象面向另一个对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42984308/

相关文章:

c++ - 如何从向前、向上和向右 vector 计算欧拉角?

android - 如何在 Android 中对旋转图像进行 HitTest ?

有人可以解释一下 C 中 signal() 语法的含义吗?

c - 标准 C 函数名称

C 编程 - 分析字符串中的散文?

c++ - 特征:vector3ds 之间的快速平均角度

matrix - 沿一个坐标轴的3D偏斜变换矩阵

c++ - 模数最接近零

c - 无符号和有符号数据比较及其在内存中的表示

c - 将结构定义为 extern