c++ - 偏航、俯仰和滚动到 glm::rotate

标签 c++ opengl rotation leap-motion

几个小时以来,我一直在努力解决这个问题,但我根本想不出一个解决方案:我想要实现的是将方向 vector 转换为 3 glm::rotation调用。

我有一个位于 (0, 0, 0) 的圆柱体,具有一定的半径和高度。使用 Leap Motion SDK,我正在执行工具跟踪,为我提供尖端位置和方向 vector (表示为指向与尖端相同方向的单位 vector ):

enter image description here 来源:https://developer.leapmotion.com/documentation/skeletal/java/api/Leap.Pointable.html

此外,yawpitchroll 可以使用以下 SDK 函数从该 vector 中提取:

/// The yaw angle in radians.
///
/// Yaw is the angle between the negative z-axis and the projection of
/// the vector onto the x-z plane. In other words, yaw represents rotation
/// around the y-axis. If the vector points to the right of the negative z-axis,
/// then the returned angle is between 0 and pi radians (180 degrees);
/// if it points to the left, the angle is between 0 and -pi radians.
///
/// \image html images/Math_Yaw_Angle.png
///
/// @returns The angle of this vector to the right or left of the negative z-axis.
float yaw() const 
{
  return std::atan2(x, -z);
}

/// The pitch angle in radians.
///
/// Pitch is the angle between the negative z-axis and the projection of
/// the vector onto the y-z plane. In other words, pitch represents rotation
/// around the x-axis.
/// If the vector points upward, the returned angle is between 0 and pi radians
/// (180 degrees); if it points downward, the angle is between 0 and -pi radians.
///
/// \image html images/Math_Pitch_Angle.png
///
/// @returns The angle of this vector above or below the horizon (x-z plane).
float pitch() const {
  return std::atan2(y, -z);
}

/// The roll angle in radians.
///
/// Roll is the angle between the negative y-axis and the projection of
/// the vector onto the x-y plane. In other words, roll represents rotation
/// around the z-axis. If the vector points to the left of the negative y-axis,
/// then the returned angle is between 0 and pi radians (180 degrees);
/// if it points to the right, the angle is between 0 and -pi radians.
///
/// \image html images/Math_Roll_Angle.png
///
/// Use this function to get roll angle of the plane to which this vector is a
/// normal. For example, if this vector represents the normal to the palm,
/// then this function returns the tilt or roll of the palm plane compared
/// to the horizontal (x-z) plane.
///
/// @returns The angle of this vector to the right or left of the y-axis.
float roll() const {
  return std::atan2(x, -y);
}

在我的 OpenGL 渲染循环中,我在每一帧中提取尖端位置和方向 vector 。我通过以下方式将模型矩阵作为统一变量传递给我的着色器:

cylinder->ResetModelMatrix(); // Set model matrix to identity
glm::vec3 translation = glm::vec3(toolTipPosition.x, toolTipPosition.y, toolTipPosition.z);
cylinder->TranslateModel(translation);
cylinderProgram->SetUniform("modelMatrix", cylinder->GetModelMatrix());

仅通过翻译,效果很好,看起来像这样(圆柱体始终朝上): enter image description here

当我尝试根据 Leap 的工具方向旋转圆柱体时出现问题。 我有以下代码:

yaw = toolDirection.yaw() * Leap::RAD_TO_DEG;
// similarly for pitch and roll

cylinder->Rotate(yaw, glm::vec3(0.0f, 1.0f, 0.0f));
// pitch around (1, 0, 0) and roll around (0, 0, 1)

但是,我没有获得正确的方向,当我尝试应用此方法时,圆柱体“闪烁”并在许多方向之间跳跃。


重要的转换函数位于我的圆柱类继承的接口(interface)中...

void Drawable::ResetModelMatrix()
{
    this->modelMatrix = glm::mat4(1.0f);
}

void Drawable::TranslateModel(glm::vec3 translationVector)
{
    this->modelMatrix = glm::translate(this->modelMatrix, translationVector);
}

void Drawable::RotateModel(float angle_in_degrees, glm::vec3 axisOfRotation)
{
    this->modelMatrix = glm::rotate(this->modelMatrix, angle_in_degrees, axisOfRotation);
}

