http://blog.csdn.net/cppyin/article/details/6177742

 

在3D程序中,通常用quaternion来计算3D物体的旋转角度,与Matrix相比,quaternion更加高效,占用的储存空间更小,此外也更便于插值。在数学上,quaternion表示复数w+xi+yj+zk,其中i,j,k都是虚数单位:

i*i = j*j = k*k= -1

i*j = k, j*i = -k

可以把quaternion看做一个标量和一个3D向量的组合。实部w表示标量,虚部表示向量标记为V,或三个单独的分量(x,y,z)。所以quaternion可以记为[ w, V]或[ w,(x,y,x)]。对quaternion最大的误解在于认为w表示旋转角度,V表示旋转轴。正确的理解应该是w与旋转角度有关,v与旋转轴有关。例如,要表示以向量N为轴,轴旋α度,相对的quaternion应该是:

q = [ cos(α/ 2) , sin(α/ 2) N]

=[ cos(α/ 2) , ( sina(α/ 2) Nx, sin(α/ 2)Ny, sin(α/ 2)Nz ) ]

为了计算方便,一般要求N为单位矢量。对quaternion来说使用四个值就能记录旋转,而不是Matrix所需的十六个值。为什么用quaternion来计算旋转很方便呢?先说过quaternion是一个复数,如果你还记得一点点复数的知识,那么应该知道复数乘法(叉乘)的几何意义实际上就是对复数进行旋转。对最简单的复数p= x + yi来说,和另一个复数q = ( conα,sinα)相乘,则表示把p沿逆时针方向旋转α:

p’ = pq

当然,x+yi的形式只能表示2D变换,对3D变换来说就需要使用 quaternion了,而且计算也要复杂一点。为了对3D空间中的一个点p(x,y,z)进行旋转,需要先把它转换为quaternion形式p = [0, ( x, y, z)],接下来前面讨论的内容,定义q = cos(α/ 2) , sin(α/ 2) N为旋转quaternion,这里N为单位矢量长度的旋转轴,α为旋转角度。那么旋转之后的点p’则为:

p’ = qpq-1

1. 数学分析

1) 四元数是什么东西?

这个东西算盘、矩阵、复数是一类东西,即数学工具,数学家们创造了这个东西来解决一些数学问题。其实四元数是一种超复数,他不是只有一个虚数的复数,而是有三个虚数的复数。我们先回顾一下复数吧。

2) 虚数的来源

实数集中没有-1的平方根,因为没有哪个实数的平方等于-1,所以数学家们就创造它——虚数i,并且定义了i * i = -1。

所以我们可以计算sqrt(-4)了,sqrt(-4) = 2*i

3) 复数

定义:一个实数与一个虚数的和。 z = a + bi。

a叫实部,b叫虚部。

其中(a,b)为复平面上的点。这也是为什么3D图形运算能和四元数挂上关系了。

运算法则:

z1 = a + b*i

z2 = c + d*i

与标量相乘 k*z1 = k*a + K*b*i

复数相加 z1 + z2 = a + c + (b + d) * i

复数相乘 z1 * z2 = (a + b * i) * (c + d * i) = a * c + a * d * i + c * b * i - b * d = a*c - b*d + (a*d+b*c) * i

复数相除 z1 / z2 = (a + b*i) / (c + d*i)

z1的共轭z1* = a - b*i,作用是z×z*为实数

所以复数相除 z1 / z2 = (a + b*i)*(c - d*i) / (c2+d2) = ... = (a*c + b*d)/(a2+b2) + (b*c-a*d)*i)/(a2+b2)

z1的倒数 1/z = 1 / (a+b*i),同样可以转化为:a/(a2+b2) + b*i/(a2+b2)

z1的范数 |z| = sqrt(a2+b2) = sqrt(z×z*)

4) 超复数

q = q0 + q1*i + q2*j + q3*k

上面就是四元数的表示,其中q0为实部,而虚部<q1,q2,q3>正好组成了3D复平面中的向量。

简写就是q = q0 + qv,其中qv是向量<q1, q2 ,q3>。

