zoukankan      html  css  js  c++  java
  • CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换

    CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换

    三维世界里的旋转(rotate),可以用一个3x3的矩阵描述;可以用(旋转角度float+旋转轴vec3)描述。数学家欧拉证明了这两种形式可以相互转化,且多次地旋转可以归结为一次旋转。这实际上就是著名的轨迹球(arcball)方式操纵模型的理论基础。

    本文中都设定float angleDegree为旋转角度,vec3 axis为旋转轴。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    四元数

    +BIT祝威+悄悄在此留下版了个权的信息说:

    定义(angleDegree+axis到四元数)

    四元数就是一个四维向量(w, x, y, z),其中w描述旋转的角度(但不是直接的angleDegree值),(x, y, z)描述旋转轴。从angleDegree和axis得到一个四元数的方式比较简单。

     1     public struct Quaternion
     2     {
     3         private float w;
     4         private float x;
     5         private float y;
     6         private float z;
     7         
     8         /// <summary>
     9         /// Quaternion from a rotation angle and axis.
    10         /// </summary>
    11         /// <param name="angleDegree">angle in degree.</param>
    12         /// <param name="axis">rotation axis.</param>
    13         public Quaternion(float angleDegree, vec3 axis)
    14         {
    15             vec3 normalized = axis.normalize();
    16             double radian = angleDegree * Math.PI / 180.0;
    17             double halfRadian = radian / 2.0;
    18             this.w = (float)Math.Cos(halfRadian);
    19             float sin = (float)Math.Sin(halfRadian);
    20             this.x = sin * normalized.x;
    21             this.y = sin * normalized.y;
    22             this.z = sin * normalized.z;
    23         }
    24     }

    先别管为什么四元数是这么定义的,只要知道这个定义就好。这里引入四元数只是为了方便提取出矩阵中蕴含的angleDegree和aixs。四元数的其他用途本文不涉及。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    四元数到angleDegree+axis

    从上面的定义可以很容易推算出四元数里蕴含的angleDegree和axis。显然得到的axis已经失去了原有的长度,但是axis的长度并不重要,保持在单位长度才是最方便的。

    1         public void Parse(out float angleDegree, out vec3 axis)
    2         {
    3             angleDegree = (float)(Math.Acos(w) * 2 * 180.0 / Math.PI);
    4             axis = (new vec3(x, y, z)).normalize();
    5         }
    +BIT祝威+悄悄在此留下版了个权的信息说:

    四元数到矩阵

    从四元数到矩阵的推导有点复杂,有很多相关文章,本文就只贴代码了。代码还是很简练的。

     1         /// <summary>
     2         /// Transform this quaternion to equivalent matrix.
     3         /// </summary>
     4         /// <returns></returns>
     5         public mat3 ToRotationMatrix()
     6         {
     7             vec3 col0 = new vec3(
     8                 2 * (x * x + w * w) - 1,
     9                 2 * x * y + 2 * w * z,
    10                 2 * x * z - 2 * w * y);
    11             vec3 col1 = new vec3(
    12                 2 * x * y - 2 * w * z,
    13                 2 * (y * y + w * w) - 1,
    14                 2 * y * z + 2 * w * x);
    15             vec3 col2 = new vec3(
    16                 2 * x * z + 2 * w * y,
    17                 2 * y * z - 2 * w * x,
    18                 2 * (z * z + w * w) - 1);
    19 
    20             return new mat3(col0, col1, col2);
    21         }
    实际上得到的矩阵就是这样的:
    +BIT祝威+悄悄在此留下版了个权的信息说:

    矩阵到四元数

    矩阵到四元数的推导也有点复杂,借助了一些数学技巧,本文不详述,直接贴代码。

     1         /// <summary>
     2         /// Transform this matrix to a <see cref="Quaternion"/>.
     3         /// </summary>
     4         /// <returns></returns>
     5 struct mat3
     6 {
     7     public Quaternion ToQuaternion()
     8         {
     9             // input matrix.
    10             float m11 = this.col0.x, m12 = this.col1.x, m13 = this.col2.x;
    11             float m21 = this.col0.y, m22 = this.col1.y, m23 = this.col2.y;
    12             float m31 = this.col0.z, m32 = this.col1.z, m33 = this.col2.z;
    13             // output quaternion
    14             float x = 0, y = 0, z = 0, w = 0;
    15             // detect biggest in w, x, y, z.
    16             float fourWSquaredMinus1 = +m11 + m22 + m33;
    17             float fourXSquaredMinus1 = +m11 - m22 - m33;
    18             float fourYSquaredMinus1 = -m11 + m22 - m33;
    19             float fourZSquaredMinus1 = -m11 - m22 + m33;
    20             int biggestIndex = 0;
    21             float biggest = fourWSquaredMinus1;
    22             if (fourXSquaredMinus1 > biggest)
    23             {
    24                 biggest = fourXSquaredMinus1;
    25                 biggestIndex = 1;
    26             }
    27             if (fourYSquaredMinus1 > biggest)
    28             {
    29                 biggest = fourYSquaredMinus1;
    30                 biggestIndex = 2;
    31             }
    32             if (fourZSquaredMinus1 > biggest)
    33             {
    34                 biggest = fourZSquaredMinus1;
    35                 biggestIndex = 3;
    36             }
    37             // sqrt and division
    38             float biggestVal = (float)(Math.Sqrt(biggest + 1) * 0.5);
    39             float mult = 0.25f / biggestVal;
    40             // get output
    41             switch (biggestIndex)
    42             {
    43                 case 0:
    44                     w = biggestVal;
    45                     x = (m23 - m32) * mult;
    46                     y = (m31 - m13) * mult;
    47                     z = (m12 - m21) * mult;
    48                     break;
    49 
    50                 case 1:
    51                     x = biggestVal;
    52                     w = (m23 - m32) * mult;
    53                     y = (m12 + m21) * mult;
    54                     z = (m31 + m13) * mult;
    55                     break;
    56 
    57                 case 2:
    58                     y = biggestVal;
    59                     w = (m31 - m13) * mult;
    60                     x = (m12 + m21) * mult;
    61                     z = (m23 + m32) * mult;
    62                     break;
    63 
    64                 case 3:
    65                     z = biggestVal;
    66                     w = (m12 - m21) * mult;
    67                     x = (m31 + m13) * mult;
    68                     y = (m23 + m32) * mult;
    69                     break;
    70 
    71                 default:
    72                     break;
    73             }
    74 
    75             return new Quaternion(w, -x, -y, -z);
    76         }
    77     }
    matrix to quaternion

    好了,现在矩阵 ⇋ 四元数 ⇋ (angleDegree+axis)之间的转换就全有了。

    BTW,OpenGL里的glRotate{fd}(angle, axis)里的angle是以角度为单位的。为了统一,我将CSharpGL里的所有angle都设定为以角度为单位了。

    +BIT祝威+悄悄在此留下版了个权的信息说:

    下载

    CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL

    +BIT祝威+悄悄在此留下版了个权的信息说:

    总结

    现在解决了矩阵与(angleDegree+axis)之间的转换问题,就可以从容地解析轨迹球算出的旋转矩阵,抽取出里面蕴含的(angleDegree+axis)了。这就可以单独更新模型的旋转角度和旋转轴,避免了对整个模型矩阵的破坏。

  • 相关阅读:
    Windows远程桌面跳板机无法复制粘贴
    无法打开“XXXX”,因为Apple无法检查其是否包含恶意软件。怎么解决?
    mac下的快捷键
    python:递归函数
    ps:新建Photoshop图像
    python-函数的参数
    python3-定义函数
    python3-调用函数
    python3-函数
    ps:界面概览
  • 原文地址:https://www.cnblogs.com/bitzhuwei/p/CSharpGL-32-transform-between-matrix-and-angle-axis.html
Copyright © 2011-2022 走看看