四元数乘法旋转的本质是旋转的连续应用。
当你执行p * q时,可以理解为首先应用四元数p的旋转,然后再应用四元数q的旋转。
四元数旋转乘法主要分为全局坐标的旋转和局部坐标的旋转.
transform.rotation=transform.roation*quaterion可以解析如下:
transform.rotation表示应用自身的旋转(也可以理解为将物体从默认旋转应用到transform.rotation),然后应用Quaternion旋转(也可以说是向自己施加quaterion旋转,一个意思),因此结果就是绕本地轴旋转了quaternion.
而transform.rotation=Quaternion*transform.rotation;
可以理解为对空间应用Quaternion旋转,对空间进行旋转也就是对空间坐标系的基进行旋转。也就是将整个空间朝某个方向旋转,然后在旋转后的空间中再应用transform.rotaion,因此就是绕世界轴进行了旋转。
物体的localRotation其实就等于先应用父物体旋转,再应用本地旋转,也就是:transform.localRotation = transform.parent.rotation * transform.localRotation。但是我这里不打算通过这个来推断之后的局部旋转和全局旋转的异同关系。因为局部旋转的计算本质上依然是旋转的连续应用。
局部坐标时,localRotation=loaclRotation*q和世界坐标的原理一样,也是先应用自身旋转然后应用q的旋转。结果便是绕自身坐标系旋转。
而使用transform.localRotation=q*transform.localRotation也和之前一样。还是先对坐标系进行旋转,只不过这里旋转的是局部坐标系,然后再应用自身旋转,也就是自身绕着局部坐标系中的q进行了旋转。和世界坐标中的旋转原理一样,但是坐标系不同,结果自然不同。
假设我们想要按下x键时,物体绕y轴持续旋转:
using UnityEngine;
public class RotateOnKeyPress : MonoBehaviour
{
public float rotationSpeed = 90.0f; // 每秒旋转的度数
void Update()
{
// 检查是否按下了X键
if (Input.GetKey(KeyCode.X))
{
// 计算每帧的旋转量
float rotationThisFrame = rotationSpeed * Time.deltaTime;
// 创建一个代表顺时针旋转的四元数
Quaternion rotation = Quaternion.Euler(0, rotationThisFrame, 0);
// 将四元数乘法应用于当前旋转
transform.rotation = transform.rotation * rotation;
}
}
}
某些时候我们需要将世界旋转转换为局部旋转,比如制作布娃娃的关节朝向时,我们需要将世界朝向转换为关节的局部朝向并设置给关节的targetRotation。而之所以要转为局部的旋转是因为关节的targetRotation就是使用关节自己的坐标系进行控制的
为了简化,我们不使用布娃娃,而是使用一个简单的例子。例子中我们需要让自己指向worldRotation,代码为:
Quaternion worldRotation = ...; // 你的世界旋转四元数
Transform parentTransform = myObject.transform.parent; // 获取父对象的Transform
// 如果物体有父对象,则考虑父对象的旋转
if (parentTransform != null)
{
Quaternion parentRotation = parentTransform.rotation; // 父对象的世界旋转
Quaternion localRotation = Quaternion.Inverse(parentRotation) * worldRotation; // 转换为局部旋转
myObject.transform.localRotation = localRotation; // 应用局部旋转
}
else
{
// 如果没有父对象,世界旋转即是局部旋转
myObject.transform.localRotation = worldRotation;
}
其中的关键代码为:
Quaternion parentRotation = parentTransform.rotation; // 父对象的世界旋转
Quaternion localRotation = Quaternion.Inverse(parentRotation) * worldRotation; // 转换为局部旋转
首先我们获取parentRotaion,然后我们要指向的局部旋转就是Quaternion.Inverse(parentRotation) * worldRotation;
可能有些人一头雾水,但是结合我上面所说的四元数乘法的本质,连续的应用旋转。
我们可以看到,先应用parentRotation旋转的逆。然后再应用wolrdRotaion。
注意,应用这两个字。
Quaternion.Inverse表示旋转的逆,看作名词的话,就是父物体旋转的反方向的旋转,而在我们这里我们可以把它看作动词,就是逆向旋转,把父物体进行逆向旋转。
所以应用inverse,就是把父物体旋转回初始方向,也就相当于没有旋转,此时,本地坐标系的朝向就等于世界坐标系的朝向,所以我们只需要再应用目标朝向就可以旋转到目标方向了。
在Unity中,当涉及到向量和四元数的乘法时,四元数代表旋转,而向量代表一个点或方向。要将旋转应用到向量上,四元数必须位于乘法的左侧,向量位于右侧。这是因为四元数和向量之间的乘法是为了应用四元数表示的旋转到向量上定义的。因此,正确的乘法顺序是 Quaternion * Vector3
。
举例来说:
Quaternion rotation = Quaternion.Euler(30, 0, 0); // 代表旋转的四元数
Vector3 vector = new Vector3(1, 0, 0); // 代表点或方向的向量
// 应用四元数旋转到向量
Vector3 rotatedVector = rotation * vector;
在这个例子中,rotation * vector
的结果是将由 rotation
表示的旋转应用到 vector
上。这里,四元数(旋转)总是位于乘法的左侧,向量位于右侧。
反过来的顺序(即 Vector3 * Quaternion
)在Unity中是不合法的,因为它没有定义,也不符合四元数旋转的数学原理。四元数乘法的这种设计是为了确保旋转可以直观且正确地应用到向量上,同时保持数学上的一致性和物理上的准确性。