UE5中四元数的旋转技巧

2021/08/22 10:43
阅读数 163

UE4_欧拉角

UE4中角度的表示通常为欧拉角

表示形式(X,Y,Z)

欧拉角在Lerp过程中起点和终点都是正确的,但是中间插值的过程是不够顺滑的

UE4的旋转计算过程是(Yaw[Z]→Pitch[Y]→Roll[X])

旋转角过渡:测试角度: 0,45,0旋转到 120,90,100【可以看到旋转绕了一圈】

UE4_万向锁

在欧拉角的情况下

当Y轴为90、-90的时候,X、Z轴旋转肉眼看上去是错误的,也就是万向锁问题

“欧拉角旋转”产生“万向锁”的来源,以及如何避免万向锁_哔哩哔哩_bilibili www.bilibili.com/video/BV1YJ41127qe?from=search&seid=15710488198256819120




UE4_四元数

四元数知识参考:

YivanLee:虚幻4渲染编程(数学篇)【第四卷:游戏中的旋转变换——四元数和欧拉角】50 赞同 · 1 评论文章

四元数可视化讲解:

四元数的可视化_哔哩哔哩_bilibili www.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,和旋转增量Ф;

这是不修改物体与轴之间的距离

自定义物体与轴之间的距离

轴旋转节点

RotateVector / UnrotateVector

旋转向量 / 未旋转向量

向量A旋转B / 向量A反向旋转B

RotateVectorAroundAxis

围绕轴旋转向量

向量 A 绕着 Axis 轴旋转了 Angle Deg 度

Rotator from Axis and Angle

轴和角中的旋转体

以轴A经过Angle旋转后的旋转角


本文分享自微信公众号 - WebHub(myWebHub)。
如有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
0 评论
0 收藏
0
分享
返回顶部
顶部