UE4中角度的表示通常为欧拉角
表示形式(X,Y,Z)
欧拉角在Lerp过程中起点和终点都是正确的,但是中间插值的过程是不够顺滑的
UE4的旋转计算过程是(Yaw[Z]→Pitch[Y]→Roll[X])
旋转角过渡:测试角度: 0,45,0旋转到 120,90,100【可以看到旋转绕了一圈】
UE4_万向锁
在欧拉角的情况下
当Y轴为90、-90的时候,X、Z轴旋转肉眼看上去是错误的,也就是万向锁问题
“欧拉角旋转”产生“万向锁”的来源,以及如何避免万向锁_哔哩哔哩_bilibiliwww.bilibili.com/video/BV1YJ41127qe?from=search&seid=15710488198256819120
四元数知识参考:
YivanLee:虚幻4渲染编程(数学篇)【第四卷:游戏中的旋转变换——四元数和欧拉角】50 赞同 · 1 评论文章
四元数可视化讲解:
四元数的可视化_哔哩哔哩_bilibiliwww.bilibili.com/video/BV1SW411y7W1
四元数也能表示旋转
四元数的表示形式(x,y,z,w)
旋转角过渡:测试角度: 0,45,0 旋转到 120,90,100【可以看到非常的平滑,直接就转过去了】
四元数的插值方式有线性插值和球面插值
四元数插值参考:
四元数定义、运算、插值_studylearnjava的博客-CSDN博客_四元数运算blog.csdn.net/studylearnjava/article/details/79978141
0.创建四元数
1.欧拉角怎么转换为四元数
UE4的C++和蓝图中都有欧拉角转四元数的方法;如果像自己实现的话可以参考第三种,或者看UE4的源码
FRotator r;
FQuat q = r.Quaternion();
或者
或者
struct Quaternion
{
double w, x, y, z;
};
Quaternion ToQuaternion(double yaw, double pitch, double roll) // yaw (Z), pitch (Y), roll (X)
{
// Abbreviations for the various angular functions
double cy = cos(yaw * 0.5);
double sy = sin(yaw * 0.5);
double cp = cos(pitch * 0.5);
double sp = sin(pitch * 0.5);
double cr = cos(roll * 0.5);
double sr = sin(roll * 0.5);
Quaternion q;
q.w = cy * cp * cr + sy * sp * sr;
q.x = cy * cp * sr - sy * sp * cr;
q.y = sy * cp * sr + cy * sp * cr;
q.z = sy * cp * cr - cy * sp * sr;
return q;
}
2.四元数/旋转体怎么线性插值
UE4C++中提供了四元数线性插值的方法、蓝图中提供了旋转体的简单插值节点,两种插值的效果是一样的
FQuat q1; //起始旋转
FQuat q2; //终点旋转
float f; //插值参数
FQuat q3 = FQuat::FastLerp(q1, q2, f);
3.四元数/旋转体怎么球面插值【让物体或镜头平滑过渡/旋转】【推荐】
球面插值能做到角速度很平滑的旋转
UE4C++中也提供了四元数球面插值的方法、蓝图中旋转体插值节点启用最短路径,两种插值的效果一样
//c++
FQuat q1; //起始旋转
FQuat q2; //终点旋转
float f; //插值参数
FQuat q3 = FQuat::Slerp(q1, q2, f);
4.四元数怎么转换为欧拉角
C++中也能直接进行转换,蓝图中一个节点自动完成转换,或者你可以用C++自己写
FQuat q1;
FRotator r = q1.Rotator();
或者【图中两种方法都可以】
或者
#define _USE_MATH_DEFINES
#include <cmath>
struct Quaternion {
double w, x, y, z;
};
struct EulerAngles {
double roll, pitch, yaw;
};
EulerAngles ToEulerAngles(Quaternion q) {
EulerAngles angles;
// roll (x-axis rotation)
double sinr_cosp = 2 * (q.w * q.x + q.y * q.z);
double cosr_cosp = 1 - 2 * (q.x * q.x + q.y * q.y);
angles.roll = std::atan2(sinr_cosp, cosr_cosp);
// pitch (y-axis rotation)
double sinp = 2 * (q.w * q.y - q.z * q.x);
if (std::abs(sinp) >= 1)
angles.pitch = std::copysign(M_PI / 2, sinp); // use 90 degrees if out of range
else
angles.pitch = std::asin(sinp);
// yaw (z-axis rotation)
double siny_cosp = 2 * (q.w * q.z + q.x * q.y);
double cosy_cosp = 1 - 2 * (q.y * q.y + q.z * q.z);
angles.yaw = std::atan2(siny_cosp, cosy_cosp);
return angles;
}
5.用欧拉角递增x/y/z轴(使用飞行模板,解决飞行器万向锁问题)
void UMyBlueprintFunctionLibrary::SetWorldRotationQuat(AActor* Actor, const FQuat& Target_Rotation)
{
if (Actor)
{
Actor->SetActorRotation(Target_Rotation);
}
}
void UMyBlueprintFunctionLibrary::SetRelativeRotationQuat(AActor* Actor, const FQuat& Target_Rotation)
{
if (Actor)
{
Actor->SetActorRelativeRotation(Target_Rotation);
}
}
void UMyBlueprintFunctionLibrary::AddLocalRotationQuat(AActor* Actor, const FQuat& DeltaRotation)
{
if (Actor)
{
Actor->AddActorLocalRotation(DeltaRotation);
}
}
6.其实自己也尝试了其他的旋转方案,发现都没有球面插值好,就不献丑了
思路:
1.获取物体A、中心B位置,计算向量BA;
2.向量BA旋转Ф角度加上中心B位置,得到新位置C;
3.设置原来物体A的到新位置C,和旋转增量Ф;
这是不修改物体与轴之间的距离
自定义物体与轴之间的距离
旋转向量 / 未旋转向量
向量A旋转B / 向量A反向旋转B
围绕轴旋转向量
向量 A 绕着 Axis 轴旋转了 Angle Deg 度
轴和角中的旋转体
以轴A经过Angle旋转后的旋转角