原创

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

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://linsh-tech.blog.csdn.net/article/details/51206377

        在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的四元数

        四元数基本运算法则:(证明:http://www.cnblogs.com/yiyezhai/p/3176725.html

        


        假设我们想让点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.EulerQuaternion.Slerp来完成Rolation的赋值操作。

文章最后发布于: 2016-05-01 19:41:59
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 书香水墨 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览