超复数的运算法则与复数相同,这里就不再重复了,但要特别说明四元数的倒数q-1

q×q-1 = 1

两边同时乘以q*:

q×q-1×q* = q*

因为:q×q* = |q|2

所以q-1=q* / |q|2

可以注意到,如果q是一个单位四元数的话,那么q的倒数就等于q的共轭。

这个特性非常重要,因为在四元数旋转中要使用。

5) 四元数旋转

对于一个3D向量<x, y, z>,我们把他表现为四元数形式,则是:vq= <0, x, y, z>。

以及给定一个表示旋转轴和旋转角度的单位四元数q,那么向量vq绕指定轴旋转指定角度的结果四元数vq'满足如下:

右手坐标系:

顺时针旋转:vq' = q*×vq×q

逆时针旋转:vq' = q×vq×q*

左手坐标系:

顺时针旋转:vq' = q×vq×q*

逆时针旋转:vq' = q*×vq×q

其中,对于那个用于存储旋转轴和旋转角度的单位四元数q,他与旋转轴v = <x0, y0, z0>和角度theta关系如下:

q = Cos(theta/2) + Sin(theta/2) × v

2. 代码实现

1) 四元数结构体定义

[cpp] view plaincopy
  1. typedef struct QUAT_TYPE // 四元数
  2. {
  3. union
  4. {
  5. double M[4];
  6. struct
  7. {
  8. double w, x, y, z;
  9. };
  10. struct
  11. {
  12. double q0;
  13. VECTOR3D qv;
  14. };
  15. };
  16. } QUAT, *QUAT_PTR;

2) 四元数常用操作函数实现