void Drawable::ScaleModel(glm::vec3 scalingVector)
{
    this->modelMatrix = glm::scale(this->modelMatrix, scalingVector);
}

...以modelMatrix作为成员变量。

我在 OpenGL 中和一般情况下扫描了很多关于旋转主题的类似问题,但我不太确定我的代码到底出了什么问题。

最佳答案

我猜对我们所拥有的和我们想要得到的存在一些误解。

情况是,方向 vector 并不能唯一确定物体在空间中的朝向。为了使这个陈述更清楚,让我们仔细看看如何在 3D 空间中表示旋转。

绕原点的旋转具有三个自由度。这意味着,您需要三个独立的参数来唯一指定物体的方向。正好三个,不多也不少。不管它们是什么,只要它们都不能用其他术语来表达。可以通过多种方式指定 3D 旋转。最常用的方法是:

  • 欧拉角。三个独立的参数。
  • Tait-Bryan 角(偏航角、俯仰角和滚转角)。也是三个独立的参数。不要与欧拉角混淆,它们是不一样的!
  • 轴角表示法。通过单位 vector 和角度指定旋转。您可能会争辩说,这种情况有四个参数。然而事实并非如此,因为这些参数不是独立的。事实上,单位 vector 的长度是有限制的。或者您可以将其视为 vector 的长度不会影响旋转。因此,同样存在三个独立的参数。
  • 方向四元数。和前面的方法一样,四个参数,就是依赖。通常,方向四元数可以被视为编码轴角表示的一种方式。四元数的长度不影响旋转,通常假定四元数具有单位长度。因此,我们有四个参数和一个约束,这意味着我们有三个独立的自由度。

让我们仔细看看您的问题。你有一个方向 vector ,你想用它来确定圆柱体的方向。本质上,您只有两个独立的参数,因为方向 vector 的长度无关紧要(为简单起见,让它成为一个单位 vector )。所以你不能只从方向 vector 获得旋转,因为会有一个任意参数。

从仅包含两个独立参数的输入中获取三个未知的独立参数是不可能的。需要一些额外的约束。通常它是一个向上 vector 。附加约束将三个未知的独立参数转换为相关参数。

我建议您使用 glm::orientation从方向 vector 和向上 vector 的输入构建方向矩阵的函数。

至于偏航角、俯仰角和横滚角,您可以从指定的 SDK 函数中获取。此功能不会以您尝试使用它们的方式工作。您应该指定一个向上 vector 。例如,让我们考虑一下,向上 vector 是 y 轴的方向。这意味着,滚动角将始终为零。您应该像以前一样计算偏航角。所以:

float yaw = std::atan2(x, -z);

俯仰角将是 std::atan2(y, -z),但在旋转的框架中,而不是在原始框架中。您应该将俯仰角视为方向 vector 在 x-z 平面上的投影长度与其在 y 轴上的投影之比的反正切。我的意思是:

float projectionLength = std::sqrt(x * x + z * z);
float pitch = std::atan2(y, projectionLength);

构建变换矩阵时,应首先应用适合俯仰角的旋转,然后应用适合偏航旋转的旋转,然后是平移。

参见 this有关 Tait-Bryan 角的更多信息

此外,您应该意识到旋转不可交换的事实。这意味着您应该按照严格的顺序执行轮换。 Tait-Bryan 角的旋转轴有六种可能,称为约定。这些约定取决于执行旋转所围绕的轴。

关于c++ - 偏航、俯仰和滚动到 glm::rotate,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26555040/

相关文章:

c++ - AVL树:词典的实际使用

opengl - glBufferData分配的内存什么时候释放?

iphone - 三角学 - 旋转物体以背离一条线

java - 如何在 GC 内绘制旋转文本

android - 如何在 android 中按 Y 轴旋转 View ?

c++ - 将行插入 QSqlTableModel

c++ - QWidgetWindow 在 Qt5 的事件过滤器中作为 QObject

c++ - OpenGL 3.2 核心配置文件指南

opengl - 早期模板剔除

c++ - 无法使用编译器将我的 C++ 文件放入库中