代码之家  ›  专栏  ›  技术社区  ›  Phrogz

用上方向向量看四元数

  •  6
  • Phrogz  · 技术社区  · 6 年前

    This question 在没有“向上”向量的情况下要求相同的东西。所有这三个答案都会导致相机指向正确的方向,但会滚动(如偏航/俯仰/滚动;想象一下,当你看着什么东西的时候,把头靠在耳朵上。

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

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

    如何从这三个向量创建四元数? This question 问同样的问题…但不幸的是,唯一被接受的答案是~“假设你不在乎滚动”,然后忽略了上轴。我真的很在乎滚动。我不想忽略上轴。

    3 回复  |  直到 6 年前
        1
  •  8
  •   meowgoesthedog    6 年前

    前面的答案给出了一个使用角度的有效解决方案。这个答案将提供另一种方法。

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

    enter image description here

    3x3旋转矩阵可以转换成四元数 没有

    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;
      }
    }
    

    (不用说交换了 y z

        2
  •  2
  •   Community CDub    4 年前

    假设你最初有三个ortonormal向量:worldUp,worldFront和worldSide,让我们使用你的lookAt,sideAxis和rotatedUp方程。世界侧矢量将不必实现该结果。

    Axis1=worldUp
    角度1=(见下文)

    轴2=交叉(注视,世界向上)=侧轴
    角度2=(见下文)

    Q=cos(角/2)+i*轴x*sin(角/2)+j*轴y*sin(角/2)+k*轴z*sin(角/2)

    将Q1和Q2相乘,得到所需的四元数。

    设P(worldUp)为worldUp方向上的投影矩阵,即P(worldUp).v=cos(worldUp,v).worldUp或使用kets和bras,P(worldUp)=| worldUp>&书信电报;worldUp |。我是单位矩阵。

    1. 在垂直于worldUp的平面上投影lookAt并使其正常化。

      tmp1=(I-P(worldUp))。看

    2. 角度1=arccos(点(worldFront,n1))

    3. 角度2=arccos(点(注视,n1))

    注意,不需要计算超越函数。因为一对归一化向量的点积是一个角度的余弦,假设 cos(t) = x

    • cos(t/2) = sqrt((1 + x)/2)
    • sin(t/2) = sqrt((1 - x)/2)
        3
  •  -2
  •   minorlogic    6 年前

    你看 横轴

    如果规范化这3个向量,它就是旋转矩阵3x3的一个分量。把这个旋转矩阵转换成四元数。