[cpp] view plaincopy
  1. void _CPPYIN_Math::QuatCreate(QUAT_PTR q, VECTOR3D_PTR v, double theta) // 创建用于旋转的四元数q, v必须为单位向量
  2. {
  3. double theta_div_2 = (0.5)*theta;
  4. double sin_theta = sin(theta_div_2);
  5. q->x = sin_theta * v->x;
  6. q->y = sin_theta * v->y;
  7. q->z = sin_theta * v->z;
  8. q->w = cos( theta_div_2 );
  9. }
  10. void _CPPYIN_Math::QuatGetVectorAndTheta(QUAT_PTR q, VECTOR3D_PTR v, double *theta)
  11. {
  12. *theta = acos(q->w);
  13. double sin_theta_inv = 1.0/sin(*theta);
  14. v->x = q->x * sin_theta_inv;
  15. v->y = q->y * sin_theta_inv;
  16. v->z = q->z * sin_theta_inv;
  17. *theta *= 2;
  18. }
  19. void _CPPYIN_Math::QuatAdd(QUAT_PTR q1, QUAT_PTR q2, QUAT_PTR qsum)
  20. {
  21. qsum->x = q1->x + q2->x;
  22. qsum->y = q1->y + q2->y;
  23. qsum->z = q1->z + q2->z;
  24. qsum->w = q1->w + q2->w;
  25. }
  26. void _CPPYIN_Math::QuatSub(QUAT_PTR q1, QUAT_PTR q2, QUAT_PTR qdiff)
  27. {
  28. qdiff->x = q1->x - q2->x;
  29. qdiff->y = q1->y - q2->y;
  30. qdiff->z = q1->z - q2->z;
  31. qdiff->w = q1->w - q2->w;
  32. }
  33. void _CPPYIN_Math::QuatConjugate(QUAT_PTR q, QUAT_PTR qconj) // 求共轭
  34. {
  35. qconj->x = -q->x;
  36. qconj->y = -q->y;
  37. qconj->z = -q->z;
  38. qconj->w = q->w;
  39. }
  40. void _CPPYIN_Math::QuatScale(QUAT_PTR q, double scale, QUAT_PTR qs)
  41. {
  42. qs->x = scale * q->x;
  43. qs->y = scale * q->y;
  44. qs->z = scale * q->z;
  45. qs->w = scale * q->w;
  46. }
  47. double _CPPYIN_Math::QuatNorm(QUAT_PTR q)
  48. {
  49. return sqrt(q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z);
  50. }
  51. double _CPPYIN_Math::QuatNorm2(QUAT_PTR q)
  52. {
  53. return q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z;
  54. }
  55. void _CPPYIN_Math::QuatNormalize(QUAT_PTR q, QUAT_PTR qn)
  56. {
  57. double qlength_inv = 1.0/(sqrt(q->w*q->w + q->x*q->x + q->y*q->y + q->z*q->z));
  58. qn->w = q->w * qlength_inv;
  59. qn->x = q->x * qlength_inv;
  60. qn->y = q->y * qlength_inv;
  61. qn->z = q->z * qlength_inv;
  62. }
  63. void _CPPYIN_Math::QuatUnitInverse(QUAT_PTR q, QUAT_PTR qi) // 单位四元数的逆,等于求共轭
  64. {
  65. qi->w =  q->w;
  66. qi->x = -q->x;
  67. qi->y = -q->y;
  68. qi->z = -q->z;
  69. }
  70. void _CPPYIN_Math::QuatInverse(QUAT_PTR q, QUAT_PTR qi) // 非单位四元数的逆
  71. {
  72. double norm2_inv = 1.0 / (q->w * q->w + q->x * q->x + q->y * q->y + q->z * q->z);
  73. qi->w =  q->w * norm2_inv;
  74. qi->x = -q->x * norm2_inv;
  75. qi->y = -q->y * norm2_inv;
  76. qi->z = -q->z * norm2_inv;
  77. }
  78. void _CPPYIN_Math::QuatMul(QUAT_PTR q1, QUAT_PTR q2, QUAT_PTR qprod)
  79. {
  80. double prd_0 = (q1->z - q1->y) * (q2->y - q2->z);
  81. double prd_1 = (q1->w + q1->x) * (q2->w + q2->x);
  82. double prd_2 = (q1->w - q1->x) * (q2->y + q2->z);
  83. double prd_3 = (q1->y + q1->z) * (q2->w - q2->x);
  84. double prd_4 = (q1->z - q1->x) * (q2->x - q2->y);
  85. double prd_5 = (q1->z + q1->x) * (q2->x + q2->y);
  86. double prd_6 = (q1->w + q1->y) * (q2->w - q2->z);
  87. double prd_7 = (q1->w - q1->y) * (q2->w + q2->z);
  88. double prd_8 = prd_5 + prd_6 + prd_7;
  89. double prd_9 = 0.5 * (prd_4 + prd_8);
  90. qprod->w = prd_0 + prd_9 - prd_5;
  91. qprod->x = prd_1 + prd_9 - prd_8;
  92. qprod->y = prd_2 + prd_9 - prd_7;
  93. qprod->z = prd_3 + prd_9 - prd_6;
  94. }
  95. void _CPPYIN_Math::QuatMul3(QUAT_PTR q1, QUAT_PTR q2, QUAT_PTR q3, QUAT_PTR qprod) // 三个四元数相乘,用于做旋转变换
  96. {
  97. QUAT qtmp;
  98. QuatMul(q1, q2, &qtmp);
  99. QuatMul(&qtmp, q3, qprod);
  100. }

