Unity 四元数

news2024/10/6 22:24:39

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

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


一、万向节锁定

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

6bba4e32e16d4a0daab389baeb912978.png

上图为欧拉角示意图。虽然可以调整欧拉角到任意角度,但欧拉角的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的另一种形式,用两个参数分别制定前方向量和上方向量,这样就可以得到更符合要求的结果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/529799.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

TouchGFX开发(3)----触摸屏幕组件点亮LED

TouchGFX开发.3----触摸屏幕组件点亮LED 概述生成例程配置时钟树开启调试接口移植SSD1306配置调试开启TouchGFX设置屏幕刷新率配置TouchGFXTouchGFX代码配置编译实际效果 概述 TouchGFX是一种先进的软件框架,用于开发嵌入式图形界面(GUI)。借助其特性,…

一个非奇异快速终端滑模控制(NTSM)实例及仿真

一、被控对象 考虑这么一个被控对象 J θ ( t ) u ( t ) d ( t ) J \ddot\theta(t) u(t) d(t) Jθ(t)u(t)d(t) 其中&#xff0c; J J J 为转动惯量&#xff0c; θ \theta θ 为角度&#xff0c; u u u 为控制量&#xff0c; d d d 为扰动&#xff0c;且 d ( t ) < …

vue diff算法与虚拟dom知识整理(7) 根据init.ts源码简单梳理patch都做了些什么

之前我们也见证了 diff算法 的强大 但他 只有确认是同一个节点才做对比 如果不是就直接暴力拆卸了 我们打开我们的案例 找到 node_modules 下面的snabbdom/src下面的 init.ts文件 init.ts 拉到最下面 我们就可以看到这个返回的patch函数 patch相比于他的功能 代码算比较少的…

LeetCode高频算法刷题记录1

文章目录 1. 无重复字符的最长子串【中等】1.1 题目描述1.2 解题思路1.3 代码实现 2. 反转链表【简单】2.1 题目描述2.2 解题思路2.3 代码实现 3. LRU 缓存【中等】3.1 题目描述3.2 解题思路3.3 代码实现 4. 数组中的第K个最大元素【中等】4.1 题目描述4.2 解题思路4.3 代码实现…

吴恩达OpenAI最新课程:prompt-engineering-for-developers读书笔记

文章目录 一、前言二、Prompt编写原则2.1 环境配置2.2 编写清晰、具体的指令2.2.1 使用分隔符2.2.2 结构化输出&#xff08;JSON、HTML等&#xff09;2.2.3 要求模型检查条件是否满足2.2.4 提供少量示例&#xff08;Few-shot Prompting&#xff09; 2.3 指导模型思考2.3.1 指定…

未来工业维护:探索数据分析与机器学习的融合之路

随着工业领域相关技术的不断发展&#xff0c;预测性维护作为一种先进的维护策略&#xff0c;正日益受到企业的重视。预测性维护的核心目标是通过准确预测设备故障的发生时间&#xff0c;实现及时维护和优化生产效率。而在实现这一目标的过程中&#xff0c;数据分析和机器学习的…

FreeRTOS:任务状态和信息查询

目录 一、任务相关 API函数预览二、任务相关API函数详解2.1uxTaskPriorityGet()2.2vTaskPrioritySet()2.3uxTaskGetSystemState() ※※※※※2.4vTaskGetInfo() ※※※※※2.5xTaskGetApplicationTaskTag()2.6xTaskGetCurrentTaskHandle()2.7xTaskGetHandle()2.8xTaskGetIdleTa…

教你用JMeter做接口测试的几个简单实例

目录 前言 1、登录&#xff08;POST&#xff09; 登录 2、获取学生信息&#xff08;GET&#xff09; 获取学生信息 3、添加学生信息&#xff08;POST&#xff0c;JSON&#xff09; 添加学生信息 4、学生充值金币&#xff08;POST&#xff0c;Cookie&#xff09; 学生金…

【Linux】11. 进程控制

小实验(谨慎测试) 1. 进程退出码的引出 2. 进程码的使用 3. 进程退出 3.1 进程退出情况 进程退出分三种情况&#xff1a; 1.代码运行完毕&#xff0c;结果正确 – return 0; 2.代码运行完毕&#xff0c;结果不正确 – 根据退出码判断错误情况 3.代码没有运行完毕&#xff0c;…

如何0基础自学黑客(网络安全)技术,万字长文教你如何学习黑客(网络安全)

