Unity学习笔记(七):旋转(四元数和欧拉角)

发表于2017-09-30
评论0 6.1k浏览

  在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=qpq1


三、实际应用:

        上述讲解的是四元数的原理,但是在实际的使用中并没有那么复杂,我们只要调用Unity为我们提供的接口来修改旋转角度即可,例如为对象直接设置一个旋转值:

float speed = 100.0f;  
float x;  
float z;  
void Update () {  
  if(Input.GetMouseButton(0)){//鼠标按着左键移动   
    y = Input.GetAxis("Mouse X") * Time.deltaTime * speed;                 
    x = Input.GetAxis("Mouse Y") * Time.deltaTime * speed;   
  }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 = targetRotation;  
    // 平滑旋转至目标角度   
    iszhuan = true;  
  }  
}  
bool iszhuan= false;  
Quaternion targetRotation;  
void pinghuaxuanzhuan(){  
  if(iszhuan){  
    transform.rotation = Quaternion.Slerp(transform.rotation, targetRotation, Time.deltaTime * 3);  
  }  
}
就像上述的代码中,在实际应用中我们只需通过Quaternion.Euler和Quaternion.Slerp来完成Rolation的赋值操作。

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引