转-D3D中的四元数相关推荐

  1. D3D中简单的截图方法 (转)

    [ZT]D3D中简单的截图方法 试了下,果然可以. 在渲染完所有东东后(Present之前) 获得BackBuffer表面 然后用D3DX的函数保存 void ScreenShot (char *fi ...

  2. D3D中材质通过D3DMATERIAL9结构说明

    光照的两大要素是光源和物体的材质,物体表面材质属性决定了它能反射什么颜色的光线以及反射多少光线. 一.Direct3D中材质的定义 D3D中材质通过D3DMATERIAL9结构来表示: typedef ...

  3. D3D中2D图片的绘制

    想要在D3D中加载2D图片可以使用如下两种方法(我只想到这两种方法,如果有其他方法,请指教).第一种就是把图片以纹理方式加载,然后以此为纹理绘制一个四边形即可:第二种就是使用2D点精灵,不解释,你懂得 ...

  4. ROS TF2 中的 四元数 基础部分

    ROS TF2 中的 四元数 基础部分 1.四元数的组成 2.将 RPY坐标系 下的 角度 转换为 四元数 3.如何通过四元数 做 旋转 4.四元数转置 5.求两个姿态(四元数)的旋转 5. 完毕 这 ...

  5. D3D中2D图片的绘制两种方法

    2014/09/19 (转载自:http://blog.csdn.net/rabbit729/article/details/6388703) 想要在D3D中加载2D图片可以使用如下两种方法(我只想到 ...

  6. 计算机图形学中的四元数(Quaternions)

    计算机图形学中的四元数 计算机图形学中的四元数(Quaternions) 前言 欧拉角 欧拉角的万向节死锁(Gimbal Lock)与插值问题 四元数 参考 计算机图形学中的四元数(Quaternio ...

  7. D3D中的纹理贴图(1)

    D3D中的纹理贴图(1) 提示: 阅读本文需要一定的3D图形学和DirectX9基础,如果你发现阅读困难,请参阅 D3D中基本三角形面的绘制. 本文用到的坐标系统变换函数请参阅 DirectX 9的坐 ...

  8. 在D3D中绘制一个三角形的一个完整过程(学习3D游戏笔记一)

    本笔记是基于Microsfot DirectX 9.0 SDK Update的SimpleSample生成的框架. 在3D场景中,所有的对象和模型均由三角形构成;而三角形由三个顶点构成;每个顶点不仅包 ...

  9. D3D中的网格(Mesh)

    该教程基于DirectX 8.0 Graphics, 一部分内容由DirectX 8.0 SDK 英文文档翻译而来,一部分是自己工作经验的总结,作者对此享有著作权,读者可任意拷贝和传播,但不包含商业的 ...

最新文章

  1. php博客浏览人数,在博客中实现浏览次数的统计
  2. Ivanti 洞察职场新趋势:71% 的员工宁愿放弃升职也要选择随处工作
  3. 好的架构是有价值观的
  4. 影像组学视频学习笔记(25)-查看准确度、灵敏度、特异度及混淆矩阵、Li‘s have a solution and plan.
  5. open(/dev/ietctl, O_RDWR) 参数含义
  6. 科普MinGW MinGW-W64
  7. edg击败we视频_厂长在EDG的地位有多高?阿布爆料:团队会无条件支持他的决定...
  8. 《论语》读后颜渊第十二主要大意
  9. 贝叶斯机器学习:经典模型与代码实现
  10. dz论坛服务器技术支持,服务器更换 升级 dz论坛首页 dz其他页面打开为空白解决方法...
  11. C语言中的二进制、八进制和十六进制的表示
  12. 嵌入式开发板02---点亮LED升级版
  13. oracle 区管理系统,oracle区管理和段空间管理详细介绍
  14. 热敏打印机原理java_java - 使用TSC TTP-342E Pro热敏打印机Java打印 - 堆栈内存溢出...
  15. 管理后台--4,删除分类
  16. 【读书笔记《Android游戏编程之从零开始》】4.Android 游戏开发常用的系统控件(EditText、CheckBox、Radiobutton)
  17. hmssdk php,接入华为推送,开发流程, 与遇到的坑(PUSH SDK 和 HMS SDK)
  18. 乔治城大学计算机专业,美国乔治城大学计算机
  19. tkinter的图片格式
  20. ServiceMesh实战-服务网格是什么?

热门文章

  1. 再谈 Promise
  2. JavaScript学习笔记——对象知识点
  3. Lua 学习笔记(一)环境搭建
  4. 通用寄存器的专门用途
  5. 使用beanUtils操纵bean的属性
  6. 算法:数值的整数次方
  7. UITextField与UITextView的区别
  8. Android获取cpu和内存信息、网址的代码
  9. debian6 xen4.0安装 guest半虚拟化--tar安装
  10. gnome3.2 安装体验