Unity 四元数

简介: Unity 四元数

前言:在场景中,可以用旋转工具改变物体角度,也可以在Inspector窗口中改变物体的X、Y、Z值(欧拉角)来改变物体角度。

虽然用欧拉角表示角度和旋转,但一般人想不到,物体在三维空间的旋转并不是一个简单问题,用3个角度表示是远远不够的


一、万向节锁定

要说明三维物体旋转的复杂性,从"万向节锁定"这一问题入手最有说服力。

上图为欧拉角示意图。虽然可以调整欧拉角到任意角度,但欧拉角的3个轴并不是独立的,x,y,z轴之间存在嵌套结构,如上图所示,这种嵌套结构被称为"万向节",意思是可以旋转到任意角度的关节。

当中层轴旋转时,会带动内层的轴跟着旋转。通常三维软件的欧拉角,从外层到哪层是按照y->x->z轴的顺序,也就是说x轴的旋转会影响z轴

这里介绍一下用Unity怎样让万向节锁定

  1. 在场景中新建一个Cube
  2. 用鼠标在Inspector窗口中朝向的x、y、z这三个字母上拖拽,不要在场景中用鼠标直接旋转物体,这样可以看到3个轴分别是如何旋转的
  3. 将x的旋转改为90,y和z的旋转改为0
  4. 用鼠标在旋转的y和z上拖拽,修改y和z的旋转。这是会发现,绕y和绕z旋转方向变成一样了,缺少了一个自由度

按照上述步骤操作,会顺利进入"万向节锁定"状态。一旦进入此状态,物体旋转就会受限制。如果游戏系统是基于欧拉角设计的,那么在主角会俯仰运动的游戏,万向节锁定问题会显得非常严重,而且在其他类型有种也会带来各种问题。

值得说明的是,Unity内部使用四元数比哦时物体的旋转,这里并不会真的遇到锁定问题,只是在编辑器界面上模拟了万向节锁定的效果。

如果用循转工具在场景里直接旋转物体,就跳过了编辑窗口的限制,物体仍然可以自由旋转

到这里,你们可能还没有完全理解3D旋转的奥秘哦,但它具有的复杂性已经毋庸置疑了。幸运的事,数学家哈密顿提出的"四元数"能够让我们跨越欧拉角,彻底解决旋转难题

二、四元数的概念

四元数包含一个标量分量和一个三维向量分量,四元数Q可以记作:

Q=[w,(x,y,z)]。

下面给出四元数的公式定义。对于旋转轴为n,旋转角度为ß的旋转,如果用四元数表示,则四个分量分别为:

  • w=cos(ß/2)
  • x=sin(ß/2)cos(∂x)
  • y=sin(ß/2)cos(∂y)
  • z=sin(ß/2)cos(∂z)

用四元数表示旋转一旦也不知管,4个分量w、x、y、z与绕各轴的旋转角度并没有直接的对应关系。在实际开发中也不要试图获取和修改某一份量,应当只做整体处理。

前文提到,矩阵也可以表示旋转,而且矩阵也不存在万向节锁定的问题。其实,旋转还可以用欧拉角和四元数表示,但么一种都有自己的优缺点,下面对这三种方式进行对比

 

欧拉角

矩阵

四元数

旋转一个位置点 

不支持

支持

不支持

增量旋转

不支持

支持,运算量大

支持,运算量小

平滑差值

支持(存在问题)

基本不支持

支持

内存占用

3个浮点数

16个数值

4个浮点数

表达式是否唯一

无数种

唯一

互为负的两种表示

潜在问题

欧拉角锁定

矩阵蠕变

误差累计

实际应用中应根据优缺点来应用,并尽可能在引擎层面进行统一,尽量减少开发时的问题

小提示:Unity内部旋转使用四元数,即结构体Quaternion表示的,但在街面上很多地方会转为更直观的偶来奥,以便直接指定旋转的角度,这种方式兼顾了准确性和便利性

三、Quaternion结构体

这部分介绍Quaternion的属性和方法

属性:

属性

说明

x

四元数x的分量,不应直接修改

y

同x

z

同x

w

同x