一、自学网络安全学习的误区和陷阱 1.不要试图先成为一名程序员&#xff08;以编程为基础的学习&#xff09;再开始学习 我在之前的回答中&#xff0c;我都一再强调不要以编程为基础再开始学习网络安全&#xff0c;一般来说&#xff0c;学习编程不但学习周期长&#xff0c;而…

【数字化转型-06】数字化转型咨询项目中如何做好高层访谈

咨询项目中少不了至关重要的一步&#xff0c;那就是高层访谈&#xff0c;做好高层访谈&#xff0c;对于咨询项目至关重要&#xff0c;我们接触的维度越高&#xff0c;就会越能把控项目的真实意图&#xff0c;有的放矢&#xff0c;不会让下面的人带偏&#xff1b;另一方面我们也…

Vue3 使用 Ts 泛型 封装本地存储

前期回顾 NVM —— 你把我玩明白_彩色之外的博客-CSDN博客本文将学习 使用 NVM 管理node版本https://blog.csdn.net/m0_57904695/article/details/130670262?spm1001.2014.3001.5501 目录 新建 \src\utils\storage-utils.ts 使用 泛型示例 泛型交换变量 泛型 strin…

在Linux系统中用vim编写第一个C语言之gcc编译器

文章目录 在Linux系统中用vim编写第一个C语言HelloWorld第一步 创建第二步 编写第三步&#xff0c;编译第四步 运行 gcc四步骤gcc常用选项 在Linux系统中用vim编写第一个C语言HelloWorld 第一步 创建 vim HelloWorld.c第二步 编写 #include<stdio.h>int main(){printf(…

Android Jsoup爬取网页数据及其局限性,接口爬取数据的思路

1.Jsoup jsoup 是一款Java 的HTML解析器&#xff0c;可直接解析某个URL地址、HTML文本内容。它提供了一套非常省力的API&#xff0c;可通过DOM&#xff0c;CSS以及类似于jQuery的操作方法来取出和操作数据。 需求是需要获取某个网站上的排行榜数据&#xff0c;用作App展示&am…

【axios】后端未收到前端post传参

今天遇到过问题&#xff0c;前端post请求参数明明已经传过去了&#xff0c;可是后端说没收到&#xff0c;不知道后端大哥是不是故意搞我。 代码前端图如下↓ 代码 import axios from axios //对象形式 const val {pass:123,user:name}axios.post(/api/login, val).then(res>…

Vue3-黑马(十三)

目录&#xff1a; &#xff08;1&#xff09;vue3-router-动态路由3 &#xff08;2&#xff09;vue3-进阶router-动态菜单 &#xff08;3&#xff09;vue3-进阶-router-令牌-获取用户信息 &#xff08;1&#xff09;vue3-router-动态路由3 登录页面后&#xff0c;如果点击了…

Android技术探索与实践:从新功能体验到故障调试的全方位探索

目录 Android技术探索与实践&#xff1a;从新功能体验到故障调试的全方位探索 第一章&#xff1a;技术解析 Android平台的架构和工作原理 应用组件的生命周期和交互方式 Android开发中常用的设计模式和技术框架解析 第二章:产品新功能体验测评 深入了解最新发布的Androi…

【安卓源码】Binder机制5 -- Java层Framework Binder机制和 AIDL

图中红色代表整个framework层 binder架构相关组件&#xff1b; Binder类代表Server端&#xff0c;BinderProxy类代码Client端&#xff1b;图中蓝色代表Native层Binder架构相关组件&#xff1b;上层framework层的Binder逻辑是建立在Native层架构基础之上的&#xff0c;核心逻辑都…

shell编程:概述、脚本入门、变量、运算符、条件判断、流程控制、读取控制台、函数、正则表达式、文本处理工具、综合案例

第 1 章 Shell 概述 1&#xff09;Linux 提供的 Shell 解析器有 [atguiguhadoop101 ~]$ cat /etc/shells /bin/sh /bin/bash /usr/bin/sh /usr/bin/bash /bin/tcsh /bin/csh2&#xff09;bash 和 sh 的关系 sh&#xff1a;比较基础bash&#xff1a;功能更加强大&#xff0c;默…

三十四、Hybrid 接口用法解析

文章目录 前言交换机接口类型有哪些Hybrid 端口使用场景什么时候必须使用 Hybrid 一、Hybrid 特点二、Hybrid 当做 access和trunk使用三、Hybrid 特殊用法 前言 交换机接口类型有哪些 Access、trunk、Hybrid、qinq Hybrid 端口使用场景 接pc、服务器、接交换机、接路由器&a…