今天继续学习了欧拉角和四元数,明天开始“合成大西瓜”!
目录
向量的运算(续)
点乘,又称“内积”或“点积”
公式:
几何意义:
应用:
结果与角度的关系
叉乘
公式
几何意义
应用:
叉乘所得向量的模长与角度关系:
欧拉角
什么是欧拉角
优点:
缺点:
1.方位的表达方式不唯一
2.万向节死锁
四元数
什么是四元数
优点:
缺点
四元数运算
·与向量相乘
·与四元数相乘
各分量乘积和[x1,y1,z1]·[x2,y2,z2]=x1x2+y1y2+z1z2
两个向量的单位向量相乘后再乘以二者夹角的余弦值
API:float dot=Vector3.Dot(va,vb);
用点乘求夹角:
把两向量做narmalized处理,两个标准化向量结果就是cos,再反余弦Mathf.Acos就得到角度
点乘结果得到的角是小角
对于标准化过的向量,点乘结果等于两向量夹角的余弦值
计算两向量夹角
public Transform t1, t2;
float dot = Vector3.Dot(t1.position.normalized, t2.position.normalized);
float angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
对于标准化过的向量,方向完全相同,点乘结果为1,完全相反,点乘结果为-1,互相垂直为0
如果两个向量夹角大于60度,则
if(angle>60){}或if(dot<0.5f){}//优点:省着CPU再去算角度
private void Update()
{
Debug.DrawLine(Vector3.zero, t1.position,Color.red);
Debug.DrawLine(Vector3.zero, t2.position,Color.red);
float dot = Vector3.Dot(t1.position.normalized, t2.position.normalized);
float angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
}
又称“叉积”或“外积”
[x1,y1,z1]*[x2,y2,y3]=[y1*z2-z1*y2,z1*x2-x1*z2,x1*y2-y1*x2]
结果为两个所组成面的垂直向量,模长为两向量模长乘积再乘夹角的正弦值
API:Vector3 vector=Vector3.Cross(a,b);
·创建垂直于平面的向量
·判断两条向量相对位置,即顺逆时针关系
private void Update()
{
Debug.DrawLine(Vector3.zero, t1.position, Color.red);
Debug.DrawLine(Vector3.zero, t2.position, Color.red);
dot = Vector3.Dot(t1.position.normalized, t2.position.normalized);
angle = Mathf.Acos(dot) * Mathf.Rad2Deg;
Vector3 cross = Vector3.Cross(t1.position, t2.position);
if (cross.y < 0)
angle = 360 - angle;
Debug.DrawLine(Vector3.zero, cross, Color.green);
}
0~90度角
Vector3 cross=Vector3.Cross(a.normalized,b.normalized);
float angle=Mathf.Asin(cross.magnitude)*Mathf.Rad2Deg;
(很少应用)
使用三个角度来保存方位
x与z沿自身坐标系旋转,y沿世界坐标系旋转
API:Vector3 eulerAngle=this.transform.eulerAngles;
仅使用三个数字表达方位,占用空间小
沿坐标轴旋转的单位为角度,符合人的思考方式
任意三个数字都是合法的,不存在不合法的欧拉角
对于一个方位存在多个欧拉角描述,因此无法判断多个欧拉角代表的角位移是否相同
例:角度0,5,0与角度0,365,0
角度250,0,0与角度290,180,180
为了保证任意方位都只有独一无二的表示,unity限制了角度的范围,即沿x轴旋转限制在-90到90之间,沿y与x轴旋转限制在0到360之间(在unity编译器中不限制,在代码中限制)
Vector3 euler=this.transform.eulerAngles;
注意:欧拉角没有方向、大小的概念
因为三维向量,包含x,y,z,所以在Unity中欧拉角的数据类型为Vector3
欧拉角的x,y,z表示各个轴上的旋转角度
Vector3 por=this.transform.position;
位置,有方向(从世界原点指向当前位置),有大小(当前位置到世界原点间距)
向量的x,y,z,表示各个轴上的有向位移
可用来检测欧拉角
public Vector3 euler;
//可以用来检测欧拉角
public void OnGUI()
{
euler = this.transform.eulerAngles;
if (GUILayout.RepeatButton("沿X轴旋转"))
this.transform.eulerAngles += new Vector3(1, 0, 0);
if (GUILayout.RepeatButton("沿y轴旋转"))
this.transform.eulerAngles += Vector3.up;
if (GUILayout.RepeatButton("沿z轴旋转"))
this.transform.eulerAngles += new Vector3(0, 0,1);
}
物体沿x轴旋转+(-)90度,自身坐标系z轴与世界坐标系y轴将重合,此时再沿y或z轴旋转时,将失去一个自由度。
在万向节死锁情况下,规定沿z轴完成绕数值轴的全部旋转,即此时y轴旋转为0
Quaternion在3D图形学中代表旋转,由一个三维向量(x,y,z)和一个标量(w)组成
旋转轴为V,旋转弧度为θ,如果使用四元数表示,则四个分量为:
x=sin(θ/2)*V.x y=sin(θ/2)*V.y
z=sin(θ/2)*V.z w=cos(θ/2)
x,y,z,w的取值范围是-1到1
API:Quaternion qt=this.transform.rotation;
public void OnGUI()
{
euler = this.transform.eulerAngles;
if (GUILayout.Button("设置物体旋转角度"))
{
//旋转轴
Vector3 axis = t1.position - t2.position;
//旋转弧度
float rad = 50 * Mathf.Deg2Rad;
Quaternion qt = new Quaternion();
qt.x = Mathf.Sin(rad / 2) * axis.x;
qt.y = Mathf.Sin(rad / 2) * axis.y;
qt.z = Mathf.Sin(rad / 2) * axis.z;
qt.w = Mathf.Cos(rad / 2);
this.transform.rotation = qt;
}
}
避免万向节死锁
this.transform.rotation(四元数)*=Quaternion.Euler(0,1,0);
可使物体沿自身坐标Y轴旋转
public void OnGUI()
{
euler = this.transform.eulerAngles;
if (GUILayout.RepeatButton("沿X轴旋转"))
this.transform.rotation *= Quaternion.Euler(1, 0, 0);
if (GUILayout.RepeatButton("沿y轴旋转"))
this.transform.rotation *= Quaternion.Euler(0, 1, 0);
if (GUILayout.RepeatButton("沿z轴旋转"))
this.transform.rotation *= Quaternion.Euler(0, 0, 1);
}
this.transform.Rotate(Vector3 eulerAngles)
内部就是使用四元数相乘实现
·难于使用,不建议单独修改某个数值
·存在不合法的四元数
四元数左乘向量,表示将该向量按照四元数表示的角度旋转
例:Vector3 point =new Vector3(0,0,10);//向前走10米
Vector3 newpoint=Quaternion.Euler(0,30,0)(沿y轴旋转30度)*point;
两个四元数相乘可以组合旋转效果
例:Quaternion rotation01=Quaternion.Euler(0,30,0)*Quaternion.Euler(0,20,0);
Quaternion rotation02=Quaternion.Euler(0,50,0);
rotation01与rotation02相同