查找四元数使用向量

时间:2018-09-19 20:06:53

标签: quaternions

我有一个相机(在自定义3D引擎中),它接受四元数进行旋转变换。我有两个3D点,分别代表相机和要查看的对象。我想计算从相机到物体 的四元数,同时尊重世界上的上轴

This question要求相同的内容而没有“ up”向量。这三个答案都会使相机指向正确的方向,但会滚动(如偏航/俯仰/滚动);想象一下看着东西时将头靠在耳朵上。

我可以通过以下方式计算与所需坐标系匹配的向量的正交基础:

lookAt = normalize(target - camera)
sideaxis = cross(lookAt, worldUp)
rotatedup = cross(sideaxis, lookAt)

如何从这三个向量创建四元数? This question要求相同的内容...但不幸的是,唯一且被接受的答案是〜“让我们假设您不关心滚动”,然后忽略上轴。我在乎滚。我不想忽略上轴。

3 个答案:

答案 0 :(得分:4)

先前的答案已使用角度给出了有效的解决方案。这个答案将提出一种替代方法。

正交基矢量,将其重命名为F = lookAt, R = sideaxis, U = rotatedup,直接形成3x3旋转矩阵的,它等效于所需的四元数:

enter image description here

与向量的乘积等效于使用所述向量的分量作为相机基础中的坐标。

3x3旋转矩阵可以转换为四元数,而无需转换为角度/使用昂贵的三角函数。下面是一个数值稳定的C ++代码段,它执行此操作,并返回标准化的四元数:

inline void CalculateRotation( Quaternion& q ) const {
  float trace = a[0][0] + a[1][1] + a[2][2];
  if( trace > 0 ) {
    float s = 0.5f / sqrtf(trace + 1.0f);
    q.w = 0.25f / s;
    q.x = ( a[2][1] - a[1][2] ) * s;
    q.y = ( a[0][2] - a[2][0] ) * s;
    q.z = ( a[1][0] - a[0][1] ) * s;
  } else {
    if ( a[0][0] > a[1][1] && a[0][0] > a[2][2] ) {
      float s = 2.0f * sqrtf( 1.0f + a[0][0] - a[1][1] - a[2][2]);
      q.w = (a[2][1] - a[1][2] ) / s;
      q.x = 0.25f * s;
      q.y = (a[0][1] + a[1][0] ) / s;
      q.z = (a[0][2] + a[2][0] ) / s;
    } else if (a[1][1] > a[2][2]) {
      float s = 2.0f * sqrtf( 1.0f + a[1][1] - a[0][0] - a[2][2]);
      q.w = (a[0][2] - a[2][0] ) / s;
      q.x = (a[0][1] + a[1][0] ) / s;
      q.y = 0.25f * s;
      q.z = (a[1][2] + a[2][1] ) / s;
    } else {
      float s = 2.0f * sqrtf( 1.0f + a[2][2] - a[0][0] - a[1][1] );
      q.w = (a[1][0] - a[0][1] ) / s;
      q.x = (a[0][2] + a[2][0] ) / s;
      q.y = (a[1][2] + a[2][1] ) / s;
      q.z = 0.25f * s;
    }
  }
}

来源:http://www.euclideanspace.com/maths/geometry/rotations/conversions/matrixToQuaternion

要进行转换以适合您的情况,当然只是将矩阵元素与相应的矢量分量交换而已:

// your code from before
F = normalize(target - camera);   // lookAt
R = normalize(cross(F, worldUp)); // sideaxis
U = cross(R, F);                  // rotatedup

// note that R needed to be re-normalized
// since F and worldUp are not necessary perpendicular
// so must remove the sin(angle) factor of the cross-product
// same not true for U because dot(R, F) = 0

// adapted source
Quaternion q;
double trace = R.x + U.y + F.z;
if (trace > 0.0) {
  double s = 0.5 / sqrt(trace + 1.0);
  q.w = 0.25 / s;
  q.x = (U.z - F.y) * s;
  q.y = (F.x - R.z) * s;
  q.z = (R.y - U.x) * s;
} else {
  if (R.x > U.y && R.x > F.z) {
    double s = 2.0 * sqrt(1.0 + R.x - U.y - F.z);
    q.w = (U.z - F.y) / s;
    q.x = 0.25 * s;
    q.y = (U.x + R.y) / s;
    q.z = (F.x + R.z) / s;
  } else if (U.y > F.z) {
    double s = 2.0 * sqrt(1.0 + U.y - R.x - F.z);
    q.w = (F.x - R.z) / s;
    q.x = (U.x + R.y) / s;
    q.y = 0.25 * s;
    q.z = (F.y + U.z) / s;
  } else {
    double s = 2.0 * sqrt(1.0 + F.z - R.x - U.y);
    q.w = (R.y - U.x) / s;
    q.x = (F.x + R.z) / s;
    q.y = (F.y + U.z) / s;
    q.z = 0.25 * s;
  }
}

(如果您使用的是OpenGL,不用说交换yz。)

答案 1 :(得分:1)

假设您最初有三个正交向量:worldUp,worldFront和worldSide,然后将方程式用于lookAt,sideAxis和rotationUp。不需要worldSide向量即可获得结果。

将操作分成两部分。首先,围绕worldUp旋转。然后绕着sideAxis旋转,它现在实际上与旋转的worldSide平行。

  

Axis1 = worldUp
  Angle1 =(请参见下文)

     

Axis2 =交叉(lookAt,worldUp)= sideAxis
  Angle2 =(见下文)

这些旋转中的每一个对应于使用以下条件的四元数:

  

Q = cos(角度/ 2)+ i * Axis_x * sin(角度/ 2)+ j * Axis_y * sin(角度/ 2)+ k * Axis_z * sin(角度/ 2)

将Q1和Q2都乘以得到所需的四元数。

角度的详细信息:

让P(worldUp)为worldUp方向上的投影矩阵,即P(worldUp).v = cos(worldUp,v).worldUp或使用小胸罩和胸罩,P(worldUp)= | worldUp>

  1. 在与worldUp垂直的平面上投影lookAt并将其标准化。

      

    tmp1 =(I-P(worldUp))。lookAt
      n1 = normalize(tmp1)

  2. Angle1 = arccos(dot(worldFront,n1))

  3. Angle2 = arccos(dot(lookAt,n1))

EDIT1:

请注意,无需计算先验函数。由于一对归一化向量的点积是一个角度的余弦,并且假设cos(t) = x,我们具有三角恒等式:

  • cos(t/2) = sqrt((1 + x)/2)
  • sin(t/2) = sqrt((1 - x)/2)

答案 2 :(得分:-1)

lookAt 侧轴 旋转

如果对这三个向量进行归一化,则它是旋转矩阵3x3的组成部分。因此,只需将此旋转矩阵转换为四元数即可。