this[int index]

允许通过下标运算符访问x、y、z、w分量。例如[1]可以访问y

eulerAngles

获得对应的欧拉角

identity

获得无旋转的四元数

方法和运算符:

方法

说明

ToAngleAxis

将旋转转换为一个轴和一个角度的形式

SetFromToRotation

与FromToRotation类似,但是直接修改当前四元数对象

SetLookRotation

与LookRotation类似,但是直接修改当前四元数对象

*

四元数相乘,代表依次旋转的操作

==

判断四元数是否相等

!=

判断四元数是否不等

Dot

两个旋转点乘

AngleAxis

根据一个轴和角度获得一个四元数

FromToRotation

获得一个四元数,代表从from到to向量的旋转

LookRotation

给定前方和上方向量,获得一个旋转

Slerp

插值,根据比例在两个四元数之间进行球面插值

Lerp

插值,根据比例在两个四元数之间进行插值并将结果规范化

RotateTowards

将旋转from变到旋转to

Inverse

返回四元数的逆

Angle

返回两个旋转之间的夹角

Euler

转换为对应的欧拉角

四、理解和运用四元数

在前文中,详细介绍了"位置"与"向量"的区别。下面引出与之对应的另一对概念——"朝向"与"旋转"。

朝向和位置都是一种状态,旋转和向量指的是变化量

位置和向量都用Vector3表示,位置和朝向都用四元数表示

理解了Vector3加法的意义,就可以类比出四元数的旋转操作,只是加法变成了乘法

下表列出四元数乘法的含义

元素1

运算符

元素2

一般含义

朝向

*

朝向

意义不明

朝向

*

旋转

从某个朝向开始,旋转一个角,得到新的朝向

旋转

*

旋转

组合两次旋转,得到合并的结果

与向量不同的是,四元数相乘不满足交换律,这也印证了三维空间中物体的旋转不是一个简单的问题。

有意思的是,四元数乘法可以直接作用于向量,这样就可以方便地旋转向量了:

旋转*向量:将向量旋转一个角,得到新的向量

注意:四元数乘以向量时,必须四元数在前,向量在后。

五、四元数的插值

前文通过介绍"万向节锁定",解释了使用四元数的必要性,其实四元数还有另一个大用,就是做动画与插值。

物体的转向动画应该是直接的、均匀的,不能给人"绕远"的感觉。

几何上,在球面上的两点之间移动,沿"大圆"的路径是最短的。大圆的定义是"过球心的平面与球面相交形成的圆",换句话说就是能在球面上画出的最大的那一类圆。

两个朝向之间的旋转动画,当旋转的路径符合大圆时,旋转路径是最短的,看起来也最舒服。

欧拉角在制作旋转动画时具有很大缺陷。如果旋转较为复杂,x、y、z轴都有旋转时,物体不会按照"大圆"的路径运动,得到的结果会很奇怪。矩阵在表现旋转动画时,也很难计算正确的插值点。

而四元数就很擅长插值运算。Unity提供了Quaternion.Slerp()方法,用于旋转的插值。其函数定义如下

Static Quaternion Slerp(Quaternion a,Quaternion b,float t);//t取0到1之间的值

六、朝向与向量

朝向代表空间中的一个方向,而向量也可以表示一个方向,因此有时也可以用向量直接表示物体的朝向。

我们不仅可以通过transform.forward属性获得只想物体前方的单位向量,而且可以直接设置它以改变物体的朝向。

向量可以代表朝向,四元数也可以代表朝向,那么向量也就可以转化为四元数。向量转化为四元数的方法定义如下:

Quaternion Quaternion.LookRotation(Vector3 forward) ;

Quaternion Quaternion.LookRotation(Vector3 forward,Vector3);

Quaternion.LookRotation提供了直接获得朝向的一种方法。

仔细思考会发现——难道四元数可以用向量代替吗?应该不行,如果能替代还要四元数干啥。其实向量有很大的局限性,例如,一个人躺在床上,面朝上,前方是世界坐标系上方,这时,他可以在保持面朝上的情况下在床上水平旋转,让头部朝东朝西。而无论头朝哪里,前方都可以保持向上。

