偏航,俯仰和滚动到glm ::旋转

时间:2014-10-24 19:35:19

标签: c++ opengl rotation leap-motion

我一直试图弄清楚这几个小时,我根本无法想出一个解决方案:我想要实现的是将方向向量转换为3个glm::rotation调用。

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

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

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

/// 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渲染循环中,我每帧提取尖端位置以及方向向量。我将模型矩阵作为一个统一变量传递给我的着色器,方法如下:

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)

然而,当我试图应用它时,我没有得到正确的方向,并且圆柱体正在“闪烁”并在许多方向之间跳跃。


重要的转换函数位于我的柱面类继承的接口...

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和一般情况下扫描了很多关于旋转主题的类似问题,但我不确定我的代码究竟出现了什么问题。

1 个答案:

答案 0 :(得分:5)

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

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

关于原点的旋转有三个自由度。这意味着,您需要三个独立参数来唯一地指定正文的方向。正好三个,不多也不少。无论他们是什么,只要他们都不能表达其他的条款。可以通过多种方式指定3D旋转。最常用的方法是:

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

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

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

我建议你使用glm::orientation函数,它根据方向向量和向上向量的输入构建方向矩阵。

对于从指定的SDK函数获得的偏航,俯仰和滚转角度。此功能不会以您尝试使用它们的方式工作。您应该指定向上矢量。例如,让我们考虑一下,向上矢量是y轴的方向。这意味着,侧倾角始终为零。您应该像以前一样计算偏航角度。所以:

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

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

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

在构建变换矩阵时,首先应该应用适合于俯仰角度的旋转,然后应用适合于偏航旋转的旋转,然后进行平移。

有关Tait-Bryan角度的更多信息,请参阅this

另外,你应该意识到旋转不是可交换的。这意味着您应该按严格的顺序执行旋转。选择Tait-Bryan角度的旋转轴有六种可能性,称为约定。这些约定取决于进行旋转的轴。