




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、OpenGL ES 从零开始系列9(完结):四元数在进入下一篇关于骨骼动画的文章之前,让我们先花点时间来了解一个马上会使用到的新数据类型:四元数译者注:关于四元数的概念可以参考这个链接:点我。我们用四元数存储单一骨骼在3个轴线上的旋转信息,换句话说,存储的是骨骼指向的方向。在下一部分介绍的仿真骨骼动画中,你将会看到,模型的顶点是同一个或多个骨骼相关联的,当骨骼移动时它们也会随之变化。相对于将欧拉角信息存储在3个GLfloats变量或一个 Vector3D 变量里来说, 使用四元数有2个优点:1.四元数不会造成万向节死锁(gimbal lock),但是欧拉角容易造成万向节死锁,使用四元数能够让我
2、们的3D模型能够全方位的移动。2.相比于给每个欧拉角做矩阵旋转转换计算,使用四元数结合多角度旋转可以显著的减少计算量。从某些方面来看,四元数极其复杂且难于理解。它们是高级数学:完全疯狂的符咒。幸运的是,你不需要完全理解它们背后的数学含义。但是,我们现在需要使用它们来完成骨骼动画,所以还是值得我们花费些时间来讨论下它们的概念和怎么使用它们。Discovery探索从数学上讲,四元数是复数的一个扩展延伸,于1843年由Sir William Rowan Hamilton 发现。技术上讲,四元数表现为实数之上的4维正规可除代数。Zoiks!更简单的讲,四元数被认为是第四维度用来计算笛卡尔坐标
3、中的3个坐标值。好吧,一切可能不那么简单,对吧?先别怕,如果你不精通高等数学,四元数可能会让你头疼。但是,如我之前所说,如果你只是使用它们,完全不必深入了解。这玩意和你见过的一些概念是非常类似的。不知你是否还能想起我们在3维空间里涉及到的4X4矩阵的矩阵转换。当我们使用已转换的数据的时候,忽略了第4个值。我们可以把这里的第四个值当成四元数,为计算提供了一个位置。数学范畴内,请不要跟我说过度简化有助于凡人在四元数世界里占有一席之地,有所作为。四元数在探索时代里被认为是相当创新的,但最繁荣的时期却如此短暂。在1880中期,向量微积分开始在计算领域取代四元数理论,因为它用了一种更为容易理解和描述的概
4、念描述了同样的现象。Not Quite Dead Yet!虽死犹生但在20世纪,四元数又重新获宠。正如我们在part 7里讨论的,有一个被称为gimbal lock 的现象,当你在每个轴线单独做旋转转换的时候就会发生,此现象的危害就是可能导致在三个轴中的一个轴上停止旋转。尽管事实是四元数源于复数和理论数学,但它们都有实际应用。其中一个实际应用是三轴线上旋转角的展现。由于四元数用四个维度展示了笛卡尔(或三轴)旋转,此展现不会导致gimbal lock,而且你可以在四元数和旋转矩阵之间,四元数和欧拉角之间进行无损转换。这使得存储某些对象的旋转信息相当完美,比如。骨骼框架中的单独骨骼?不需要存贮3轴
5、的角信息,而是存储一个单独的四元数。四元数和矩阵一样,可以相乘,且存储于不同四元数中的旋转值通过相乘来合并计算。四元数乘积和2个旋转矩阵乘积的结果是完全一样的,考虑到减少计算量,这意味着除了要避免gimbal lock,还要减少每次程序循环运行的FLOPS(每秒浮点运算次数)。和矩阵乘法相比,四元数乘法不仅步骤少,而且可以通过一个四元数表达3轴所有数据。如果通过Vector3D 或3个GLfloats来存储旋转信息,我们经常不得不做3次矩阵乘法每轴都要算一次。结论是,通过把存储旋转的独立角信息存为四元数,可以带来可观的性能提升。 The Quaternion Struct 四元数结构
6、体从数据上看来,四元数只不过是比Vector 3D多加了一个GLfloat,经常把它当成w字段。所以对我们来说一个四元数就象这样:typedef struct GLfloat x; GLfloat y; GLfloat z; GLfloat w; Quaternion3D;Normalizing a Quaternion 四元数归一化这个十分简单。四元数代表空间里的一个方向,就像Vector3Ds,实际距离的值并不在意,并且在进行一些计算之前之后使他们正常下降到1.0。完成这些我们可以这样做:st
7、atic inline void Quaternion3DNormalize(Quaternion3D *quaternion) GLfloat magnitude; magnitude = sqrtf(quaternion->x * quaternion->x) + (qu
8、aternion->y * quaternion->y) + (quaternion->z * quaternion->z) +
9、160; (quaternion->w * quaternion->w); quaternion->x /= magnitude; quaternion->y /= magnitude; quaternion->z /= magnitude; quaternion->w /= magnitude;Creating a Quaternion from a Rotation matrix 从一个旋转矩阵中创建一个四元数&
10、#160;如果我们没法在四元数同其他的对象转换过程中保存旋转角度信息的话,四元数对我们来说还是没用的。首先我们从一个旋转矩阵中创建一个四元数。代码如下: static inline Quaternion3D Quaternion3DMakeWithMatrix3D(Matrix3D matrix) Quaternion3D quat; GLfloat trace, s; trace = matrix0 + matrix5 + matrix10; if (trace
11、> 0.0f) s = sqrtf(trace + 1.0f); quat.w = s * 0.5f; s = 0.5f / s; quat.x = (matrix9 - matrix6) * s;
12、; quat.y = (matrix2 - matrix8) * s; quat.z = (matrix4 - matrix1) * s; else NSInteger biggest; enum
13、160; A,E,I; if (matrix0 > matrix5) if (matrix10 > matrix0) biggest = I; &
14、#160; else biggest = A; else if (matrix10 > matrix0)
15、; biggest = I; else biggest = E; swi
16、tch (biggest) case A: s = sqrtf(matrix0 - (matrix5 + matrix10) + 1.0f);
17、 if (s > QUATERNION_TRACE_ZERO_TOLERANCE) quat.x = s * 0.5f;
18、160; s = 0.5f / s; quat.w = (matrix9 - matrix6) * s;
19、160; quat.y = (matrix1 + matrix4) * s; quat.z = (matrix2 + matrix8) * s;
20、 break; s = sqrtf(matrix10 - (matrix0 + matrix5) + 1.0f);
21、160; if (s > QUATERNION_TRACE_ZERO_TOLERANCE) quat
22、.z = s * 0.5f; s = 0.5f / s; quat.w = (matrix4 - matrix1) * s;
23、0; quat.x = (matrix8 + matrix2) * s; quat.y = (matrix9 + matrix6) * s;
24、160; break; s = sqrtf(matrix5 - (matrix10 + matrix0) + 1.0f);
25、0; if (s > QUATERNION_TRACE_ZERO_TOLERANCE) &
26、#160; quat.y = s * 0.5f; s = 0.5f / s; quat.w = (matrix2 - matrix8) * s;
27、 quat.z = (matrix6 + matrix9) * s; quat.x = (matrix4 + matrix1) * s;
28、0; break; break; &
29、#160; case E: s = sqrtf(matrix5 - (matrix10 + matrix0) + 1.0f);
30、60; if (s > QUATERNION_TRACE_ZERO_TOLERANCE) quat.y = s * 0.5f
31、; s = 0.5f / s; quat.w = (matrix2 - matrix8) * s;
32、; quat.z = (matrix6 + matrix9) * s; quat.x = (matrix4 + matrix1) * s;
33、60; break; s = sqrtf(matrix10 - (matrix0 + matrix5) + 1.0f);
34、; if (s > QUATERNION_TRACE_ZERO_TOLERANCE)
35、160; quat.z = s * 0.5f; s = 0.5f / s; quat.w = (matrix4 - matrix1) * s; &
36、#160; quat.x = (matrix8 + matrix2) * s; quat.y = (matrix9 + matrix6) * s;
37、; break; s = sqrtf(matrix0 - (matrix5 + matrix10) + 1
38、.0f); if (s > QUATERNION_TRACE_ZERO_TOLERANCE)
39、0; quat.x = s * 0.5f; s = 0.5f / s; quat.w = (matrix9 - matr
40、ix6) * s; quat.y = (matrix1 + matrix4) * s; quat.z = (matrix2 + matrix8) * s; &
41、#160; break; break;
42、0; case I: s = sqrtf(matrix10 - (matrix0 + matrix5) + 1.0f);
43、 if (s > QUATERNION_TRACE_ZERO_TOLERANCE) quat.z =
44、 s * 0.5f; s = 0.5f / s; quat.w = (matrix4 - matrix1) * s;
45、160; quat.x = (matrix8 + matrix2) * s; quat.y = (matrix9 + matrix6) * s;
46、 break; s = sqrtf(matrix0 - (matrix5 + matrix10) + 1.0f);
47、160; if (s > QUATERNION_TRACE_ZERO_TOLERANCE)
48、; quat.x = s * 0.5f; s = 0.5f / s; quat.w = (matrix9 - matrix6) * s;
49、0; quat.y = (matrix1 + matrix4) * s; quat.z = (matrix2 + matrix8) * s;
50、160; break; s = sqrtf(matrix5 - (matrix10 + mat
51、rix0) + 1.0f); if (s > QUATERNION_TRACE_ZERO_TOLERANCE) &
52、#160; quat.y = s * 0.5f; s = 0.5f / s; quat.w = (matri
53、x2 - matrix8) * s; quat.z = (matrix6 + matrix9) * s; quat.x = (matrix4 + matrix1) *
54、s; break; break; &
55、#160; default: break;
56、return quat;好的,如果你想真正知道这里是怎么工作的。你需要了解矩阵运算、欧拉旋转理论、特征值和纹理。更不用说理解旋转在矩阵里的表示方法。当你完成你的数学博士学位,你可以回过头用它来解释剩余的usl。你会发现这个函数中使用的算法都是Matrix的FAQ上用伪代码列出来的。 Creating a Rotation Matrix from a Quaternion 从一个四元数中创建旋转矩阵这另外的一个方法相对简单些。并且这个基本算法来自于Matrix FAQ,虽然我需要把它转换成行优先的顺序。static inline void Matrix3DSetUsingQuatern
57、ion3D(Matrix3D matrix, Quaternion3D quat) matrix0 = (1.0f - (2.0f * (quat.y * quat.y) + (quat.z * quat.z); matrix1 = (2.0f * (quat.x * quat.y) - (quat.z * quat.w); matrix2 = (2.0f * (quat.x * quat.z) + (quat.y * quat.w); matrix3 = 0.0
58、f; matrix4 = (2.0f * (quat.x * quat.y) + (quat.z * quat.w); matrix5 = (1.0f - (2.0f * (quat.x * quat.x) + (quat.z * quat.z); matrix6 = (2.0f * (quat.y * quat.z) - (quat.x * quat.w); matrix7 = 0.0f; matrix8 = (2.0f *
59、(quat.x * quat.z) - (quat.y * quat.w); matrix9 = (2.0f * (quat.y * quat.z) + (quat.x * quat.w); matrix10 = (1.0f - (2.0f * (quat.x * quat.x) + (quat.y * quat.y); matrix11 = 0.0f; matrix12 = 0.0f; matrix13 = 0.0f;
60、0; matrix14 = 0.0f; matrix15 = 1.0f;Converting an Angle and Axis of Rotation to a Quaternion 把一个角度和旋转轴转换成一个四元数四元数可以做的另外一种转换是,表示成在一个Vector3D表示的轴线上进行旋转。这在骨骼动画里面是非常有用的,因为这种表现形式通过矩阵是很难做到的。创建一个基于角度和轴旋转得四元数,我们可以这样做:static inline Quaternion3D Quaternion3DMakeWithAxisAndAngle(Vector3D axis, GLfl
61、oat angle) Quaternion3D quat; GLfloat sinAngle; angle *= 0.5f; Vector3DNormalize(&axis); sinAngle = sinf(angle); quat.x = (axis.x * sinAngle); quat.y = (axis.y * sinAngle); quat.z =
62、(axis.z * sinAngle); quat.w = cos(angle); return quat;Extracting an Angle and Axis of Rotation from a Quaternion 从一个四元数中检测角度和轴得旋转反过来,我们也可以从四元数中取得旋转的数据,包括旋转角度和深度,就像这样static inline void Quaternion3DExtractAxisAndAngle(Quaternion3D quat, Vector3D *axis, GLfloat *angl
63、e) GLfloat s; Quaternion3DNormalize(&quat); s = sqrtf(1.0f - (quat.w * quat.w); if (fabs(s) < 0.0005f) s = 1.0f; if (axis != NULL) axis->x = (quat.x / s);
64、60; axis->y = (quat.y / s); axis->z = (quat.z / s); if (angle != NULL) *angle = (acosf(quat.w) * 2.0f);Quaternion Multiplication 四元数乘法为了合并两种不同形式的四元数中得3D旋转信
65、息。我们只需要让他们彼此相乘。好了继续我们得代码static inline void Quaternion3DMultiply(Quaternion3D *quat1, Quaternion3D *quat2) Vector3D v1, v2, cp; float angle; v1.x = quat1->x; v1.y = quat1->y; v1.z = quat1->z; v2.x = quat2->
66、;x; v2.y = quat2->y; v2.z = quat2->z; angle = (quat1->w * quat2->w) - Vector3DDotProduct(v1, v2); cp = Vector3DCrossProduct(v1, v2); v1.x *= quat2->w; v1.y *= quat2->w; v1.z *= quat2->
67、;w; v2.x *= quat1->w; v2.y *= quat1->w; v2.z *= quat1->w; quat1->x = v1.x + v2.x + cp.x; quat1->y = v1.y + v2.y + cp.y; quat1->z = v1.z + v2.z + cp.z; quat1->w = angle;Inverting a Qua
68、ternion 四元数转置我们通过做一个四元数的共轭运算来取得四元数的转置。四元数做共轭运算其实就是将四元数中表示向量(x,y,z)的值取反。在这里的实现中,我们把它四元数转置计算作为四元数标准计算的一部分,而不是一个独立的步骤:static inline void Quaternion3DInvert(Quaternion3D *quat) GLfloat length = 1.0f / (quat->x * quat->x) +
69、0; (quat->y * quat->y) + (quat->z * quat->z) +
70、160; (quat->w * quat->w); quat->x *= -length; quat->y *= -length; quat->z *= -length; quat->w *= length;Creating a Quaternion from Euler Angles 从欧拉角中创建四元数前面我说过在旋转中最好不要使用欧拉角,但是有时候我们需要将欧拉角转换成四元数,比如说用户
71、输入的信息是欧拉角信息。转换的步骤是,将欧拉轴用Vector3D表示出来,然后将Vector3D的值转换成四元数,最后将四元数相乘来得到结果:static inline Quaternion3D Quaternion3DMakeWithEulerAngles(GLfloat x, GLfloat y, GLfloat z) Vector3D vx = Vector3DMake(1.f, 0.f, 0.f); Vector3D vy = Vector3DMake(0.f, 1.f, 0.f); Vector3D vz = V
72、ector3DMake(0.f, 0.f, 1.f); Quaternion3D qx = Quaternion3DMakeWithAxisAndAngle(vx, x); Quaternion3D qy = Quaternion3DMakeWithAxisAndAngle(vy, y); Quaternion3D qz = Quaternion3DMakeWithAxisAndAngle(vz, z); Quaternion3DMultiply(&qx,
73、 &qy ); Quaternion3DMultiply(&qx, &qz ); return qx;SLERPs and NLERPS最后,最重要的部分来了:SLERPS 和 NLERPS。SLERPS 是 球面线性插值 的缩写,NLERPS是 归一化线性插值 的缩写,还记得我们在“Part 9a”是怎样计算 代数的 线性插值 的吗,我们由此计算出了每一个依赖于 过去了多长时间的点的准确位置。但是,在插值四元法中 通过这样的 计算我们得不到想要的结果。想象一个球和一个窝关节图:知道哪里用得到这样的球窝关节吗?
74、在你的 胳膊肘 的地方。当然这只是为了描叙方便。站在镜子的前面,将你的胳膊举过头顶,并保持你的胳膊竖直,然后将胳膊放下,放到你身体的一侧。你的胳膊不是直线运动的,对吧?你的手是弧线运动的,并且比你的肘关节和你的手臂运动的要快,很有可能你的手并不是以一个恒定的速度运动。这就是我们在做动画旋转角 时想要模拟的基本的转动方式。球面线性插值 和 归一化线性插值 是两种最通用的方法,球面线性插值 更常用,并且对现实提供了一个更好的模拟,但是它更慢,需要的配置高。归一化线性插值 更快,因为它和我们以前用过的线性插值本质上是一样的,仅仅是插值后作归一化,这个完全可以从“归一化线性插值”这个名字中体味出来。当动画效果比对速度的要求更严格时,使用球面线性插值,否则就用 归一化线性插值,这两种方法完全相同的参数,在这两种方法之间转换
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025年开封运输从业资格证考试技巧
- 农业设备供货合同范本
- 劳动合同范本大全
- 2025年运城货运上岗证模拟考试试题
- 专利侵权检索合同范本
- 买卖废钢材合同范本
- 劳务合同范本无社保
- 临泉教师合同范本
- 三人合作协议合同范本
- 交电产品销售合同范例
- 2025包头青山宾馆有限公司面向社会公开招聘18人笔试参考题库附带答案详解
- 课件-DeepSeek从入门到精通
- 2025年业务员工作总结及工作计划模版(3篇)
- 2025至2030年中国毛绒卡通玩具数据监测研究报告
- 2024年连云港市赣榆区区属国企对外招聘笔试真题
- 海南省海口市2024-2025学年八年级上学期期末考试数学试题(含答案)
- 2025年度智能充电桩场地租赁合同范本3篇
- 2025年注射用赖氮匹林项目可行性研究报告
- 17J008挡土墙(重力式、衡重式、悬臂式)图示图集
- 药品类体外诊断试剂专项培训课件
- 心电监护仪的操作及注意事项 课件
评论
0/150
提交评论