这说明了代表某个向量方向的四元数不是唯一的。如果只用一个向量代表前方,理论上有无数种四元数都指向该方向,但它们的"上方"不同。如果仅指定前方,即使使用LookRotation方法只有一个参数的形式,Unity也会用某种规则假定一个合适的"上方";而如果要精确地控制,则要用Quaternion的另一种形式,用两个参数分别制定前方向量和上方向量,这样就可以得到更符合要求的结果


相关文章
|
监控 安全 Java
Java多线程调试技巧:如何定位和解决线程安全问题
【4月更文挑战第6天】本文探讨了Java并发编程中的线程安全问题,包括数据不一致、死锁和性能下降。为解决这些问题,文章介绍了理解线程安全的重要性,如互斥、同步和避免死锁,并提供了识别问题的迹象和调试工具,如JConsole、VisualVM、堆栈跟踪和Thread Dump分析。此外,还建议使用原子类、线程安全数据结构和静态代码分析工具来加强同步和减少锁粒度。最后,强调了避免共享状态和合理设计的重要性,以确保多线程程序的正确性和效率。
449 2
|
2月前
|
安全 网络安全 数据安全/隐私保护
解决SSH测试连接GitHub时出现“connection closed by remote host”的问题。
然后使用 `ssh -T git@ssh.github.com`来测试连接。
310 0
|
11月前
|
传感器 缓存 网络协议
CoAP 协议与 HTTP 协议的区别
CoAP(Constrained Application Protocol)协议是为资源受限的设备设计的轻量级协议,适用于物联网场景。相比HTTP,CoAP具有低功耗、低带宽占用和简单易实现的特点,支持多播通信和无连接的交互模式。
|
12月前
Px,em,rem的区别
【10月更文挑战第10天】 Px,em,rem的区别
947 2
|
12月前
|
存储 前端开发 数据库
一文搞懂SaaS应用架构:应用服务、应用结构、应用交互设计
【10月更文挑战第21天】本文介绍了 SaaS 应用服务的多租户服务、安全服务和更新与维护服务,以及 SaaS 应用的前后端结构和交互设计。多租户服务涉及数据隔离和资源分配;安全服务包括身份认证与授权及数据安全;更新与维护服务涵盖版本管理和技术支持。前端结构关注用户界面设计和前端技术选型;后端结构则涉及微服务架构和数据库管理。交互设计强调租户与应用的交互和应用内部模块间的交互。
938 0
|
持续交付 C# 敏捷开发
“敏捷之道:揭秘WPF项目中的快速迭代与持续交付——从需求管理到自动化测试,打造高效开发流程的全方位指南”
【8月更文挑战第31天】敏捷开发是一种注重快速迭代和持续交付的软件开发方法,通过短周期开发提高产品质量并快速响应变化。本文通过问题解答形式,探讨在Windows Presentation Foundation(WPF)项目中应用敏捷开发的最佳实践,涵盖需求管理、版本控制、自动化测试及持续集成等方面,并通过具体示例代码展示其实施过程,帮助团队提升代码质量和开发效率。
203 0
|
开发工具 图形学 iOS开发
Unity与IOS⭐Unity接入IOS SDK的流程图
Unity与IOS⭐Unity接入IOS SDK的流程图
|
JavaScript 前端开发 开发者
介绍如何在WebStorm中调试JavaScript文件
介绍如何在WebStorm中调试JavaScript文件
472 1
|
数据可视化 vr&ar Python
时间序列分析技巧(二):ARIMA模型建模步骤总结
时间序列分析技巧(二):ARIMA模型建模步骤总结
|
JSON 资源调度 负载均衡
这可能是你见过最全的Node.js应用程序管理与部署:使用PM2进行进程管理
node是单线程应用,单线程最大的弊端就是无法利用多核CPU带来的优势来提升运行效率。 pm2(process manager)是一个进程管理工具,可以用它来管理node进程,负责所有的node进程,并查看node进程的状态,也支持性能监控,负载均衡等功能。
1108 0
这可能是你见过最全的Node.js应用程序管理与部署:使用PM2进行进程管理