MIUIV5时可以将游戏数据包挂载sd卡下载到sd卡,但是上到MIUI9之后就不行了,怎么解决?求教

3D的编程总会用到矩阵变换,缩放和平移都简单,旋转有点麻烦,但是unity提供四元数进行旋转。
比如要把向量(1,0,0)绕Y轴旋转90度。
只需要Quaternion.AngleAxis(角度, 轴)* 向量就可以了。记住向量一定要放到“*”的右边。
var vector3 = new Vector3(1,0,0);
var result =
Quaternion.AngleAxis(90, Vector3.up)* vector3;
得到的结果是(0,0,-1)。是不是很简单,赶紧试试吧。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:7795次
排名:千里之外
(1)(2)(1)(1)(1)1609人阅读
Unity3D游戏开发(31)
& & & & 在Unity中,所有物体即使是空物体,也至少绑定Transform这个组件,这个组件有三个属性:position、rotation、scale,它们分别用于控制物体的平移、旋转和缩放三种变化,而其中最为复杂的一种就是旋转,它就对应于transform组件中的rotation属性,这个属性的类型其实就是四元数。
& & & & 常用的控制旋转的方法有:矩阵旋转和欧拉旋转,还有本篇要介绍的重点四元数,它也是实现旋转的方式之一。下面简单介绍一下前面的两种实现方式:
1.矩阵旋转:使用一个4*4的矩阵来表示绕任意轴旋转时的变换矩阵,这个矩阵具有的特点:乘以一个向量的时候只改变向量的方向而不会改变向量的大小;
优点:旋转轴可以是任意向量;
缺点:进行旋转其实我们只需要知道一个向量和一个角度,4个值的信息,而旋转变换矩阵使用了4*4=16个元素;
& & & & & & 变换过程增加乘法运算量,耗时;
2.欧拉旋转:在旋转时,按照一定的顺序(例如:x、y、z,Unity中的顺序是z、x、y)每个轴旋转一定的角度,来变换坐标或者是向量,实际上欧拉旋转也可以理解为:一系列坐标旋转的组合;
优点:只需使用3个值,即三个坐标轴的旋转角度;
缺点:必须严格按照顺序进行旋转(顺序不同结果就不同);
& & & & & &容易造成“万向节锁”现象,造成这个现象的原因是因为欧拉旋转是按顺序先后旋转坐标轴的,并非同时旋转,所以当旋转中某些坐标重合就会发生万向节锁,这时就会丢失一个方向上的选择能力,除非打破原来的旋转顺序或者三个坐标轴同时旋转;
& & & & & &由于万向节锁的存在,欧拉旋转无法实现球面平滑插值;
一、四元数:
& & & & 根据前面所述,我们知道了四元数是用于表示旋转的一种方式,而且transform中的rotation属性的数据类型就是四元数,那么四元数该如何表示呢?从本质上来讲,四元数就是一个高阶复数,也就是一个四维空间。
& & & & 普通的低阶复数形式一般是:x = a + bi,其中a、b为实数,而i则是虚数单位,而且存在i^2 = -1这样的运算规律,用坐标表示时其实就是由实轴和虚轴构成的二维空间。
& & & & 说四元数是高阶复数,是因为它一般表示为:x = a + bi + cj + dk,其中i、j、k都是虚数单位,所以也都满足:i2=j2=k2=-1。此外,这三个虚数单位还有以下特点:ij
= k,jk = i,ki = j
关于四元数的优缺点:
优点:避免万向节锁现象;
& & & & & &可绕任意过原点的向量旋转;
& & & & & &可提供球面平滑插值;
缺点:比欧拉旋转复杂,多了一个维度;
& & & & & &不够直观;
二、四元数与欧拉角:
& & & & 根据上述,我们可以这样表示一个四元数:q = w + xi + yj + zk,为了与三维旋转联系起来,可以简化表示为:q = ((x,y,z),w) = (v,w),其中v是一个向量,而w是个实数。此外,向量可以看做实部为0的四元数,而实数亦可以看做虚部为0的四元数。
& & & & 四元数基本运算法则:(证明:)
& & & & 假设我们想让点P绕单元向量u = (x,y,z)表示的旋转轴转θ角度,具体步骤:
1.将点P坐标转换到四元数空间:P = (P,0);
2.使用q=((x,y,z)sinθ2,cosθ2)&
& 来执行这个旋转;
3.旋转后的结果p'的坐标为:p′=qpq-1;
三、实际应用:
& & & & 上述讲解的是四元数的原理,但是在实际的使用中并没有那么复杂,我们只要调用Unity为我们提供的接口来修改旋转角度即可,例如为对象直接设置一个旋转值:
float speed = 100.0f;
void Update () {
  if(Input.GetMouseButton(0)){//鼠标按着左键移动
    y = Input.GetAxis(&Mouse X&) * Time.deltaTime *
    x = Input.GetAxis(&Mouse Y&) * Time.deltaTime *
  }else{
    x = y = 0 ;
  //旋转角度(增加)
  transform.Rotate(new Vector3(x,y,0));
  /**---------------其它旋转方式----------------**/
  //transform.Rotate(Vector3.up *Time.deltaTime * speed);//绕Y轴 旋转
  //用于平滑旋转至自定义目标
  pinghuaxuanzhuan();
//平滑旋转至自定义角度
void OnGUI(){
  if(GUI.Button(Rect(Screen.width - 110,10,100,50),&set Rotation&)){
    //自定义角度
    targetRotation = Quaternion.Euler(45.0f,45.0f,45.0f);
    // 直接设置旋转角度
    //transform.rotation = targetR
    // 平滑旋转至目标角度
    iszhuan =
bool iszhuan=
Quaternion targetR
void pinghuaxuanzhuan(){
  if(iszhuan){
    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 3);
}& & & & 就像上述的代码中,在实际应用中我们只需通过Quaternion.Euler和Quaternion.Slerp来完成Rolation的赋值操作。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:161011次
积分:2904
积分:2904
排名:第10819名
原创:117篇
转载:27篇
评论:118条
文章:14篇
阅读:7350
阅读:2557
阅读:34728
文章:21篇
阅读:35635
阅读:6158
阅读:14524
(2)(9)(7)(5)(4)(8)(1)(1)(7)(12)(9)(9)(15)(31)(12)(1)(5)(10)(1)版权声明:本文为博主原创文章,欢迎转载。请保留博主链接:
欧拉旋转、四元数、矩阵旋转之间的差异
除了欧拉旋转以外,还有两种表示旋转的方式:矩阵旋转和四元数旋转。接下来我们比较它们的优缺点。
优点:三个角度组成,直观,容易理解。
优点:可以进行从一个方向到另一个方向旋转大于180度的角度。
弱点:死锁问题。 前面已经介绍过万向节死锁问题。
内部由四个数字(在Unity中称为x,y,z和w)组成,然而这些数字不表示角度或轴,并且通常不需要直接访问它们。除非你特别有兴趣深入了解四元数学,你只需要知道四元数表示三维空间中的旋转,你通常不需要知道或修改x,y和z属性。
优点:四元旋转不存在万向节锁问题。
优点:存储空间小,计算效率高。
弱点:单个四元数不能表示在任何方向上超过180度的旋转。
弱点:四元数的数字表示不直观。
优点:与四元数一样,不存在万向节锁问题
优点:可以表示围绕任意轴的旋转,四元数的旋转轴均为通过物体中心点的轴,矩阵则不受限
缺点:矩阵旋转使用4x4矩阵,记录16个数值,而四元数只需要4个数值。计算复杂,效率低。
由于Unity中的旋转使用四元数,因此本文重点就放在四元数数学上。矩阵还是某些场合需要用到,比如坐标变换。不过你几乎不需要手动去执行矩阵计算,除非你在做Shader编程,或者是某些需要极端提高效率的场合。后续我计划写一篇文章专门介绍矩阵变换。
四元数的数学
由于前面两篇文章均尽可能采用无数字计算的方式,为的是方便理解。而这里由于需要理解四元数的计算,我们还是需要理解一些复数和欧拉旋转计算方面的基本数学。
首先了解一下复数,上图中,左右方向的轴(X轴)称为实数轴,上下的轴(Y轴)称为虚数轴。
任意一个二维矢量,都可以使用一个复数形式进行表示。也就是:e = a + bi
其中a是实数,i是虚数,i * i=-1。
至于什么是虚数,我的理解它就是个不同维度的后缀标记和符号而已。i标记了实数轴和虚数轴之间的差异,也就是旋转90度旋转的差别,乘以i意味着这种旋转关系。
为什么i * i=-1?也就是两个虚数i乘积又变成了实数-1?因为从实数轴(1表示)围绕上图中逆时针90度(1i)到达虚数轴(=i),再逆时针旋转90(1
i * i)就到达了负实数轴方向(=-1)。所以这里规定i * i=-1。
加法: (a+bi)+(c+di)=(a+c)+(b+d)i
减法: (a+bi)-(c+di)=(a-c)+(b-d)i
乘法: (a+bi)(c+di)=ac+bci+adi+bdi^{2}=(ac-bd)+(bc+ad)i
更多复数相关知识,请参考
欧拉旋转定理
为了方便讨论旋转,我们避开矢量长度的影响,也就是假设问题是基于长度为1的矢量去讨论。由上图可以看出,当长度为1时,矢量落在长度为1的圆形上,此时实数轴上的a = cos(φ),虚数轴上的b = sin(φ),其中φ为旋转角度。
此时的表示形式为 e = cos(φ) + sin(φ)i
那么,使用这样一种形式到底有什么意义呢?数学从来都是工具,如果没有用处,它也就不会存在了。我们接下来看它的作用。
当圆上的一个矢量进行了连续的旋转时时,假设先旋转φ,再旋转θ,则结果应该是两个旋转的角的和的复数形式,即 e = cos(φ+θ)+sin(φ+θ)i
e1= cos(φ) + sin(φ)i
(表示旋转了φ角度)
e2= cos(θ) + sin(θ)i (表示旋转了θ角度)
那么e和e1、e2之间的关系是怎样的?
根据可以得知
cos(φ+θ)+sin(φ+θ)i
= (cos(φ)cos(θ)-sin(φ)sin(θ))+(sin(θ)cos(φ)+cos(θ)sin(φ))i
= cos(φ)cos(θ)+cos(φ)sin(θ)i+cos(θ)sin(φ)i- sin(φ)sin(θ)
= (cos(φ) + sin(φ)i)(cos(θ) + sin(θ)i)
也就是说,连续的旋转(例如这里旋转φ再旋转θ),可以使用两个复数的乘积进行表示。在计算机运算中:
如果知道φ=30度,那么计算此单位矢量再旋转θ=40度是很容易的,只需要直接运算cos(φ+θ)和sin(φ+θ)即可。
如果知道一个矢量(a+bi),不知其φ值,要对其进行旋转θ=40度,那么也就有了快速的计算方法,即:
(a+bi) * (cos(40)+sin(40)i) = (a * cos(40)-b * sin(40)) + (b * cos(40) + a * sin(40))i
结果:二维向量 x = a * cos(40)-b * sin(40),y= b * cos(40) + a * sin(40)
可以快速计算出二维矢量结果,而不必先求φ。
因此这就是复数和欧拉旋转定理的作用,它可以使用复数很方便地表示出二维矢量的旋转变化。
相对于复数为二维空间,为了解决三维空间的旋转变化问题,爱尔兰数学家威廉·卢云·哈密顿把复数进行了推广,也就是四元数。
以下均为定义,所谓定义,就是我们人为设置的概念和计算方法,它们本身或许没有什么意义,但是如果按照这些概念和方法计算出某些有意义的结果,那么这些定义也就有了相应的意义。
四元数定义
四元数定义i、j、k三个虚数单位参与运算,并有以下运算规则:
k*j = -i; \]
k*j = -i; \]
i*k = -j; \]
\[i*i= j*j = k*k = i*j*k =-1 \]
i、j、k仍然理解为旋转,其中:
i旋转代表X轴与Y轴相交平面中X轴正向向Y轴正向的旋转
j旋转代表Z轴与X轴相交平面中Z轴正向向X轴正向的旋转
k旋转代表Y轴与Z轴相交平面中Y轴正向向Z轴正向的旋转
-i、-j、-k分别代表i、j、k旋转的反向旋转
一个普通四元数可以写成如下形式:
\[\overline q=a+bi+cj+dk\]
四元数的i、j、k之间乘法的性质与向量之间的叉积结果形式很类似,于是四元数有了另外一种表示形式:
\[\overline q=(\vec{(x,y,z)},w)=(\vec u,w)\]
四元数加法,跟复数、矢量和矩阵一样,两个四元数之和需要将不同的元素加起来,加法遵循实数和复数的所有交换律和结合律:
\[{\displaystyle \overline q_1+\overline q_2=w_1+w_2+{\vec {u_1}}+{\vec {u_2}}=(w_1+w_2)+(x_1+x_2)i+(y_1+y_2)j+(z_1+z_2)k}\]
格拉斯曼积定义
四元数的乘法有很多种,最常见的一种定义,与数学多项式乘法相同,称为格拉斯曼积。(注意,下面乘积的式子是由多项式形如a+bi+cj+dk的多项式进行多对多乘法(比如4项x4项=16项)计算后,使用矢量的点积和叉积替代部分计算项后形成):
\[\overline q_1\overline q_2=w_1w_2-{\vec
{u_1}}\cdot {\vec
{u_2}}+w_1{\vec
{u_2}}+w_2{\vec
{u_1}}+{\vec
{u_1}}\times {\vec
把向量部分和实数部分分开,可以写成:
\[\overline q_1\overline q_2=((w_1{\vec
{u_2}}+w_2{\vec
{u_1}}+{\vec
{u_1}}\times {\vec
{u_2}}),(w_1w_2-{\vec
{u_1}}\cdot {\vec
{u_2}}))\]
注意,格拉斯曼积符合结合率,也就是\(\overline q_1\overline q_2\overline q_3=(\overline q_1\overline q_2)\overline q_3=\overline q_1(\overline q_2\overline q_3)\),但不符合交换律,一般来说,\(\overline q_1\overline q_2≠\overline q_2\overline q_1\)。
点积也叫做欧几里得内积,四元数的点积等同于一个四维矢量的点积。点积的值是\(\overline q_1\)中每个元素的数值与 \(\overline q_2\) 中相应元素的数值的一对一乘积(比如4项x4项=4项)的和。这是四元数之间的可换积,并返回一个标量。
\[{\overline q_1\cdot \overline q_2=w_1w_2+{\vec {u_1}}\cdot {\vec {u_2}}=w_1w_2+x_1x_2+y_1y_2+z_1z_2}\]
四元数叉积:p × q
四元数叉积也称为奇积。它和矢量叉积等价,并且只返回一个矢量值:
\[{\overline p\times \overline q={\frac {\overline p\overline q-\overline q\overline p}{2}}}\]
\[{\overline p\times \overline q={\vec {u}}\times {\vec {v}}}\]
\[{\overline p\times \overline q=(cz-dy)i+(dx-bz)j+(by-xc)k}\]
四元数的共轭的定义。即实部相同,虚部相反。(与复数概念类似)
\[{\overline q^{*}=(\vec{(-x,-y,-z)},w)=(-\vec v,w)}\]
定义的推论
从以上三个定义,可以得出一个推论,一个四元数与其共轭的格拉斯曼积等于此四元数与其自身的点积:
\[{\overline q\overline q^{*}}={{\overline q^{*}\overline q}} ={\overline q\cdot \overline q= {x^{2}+y^{2}+z^{2}+w^{2}}}\]
由格拉丝曼积定义可知
\[\overline q_1\overline q_2=w_1w_2-{\vec
{u_1}}\cdot {\vec
{u_2}}+w_1{\vec
{u_2}}+w_2{\vec
{u_1}}+{\vec
{u_1}}\times {\vec
\[{{\overline q_1=\overline q}}, {{\overline q_2=\overline q^{*}}}\]
{u_1}=\vec
{u}}, {\vec
{u_2}=-\vec
{u}},w_2=w_1=w\]
\[\overline q_1\overline q_2=w_1w_2-{\vec
{u_1}}\cdot {\vec
{u_2}}+w_1{\vec
{u_2}}+w_2{\vec
{u_1}}+{\vec
{u_1}}\times {\vec
\[\overline q\overline q^{*}=ww+{\vec
{u}}\cdot {\vec
{u}}-w{\vec
{u}}+w{\vec
{u}}+{\vec
{u}}\times {\vec
由于第三四项可以消去,第五项本身为0向量,所以得出
\[\overline q\overline q^{*}=ww+{\vec
{u}}\cdot {\vec
{u}}={\overline q\cdot \overline q}\]
于是得证,\(\overline q^{*}\overline q\)也可以同理得证,结果是与之相同的。
四元数的模的定义。与矢量的模类似,都表示长度或者绝对值的意思。四元数的绝对值是四元数到原点的距离。
\[{ |\overline q|={\sqrt {x^{2}+y^{2}+z^{2}+w^{2}}}}={\sqrt {w^{2}+\vec u \cdot \vec u}}={\sqrt {\overline q\cdot \overline q}}={\sqrt {\overline q\overline q^{*}}}\]
四元数的逆通过\(\overline q^{-1}\overline q = 1\)来定义。在此式左右两端同时乘以 \(\overline q^{*}\),可以得到:
\[{ \overline q^{-1}\overline q\overline q^{*}={\overline q^{*}}}\]
由于格拉丝曼积符合结合率,左式后两项先乘,得到:
\[{ \overline q^{-1}(\overline q\overline q^{*})={\overline q^{*}}}\]
再由上述的推论,可以得出:
\[{ \overline q^{-1}(\overline q\cdot\overline q)={\overline q^{*}}}\]
由于点积是个实数,可以使用除法,所以最后简化成:
\[{ \overline q^{-1}={\frac {\overline q^{*}}{\overline q\cdot \overline q}}}\]
单位四元数的定义
所谓的单位量,都是指,任意原始量乘以单位量之后保持原始量不变。如对于实数而言,任意原始实数乘以实数1等于原始实数,因此1被定义为单位。
对于四元数同样,任意原始四元数\(\overline q\)乘以某个四元数\(\overline q_i\),原始的四元数\(\overline q\)保持不变,则称此四元数\(\overline q_i\)为单位四元数。注意这里的乘以是指格拉丝曼积。根据格拉丝曼积的定义:
\[\overline q\overline q_i=((w{\vec
{u_i}}+w_i{\vec
{u}}+{\vec
{u}}\times {\vec
{u_i}}),(ww_i-{\vec
{u}}\cdot {\vec
{u_i}}))\]
若希望结果保持不变,也就是结果仍然为:
\[\overline q\overline q_i=\overline q = (w,\vec u)\]
\[w_i= 1, \vec u_i=0\]
也就是,单位四元数为:
\[{\overline q_i =({\vec 0},1)}\]
四元数的归一化
归一化也叫单位化,与矢量的概念一样,就是为了把长度调整到单位长度。现在单位四元数已经得知,其单位长度显然是1。我们也知道了模的计算方法,因此把四元数的归一化方法如下:
\[{\overline q^{,} =\frac {\overline q}{|\overline q|}=\frac {\overline q}{{\sqrt {w^{2}+\vec u \cdot \vec u}}}}\]
前述的单位复数表示形式为:e = cos(φ)+ sin(φ)i,因为此时,复数的长度为\(cos(φ)^{2}+sin(φ)^{2}=1\),我们使用类似形式表示归一化的四元数:
\[{\overline q =({\vec sin(φ)}\vec n,cos(φ))}\]
也就是实部现在使用\(cos(φ)\)表示,虚部使用\(sin(φ)\vec n\)表示,其中\(\vec n\)是归一化的三维矢量,由于\(\vec a \cdot \vec b=|\vec a||\vec b|cos(\theta_{ab})\),因此有\(\vec n \cdot \vec n =1\)。让我们尝试计算此时的模:
\[{ |\overline q|={\sqrt {w^{2}+\vec u \cdot \vec u}}={\sqrt {cos(φ)^{2}+sin(φ)^{2}(\vec n \cdot \vec n)}}}=1\]
四元数映射_实数
我们可以将实数映射到四元数空间,所谓的映射,也就是说1对1的找到相应的存在,并且在不同空间施加施加相对应运算的法则时获得相同的结果。
例如,对应实数w,尝试进行如下映射:
\[\overline q = ( \vec 0,w)\]
那么是否能够遵守相应的运算法则?也就是说,如果实数\(w_a\)与实数\(w_b\)均被映射到了四元数空间,那么它们的加法和乘法的结果是否还能映射回去,与原始空间的运算获得相同的结果?加法显然可以,这里进行乘法计算测试:
\[\overline q_a = (\vec 0,w_a)\]
\[\overline q_b = (\vec 0,w_b)\]
\[\overline q_a\overline q_b = (\vec 0,w_a) (\vec 0,w_b)=w_aw_b-{\vec
{0}}\cdot {\vec
{0}}+w_a{\vec
{0}}+w_b{\vec
{0}}+{\vec
{0}}\times {\vec
\[\overline q_a\overline q_b =w_aw_b+\vec
{0}=(\vec 0,w_aw_b)\]
显然,依照映射原则,可以将\((\vec 0,w_aw_b)\)映射回去变成\(w_a\)\(w_b\),与实数空间的计算结果相同。因此,此映射法则是可行的:
\[\overline q = ( \vec 0,w)\]
四元数映射_矢量直接映射
与实数映射类似,我们也可以尝试将三维矢量映射到四元数。
对于矢量\(\vec v\)映射法则如下:
\[\overline q = ( \vec v,0)\]
同样,加法运算显然可以映射回去,我们来计算乘法运算。
\[\overline q_a = (\vec v_a,0)\]
\[\overline q_b = (\vec v_b,0)\]
\[\overline q_a\overline q_b = (\vec v_a,0) (\vec v_b,0)=00-{\vec
{v_a}}\cdot {\vec
{v_b}}+0{\vec
{v_b}}+0{\vec
{v_a}}+{\vec
{v_a}}\times {\vec
\[\overline q_a\overline q_b = (\vec
{v_a}\times \vec
{v_b}, -\vec v_a \cdot\vec v_b)\]
可以看到,此时出现了之前两个四元数中均不存在的实部。因此这个映射法则是错误的。(本身两个向量之间如果直接使用多对多的格拉斯曼积式乘法也是没有意义的,所以结果可想而知。那么,是不是就不能映射矢量了?不是,只是映射法则错误。回想之前在在复数中也曾使用过格拉斯曼积式的乘法
如果矢量用\(e = cos(φ)+ sin(φ)i\)表示,那么此时的\(e\)表示了一个二维旋转,向量的旋转可以使用格拉斯曼积形式的复数乘法\(e_1*e_2\)表示,因此,映射法则应该与角度相关。)
四元数映射_矢量间接映射
先考虑一个同态概念:假设M,M′是两个乘集,也就是说M和M′是两个各具有一个封闭的具有结合律的运算与的代数系统。φ是M射到M′的映射,并且任意两个元的乘积的像是这两个元的像的乘积,即对于M中任意两个元a,b,满足
φ(ab)=φ(a)φ(b);
也就是说,当a→φ(a),b→φ(b)时,ab→φ(a)φ(b),
那么这映射φ就叫做M到M′上的同态。我前面所解释的可以互相映射大致意思也就是同态。
四元数中的同态函数
为什么我们研究同态?因为这里涉及到我们希望出现的向量的旋转,一个三维空间的向量被旋转之后,它应该还是一个三维矢量。这个旋转函数应该满足以下要求:
①、旋转完的矢量长度应该保持不变。也就是\[|φ(A)|=|A|\]
②、假如对向量A和B都执行φ旋转,那么向量A、B的夹角θ应该与φ(A)、φ(B)的夹角应该相同。由矢量点积我们知道\(A \cdot B=|A||B|cos(θ)\),在满足①的情况下,也就是模的乘积已经不变,那么如果点积结果相同,也就能保证角度不变,也就是:\[φ(A) \cdot φ(B)=A \cdot B\]
③、假如对向量A和B都执行φ旋转,那么旋转完毕的φ(A)、φ(B)的相对方向关系与A、B应该保持一致,也就是所谓的手向性应该保持一致(比如原来A的正左侧恰好B,旋转完毕应该还是在正左侧,仅有夹角是不够的,正右侧也是90度关系)。假如满足下面的式子,则手向性不变\[φ(A) \times φ(B)=A \times B\]
接着,我们详细了解间接映射的步骤。如下图所示:
我们通过间接映射将矢量V映射到四元数,先如直接映射那样,映射成一个纯虚四元数,即\((\vec V,0)\),再执行φ()变换。简写为\[φ(V)\]
由于\[A B=w_aw_b-u_a \cdot u_b+w_a \cdot u_b+w_b \cdot u_a+u_a \times u_b\]
此时的\(A、B\)均是纯虚四元数,因此\(w_a=w_b=0\),可以简化为
\[A B=-u_a \cdot u_b+u_a \times u_b\]
由于\(A、B\)均是纯虚四元数,因此\(A \cdot B = u_a \cdot u_b\),再由于四元数叉积和矢量叉积等价,因此上式可以转化为
\[A B=-A \cdot B+A \times B\]
如果存在这样的同态函数函数:\[φ(A) φ(B)=φ(AB)\]
也就能满足②、③的要求。
[此处还可以解释得更明确,不过太过复杂了]
旋转四元数
最后,给出旋转四元数,也就是一直在追求的φ()函数。
\[{\overline q =({\vec sin(\frac \theta 2)}\vec n,cos(\frac \theta 2))}\]
它的含义是围绕\(\vec n\)轴旋转\(\theta\)角度。
具体旋转的方法使用以下函数:
\[{\overline p^{,} =\overline q\overline p\overline q^{*}
其中\(\overline p\)是被旋转四元数,\(\overline p^{,}\)是结果四元数。这里的\(\overline q^{*}\)写成\(\overline q^{-1}\)也是可以的,因为根据前述对逆的计算,对于此时模为1的\(\overline q\)来说,它们结果相同的。
具体推究方法以及证明方法过于复杂,就不在这里给出了。
四元数是高阶复数的数学,它用在游戏中的作用主要是计算三维矢量的旋转,它使用先将矢量映射到纯虚四元数,再应用旋转四元数的方式进行映射。最后可以达成旋转目的。
前面的四元数性质不甚了解也没有太大关系,大概了解到是如何映射的也就可以了。甚至在Unity编程过程中你对四元数的映射关系不了解都无所谓。后续文章中我再介绍具体的用法。
阅读(...) 评论()【Unity技巧】四元数(Quaternion)和旋转
我的图书馆
【Unity技巧】四元数(Quaternion)和旋转
四元数介绍旋转,应该是三种坐标变换——缩放、旋转和平移,中最复杂的一种了。大家应该都听过,有一种旋转的表示方法叫四元数。按照我们的习惯,我们更加熟悉的是另外两种旋转的表示方法——矩阵旋转和。矩阵旋转使用了一个4*4大小的矩阵来表示绕任意轴旋转的变换矩阵,而欧拉选择则是按照一定的坐标轴顺序(例如先x、再y、最后z)、每个轴旋转一定角度来变换坐标或向量,它实际上是一系列坐标轴旋转的组合。那么,又是什么呢?简单来说,四元数本质上是一种高阶复数(听不懂了吧。。。),是一个四维空间,相对于复数的二维空间。我们高中的时候应该都学过复数,一个复数由实部和虚部组成,即x = a bi,i是虚数单位,如果你还记得的话应该知道i^2 = -1。而四元数其实和我们学到的这种是类似的,不同的是,它的虚部包含了三个虚数单位,i、j、k,即一个四元数可以表示为x = a bi cj dk。那么,它和旋转为什么会有关系呢?在Unity里,tranform组件有一个变量名为rotation,它的类型就是四元数。很多初学者会直接取rotation的x、y、z,认为它们分别对应了Transform面板里R的各个分量。当然很快我们就会发现这是完全不对的。实际上,四元数的x、y、z和R的那三个值从直观上来讲没什么关系,当然会存在一个表达式可以转换,在后面会讲。大家应该和我一样都有很多疑问,既然已经存在了这两种旋转表示方式,为什么还要使用四元数这种听起来很难懂的东西呢?我们先要了解这三种旋转方式的优缺点:矩阵旋转优点:旋转轴可以是任意向量;缺点:旋转其实只需要知道一个向量 一个角度,一共4个值的信息,但矩阵法却使用了16个元素;而且在做乘法操作时也会增加计算量,造成了空间和时间上的一些浪费;欧拉旋转优点:很容易理解,形象直观;表示更方便,只需要3个值(分别对应x、y、z轴的旋转角度);但按我的理解,它还是转换到了3个3*3的矩阵做变换,效率不如四元数;缺点:之前提到过这种方法是要按照一个固定的坐标轴的顺序旋转的,因此不同的顺序会造成不同的结果;会造成(Gimbal Lock)的现象。这种现象的发生就是由于上述固定坐标轴旋转顺序造成的。理论上,欧拉旋转可以靠这种顺序让一个物体指到任何一个想要的方向,但如果在旋转中不幸让某些坐标轴重合了就会发生万向节锁,这时就会丢失一个方向上的旋转能力,也就是说在这种状态下我们无论怎么旋转(当然还是要原先的顺序)都不可能得到某些想要的旋转效果,除非我们打破原先的旋转顺序或者同时旋转3个坐标轴。这里有个可以直观的理解下;由于万向节锁的存在,欧拉旋转无法实现球面平滑插值;四元数旋转优点:可以避免万向节锁现象;只需要一个4维的四元数就可以执行绕任意过原点的向量的旋转,方便快捷,在某些实现下比旋转矩阵效率更高;可以提供平滑插值;缺点:比欧拉旋转稍微复杂了一点点,因为多了一个维度;理解更困难,不直观;四元数和欧拉角基础知识前面说过,一个四元数可以表示为q = w xi yj zk,现在就来回答这样一个简单的式子是怎么和三维旋转结合在一起的。为了方便,我们下面使用q = ((x, y, z),w) = (v, w),其中v是向量,w是实数,这样的式子来表示一个四元数。我们先来看问题的答案。我们可以使用一个四元数q=((x,y,z)sinθ2, cosθ2)&来执行一个旋转。具体来说,如果我们想要把空间的一个点P绕着单位向量轴u = (x, y, z)表示的旋转轴旋转θ角度,我们首先把点P扩展到四元数空间,即四元数p = (P, 0)。那么,旋转后新的点对应的四元数(当然这个计算而得的四元数的实部为0,虚部系数就是新的坐标)为:p′=qpq?1其中,q=(cosθ2, (x,y,z)sinθ2)&,q?1=q?N(q),由于u是单位向量,因此N(q)=1,即q?1=q?。右边表达式包含了四元数乘法。相关的定义如下:四元数乘法:q1q2=(v1→×v2→ w1v2→ w2v1→,w1w2?v1→?v2→)共轭四元数:q?=(?v?&,w)四元数的模:N(q) = √(x^2 y^2 z^2 w^2),即四元数到原点的距离四元数的逆:q?1=q?N(q)它的证明这里不再赘述,有兴趣的可以参见。主要思想是构建了一个辅助向量k,它是将p绕旋转轴旋转θ/2得到的。证明过程尝试证明wk?=kv?,以此证明w与v、k在同一平面内,且与v夹角为θ。我们举个最简单的例子:把点P(1, 0, 1)绕旋转轴u = (0, 1, 0)旋转90°,求旋转后的顶点坐标。首先将P扩充到四元数,即p = (P, 0)。而q = (u*sin45°, cos45°)。求p′=qpq?1的值。建议大家一定要在纸上计算一边,这样才能加深印象,连笔都懒得动的人还是不要往下看了。最后的结果p` = ((1, 0, -1), 0),即旋转后的顶点位置是(1, 0, -1)。如果想要得到复合旋转,只需类似复合矩阵那样左乘新的四元数,再进行运算即可。我们来总结下四元数旋转的几个需要注意的地方:用于旋转的四元数,每个分量的范围都在(-1,1);每一次旋转实际上需要两个四元数的参与,即q和q*;所有用于旋转的四元数都是单位四元数,即它们的模是1;下面是几点建议:实际上,在Unity里即便你不知道上述公式和变换也丝毫不妨碍我们使用四元数,但是有一点要提醒你,除非你对四元数非常了解,那么不要直接对它们进行赋值。如果你不想知道原理,只想在Unity里找到对应的函数来进行四元数变换,那么你可以使用这两个函数:和。它们基本可以满足绝大多数的四元数旋转变换。和其他类型的转换首先是轴角到四元数:给定一个单位长度的旋转轴(x, y, z)和一个角度θ。对应的四元数为:q=((x,y,z)sinθ2,&cosθ2)&这个公式的推导过程上面已经给出。欧拉角到四元数:给定一个欧拉旋转(X, Y, Z)(即分别绕x轴、y轴和z轴旋转X、Y、Z度),则对应的四元数为:x = sin(Y/2)sin(Z/2)cos(X/2) cos(Y/2)cos(Z/2)sin(X/2)y = sin(Y/2)cos(Z/2)cos(X/2) cos(Y/2)sin(Z/2)sin(X/2)z = cos(Y/2)sin(Z/2)cos(X/2)-sin(Y/2)cos(Z/2)sin(X/2)w = cos(Y/2)cos(Z/2)cos(X/2)-sin(Y/2)sin(Z/2)sin(X/2)q = ((x, y, z), w)它的证明过程可以依靠轴角到四元数的公式进行推导。其他参考链接:1.&2.&3.&4.&四元数的插值这里的插值指的是球面线性插值。设t是一个在0到1之间的变量。我们想要基于t求Q1到Q2之间插值后四元数Q。它的公式是:Q3 &= (sin((1-t)A)/sin(A))*Q1 (sin((tA)/sin(A))*Q2)Q = Q3/|Q3|,即单位化四元数的创建在了解了上述知识后,我们就不需要那么惧怕四元数了,实际上它和矩阵类似,不同的只是它的表示方式以及运算方式。那么在Unity里如何利用四元数进行旋转呢?Unity里提供了非常多的方式来创建一个四元数。例如Quaternion.AngleAxis(float angle, Vector3 axis),它可以返回一个绕轴线axis旋转angle角度的四元数变换。我们可以一个Vector3和它进行左乘,就将得到旋转后的Vector3。在Unity里只需要用一个“ * ”操作符就可以进行四元数对向量的变换操作,相当于我们上述讲到的p′=qpq?1操作。如果我们想要进行多个旋转变换,只需要左乘其他四元数变换即可。例如下面这样:Vector3 newVector = Quaternion.AngleAxis(90, Vector3.up) * Quaternion.LookRotation(someDirection) * someV尽管欧拉角更容易我们理解,但四元数比欧拉角要强大很多。Unity提供了这两种方式供我们选择,我们可以选择最合适的变换。例如,如果我们需要对旋转进行插值,我们可以首先使用Quaternion.eulerAngles来得到欧拉角度,然后使用Mathf.Clamp对其进行插值运算。最后更新Quaternion.eulerAngles或者使用Quaternion.Euler(yourAngles)来创建一个新的四元数。又例如,如果你想要组合旋转,比如让人物的脑袋向下看或者旋转身体,两种方法其实都可以,但一旦这些旋转不是以世界坐标轴为旋转轴,比如人物扭动脖子向下看等,那么四元数是一个更合适的选择。Unity还提供了transform.forward, transform.right and transform.up&这些非常有用的轴,这些轴可以和Quaternion.AngleAxis组合起来,来创建非常有用的旋转组合。例如,下面的代码让物体执行低头的动作:transform.rotation = Quaternion.AngleAxis(degrees, transform.right) * transform.关于Quaternion的其他函数,后面再补充吧,原理类似~补充:欧拉旋转在文章开头关于欧拉旋转的细节没有解释的太清楚,而又有不少人询问相关问题,我尽量把自己的理解写到这里,如有不对还望指出。欧拉旋转是怎么运作的欧拉旋转是我们最容易理解的一种旋转方式。以我们生活中为例,一个舞蹈老师告诉我们,完成某个舞蹈动作需要先向你的左边转30°,再向左侧弯腰60°,再起身向后弯腰90°(如果你能办到的话)。上面这样一个旋转的过程其实和我们在三维中进行欧拉旋转很类似,即我们是通过指明绕三个轴旋转的角度来进行旋转的,不同的是,日常生活中我们更愿意叫这些轴为前后左右上下。而这也意味着我们需要指明一个旋转顺序。这是因为,先绕X轴旋转90°、再绕Y轴30°和先绕Y轴旋转90°、再绕X轴30°得到的是不同的结果。在Unity里,欧拉旋转的旋转顺序是Z、X、Y,这在相关的API文档中都有说明,例如。其实文档中说得不是非常详细,还有一个细节我们需要明白。如果你仔细想想,就会发现有一个非常重要的东西我们没有说明白,那就是旋转时使用的坐标系。给定一个旋转顺序(例如这里的Z、X、Y),以及它们对应的旋转角度(α,β,r),有两种坐标系可以选择:绕坐标系E下的Z轴旋转α,绕坐标系E下的Y轴旋转β,绕坐标系E下的X轴旋转r,即进行一次旋转时不一起旋转当前坐标系;绕坐标系E下的Z轴旋转α,绕坐标系E在绕Z轴旋转α后的新坐标系E'下的Y轴旋转β,绕坐标系E'在绕Y轴旋转β后的新坐标系E''下的X轴旋转r, 即在旋转时,把坐标系一起转动;很容易知道,这两种选择的结果是不一样的。但如果把它们的旋转顺序颠倒一下,其实结果就会一样。说得明白点,在第一种情况下、按ZXY顺序旋转和在第二种情况下、按YXZ顺序旋转是一样的。证明方法可以看下。而Unity文档中说明的旋转顺序指的是在第一种情况下的顺序。如果你还是不懂这意味着什么,可以试着调用下这个函数。例如,你认为下面代码的结果是什么:transform.Rotate(new Vector3(0, 30, 90));原模型的方向和执行结果如下:&而我们可以再分别执行下面的代码: // First case
transform.Rotate(new Vector3(0, 30, 0));
transform.Rotate(new Vector3(0, 0, 90));
// Second case
// transform.Rotate(new Vector3(0, 0, 90));
// transform.Rotate(new Vector3(0, 30, 0));两种情况的结果分别是:&可以发现,调用transform.Rotate(new&Vector3(0,&30,&90));是和第一种情况中的代码是一样的结果,即先旋转Y、再旋转Z。进一步实验,我们会发现transform.Rotate(new&Vector3(30,&90,&-40));的结果是和transform.Rotate(new&Vector3(0,&90,&0));transform.Rotate(new&Vector3(30,&0,&0));transform.Rotate(new&Vector3(0,&0,&-40));的结果一样的。你会问了,文档中不是明明说了旋转顺序是Z、X、Y吗?怎么现在完全反过来了呢?原因就是我们之前说的两种坐标系的选择。在一次调用transform.Rotate的过程中,坐标轴是不随每次单个坐标轴的旋转而旋转的。而在调用transform.Rotate后,这个旋转坐标系才会变化。也就是说,transform.Rotate(new&Vector3(30,&90,&-40));执行时使用的是第一种情况,而transform.Rotate(new&Vector3(0,&90,&0));transform.Rotate(new&Vector3(30,&0,&0));transform.Rotate(new&Vector3(0,&0,&-40));每一句则是分别使用了上一句执行后的坐标系,即第二种坐标系情况。因此,我们看起来顺序好像是完全是反了,但结果是一样的。上面只是说了一些容易混淆的地方,更多的内容大家可以搜搜wiki之类的。数学模型欧拉旋转的数学实现就是使用矩阵。而最常见的表示方法就是3*3的矩阵。在里我们可以找到这种矩阵的表示形式,以下以按XYZ的旋转顺序为例,三个矩阵分别表示了:在计算时,我们将原来的旋转矩阵右乘(这里使用的是列向量)上面的矩阵。从这里我们也可以证明上面所说的两种坐标系选择是一样的结果,它们之间的不同从这里来看其实就是矩阵相乘时的顺序不同。第一种坐标系情况,指的是在计算时,先从左到右直接计算R中3个矩阵的结果矩阵,最后再和原旋转矩阵相乘,因此顺序是XYZ;而第二种坐标系情况,指的是在计算时,从右往左依次相乘,因此顺序是反过来的,ZYX。你可以验证R左乘和右乘的结果表达式,就可以相信这个结论了!万向节锁虽然欧拉旋转非常容易理解,但它会造成臭名昭著的万向节锁问题。我之前给出了链接大家可能都看了,但还是不明白这是怎么回事。这里是我目前找到说得最容易懂的中文文章,大家可以看看。如果你还是不明白,我们来做个试验。还是使用之前的模型,这次我们直接在面板中把它的欧拉角中的X值设为90°,其他先保持不变:此时模型是脸朝下(下图你看到的只是一个头顶):现在,如果我让你不动X轴,只设置Y和Z的值,把这个模型的脸转上来,让它向侧面看,你可以办到吗?你可以发现,这时候无论你怎么设置Y和Z的值,模型始终是脸朝下、在同一平面旋转,看起来就是Y和Z控制的是同一个轴的旋转,下面是我截取的任意两种情况:&这就是一种万向节锁的情况。这里我们先设置X轴为90°也是有原因的,这是因为Unity中欧拉角的旋转顺序是ZXY,即X轴是第二个旋转轴。当我们在面板中设置任意旋转值时,Unity实际是按照固定的ZXY顺序依次旋转特定角度的。在代码里,我们同样可以重现万向节锁现象。 transform.Rotate(new Vector3(0, 0, 40));
transform.Rotate(new Vector3(0, 90, 0));
transform.Rotate(new Vector3(80, 0, 0));我们只需要固定中间一句代码,即使Y轴的旋转角度始终为90°,那么你会发现无论你怎么调整第一句和最后一句中的X或Z值,它会像一个钟表的表针一样总是在同一个平面上运动。万向节锁中的“锁”,其实是给人一种误导,这可能也是让很多人觉得难以理解的一个原因。实际上,实际上它并没有锁住任何一个旋转轴,只是说我们会在这种旋转情况下会感觉丧失了一个维度。以上面的例子来说,尽管固定了第二个旋转轴的角度为90°,但我们原以为依靠改变其他两个轴的旋转角度是可以得到任意旋转位置的(因为按我们理解,两个轴应该控制的是两个空间维度),而事实是它被“锁”在了一个平面,即只有一个维度了,缺失了一个维度。而只要第二个旋转轴不是±90°,我们就可以依靠改变其他两个轴的旋转角度来得到任意旋转位置。数学解释我们从最简单的矩阵来理解。还是使用XYZ的旋转顺序。当Y轴的旋转角度为90°时,我们会得到下面的旋转矩阵:我们对上述矩阵进行左乘可以得到下面的结果:可以发现,此时当我们改变第一次和第三次的旋转角度时,是同样的效果,而不会改变第一行和第三列的任何数值,从而缺失了一个维度。我们再尝试着理解下它的本质。上写,万向节锁出现的本质原因,是因为从欧拉角到旋转的映射并不是一个覆盖映射,即它并不是在每个点处都是局部同胚的。不懂吧。。。恩,我们再来通俗一下解释,这意味着,从欧拉角到旋转是一个多对一的映射(即不同的欧拉角可以表示同一个旋转方向),而且并不是每一个旋转变化都可以用欧拉角来表示。其他更多的大家去参考wiki吧。建议还是多看看视频,尤其是后面的部分。当然,如果还是觉得懵懵懂懂的话,在《3D数学基础:图形与游戏开发》一书中有一话说的很有道理,“如果您从来没有遇到过万向锁情况,你可能会对此感到困惑,而且不幸的是,很难在本书中讲清楚这个问题,你需要亲身经历才能明白。”因此,大家也不要纠结啦,等到遇到的时候可以想到是因为万向节锁的原因就好。
TA的最新馆藏

我要回帖

更多关于 数据包挂载sd卡 的文章

 

随机推荐