CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换
CSharpGL(32)矩阵与四元数与角度旋转轴的相互转换
三维世界里的旋转(rotate),可以用一个3x3的矩阵描述;可以用(旋转角度float+旋转轴vec3)描述。数学家欧拉证明了这两种形式可以相互转化,且多次地旋转可以归结为一次旋转。这实际上就是著名的轨迹球(arcball)方式操纵模型的理论基础。
本文中都设定float angleDegree为旋转角度,vec3 axis为旋转轴。
四元数
定义(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。四元数的其他用途本文不涉及。
四元数到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 }
四元数到矩阵
从四元数到矩阵的推导有点复杂,有很多相关文章,本文就只贴代码了。代码还是很简练的。
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 }
矩阵到四元数
矩阵到四元数的推导也有点复杂,借助了一些数学技巧,本文不详述,直接贴代码。
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 }
好了,现在矩阵 ⇋ 四元数 ⇋ (angleDegree+axis)之间的转换就全有了。
BTW,OpenGL里的glRotate{fd}(angle, axis)里的angle是以角度为单位的。为了统一,我将CSharpGL里的所有angle都设定为以角度为单位了。
下载
CSharpGL已在GitHub开源,欢迎对OpenGL有兴趣的同学加入(https://github.com/bitzhuwei/CSharpGL)
总结
现在解决了矩阵与(angleDegree+axis)之间的转换问题,就可以从容地解析轨迹球算出的旋转矩阵,抽取出里面蕴含的(angleDegree+axis)了。这就可以单独更新模型的旋转角度和旋转轴,避免了对整个模型矩阵的破坏。
微信扫码,自愿捐赠。天涯同道,共谱新篇。
微信捐赠不显示捐赠者个人信息,如需要,请注明联系方式。 |