围绕世界轴OpenGL旋转对象

Posted

技术标签:

【中文标题】围绕世界轴OpenGL旋转对象【英文标题】:Rotation of object around World Axis OpenGL 【发布时间】:2016-08-23 13:09:51 【问题描述】:

我正在尝试在 OpenGL 中制作一个可控球。我正在使用我自己的矩阵类来转换对象矩阵,但我似乎无法正确旋转。我总是以球围绕局部轴旋转结束。 这就是它现在的样子 https://gfycat.com/LongKindBassethound 。长线是局部轴。

所以当球向前移动时,下一个侧面的移动将是错误的。矩阵类中有一个函数可以绕任意轴旋转:

Matrix& Matrix::rotationAxis(const Vector& Axis, float Angle) 
const float Si = sin(Angle);
const float Co = cos(Angle);
const float OMCo = 1 - Co;
Vector Ax = Axis;
Ax.normalize();

m00= (Ax.X * Ax.X) * OMCo + Co;
m01= (Ax.X * Ax.Y) * OMCo - (Ax.Z * Si);
m02= (Ax.X * Ax.Z) * OMCo + (Ax.Y * Si);
m03= 0;

m10= (Ax.Y * Ax.X) * OMCo + (Ax.Z * Si);
m11= (Ax.Y * Ax.Y) * OMCo + Co;
m12= (Ax.Y * Ax.Z) * OMCo - (Ax.X * Si);
m13= 0;

m20= (Ax.Z * Ax.X) * OMCo - (Ax.Y * Si);
m21= (Ax.Z * Ax.Y) * OMCo + (Ax.X * Si);
m22= (Ax.Z * Ax.Z) * OMCo + Co;
m23= 0;

m30= 0;
m31= 0;
m32= 0;
m33= 1;

return *this;

我认为这样我可以获取世界方向向量并将它们转换到对象的局部空间,然后围绕结果旋转。我真的不知道该怎么做(球的矩阵*世界矢量?那行不通)。我真的很想避免使用四元数,但如果我不能这样做,我也会很感激这个方向的建议。

编辑:更多信息

转换代码。正如你所看到的,我尝试了不同的方法,它们都做同样的事情......所以它不起作用也就不足为奇了。

Matrix transM, rotX, rotZ;
rotationX = straight;
rotationZ = side;
if (velocity != Vector(0, 0, 0)) 
    velocity.X = -0.0005 * DeltaTime;
    velocity.X = clamp(velocity.X, 0, FLT_MAX);

    velocity.Z = -0.0005 * DeltaTime;
    velocity.Z = clamp(velocity.Z, 0, FLT_MAX);


velocity.X += speed * -side * DeltaTime;
velocity.Z += speed * straight * DeltaTime;
transM.translation(velocity.X, 0, velocity.Z);

if (velocity.Z != 0 || velocity.X != 0) 
    //http://mathworld.wolfram.com/EulerAngles.html
    //http://gamedev.stackexchange.com/questions/67199/how-to-rotate-an-object-around-world-aligned-axes

    Vector localAxisX =  m_Ball * Vector(1, 0, 0);
    Vector localAxisZ = m_Ball * Vector(0, 0, 1);
    rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime);
    rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime);
    //rotX.rotationX(0.5* M_PI * straight * DeltaTime * 3);
    //rotZ.rotationZ(0.5* M_PI * side * DeltaTime * 3);
    //Matrix fullRotation.rotationYawPitchRoll(Vector(0,  0.5* M_PI * straight * DeltaTime, 0.5* M_PI * side * DeltaTime));
    m_Ball = transM * m_Ball * (rotX*rotZ);

else 
    m_Ball = transM * m_Ball;

使用我之前尝试使用 glRotatef 的尝试绘制代码(现在显然已注释掉)

void Ball::draw(float DeltaTime) 
  glPushMatrix();
  glMultMatrixf(m_Ball);
  if(rotationX)
    glRotatef(0.5* M_PI * rotationX * DeltaTime, 1.0, 0.0, 0.0);
  if(rotationZ)
    glRotatef(0.5* M_PI * rotationZ * DeltaTime, 0.0, 0.0, 1.0);
  g_Model_ball.drawTriangles();
  glPopMatrix();
  drawAxis();

【问题讨论】:

请发布您调用此函数的驱动程序代码,其次您是否有此要求来纠正您自己的旋转函数?如果没有,你可以使用 glRotatef() 编辑更多信息 请看这篇文章以了解 OpenGL 转换的工作原理open.gl/transformations 查看 Understanding 4x4 homogenous transform matrices 并在项目符号 #5 中查找局部和全局旋转之间的区别 这就是我要搜索的!谢谢老兄,现在可以了。我会更新一个解决方案。我还在做一些奇怪的事情,但它有效! 【参考方案1】:

我强烈建议使用四元数来轻松处理复合旋转并避免万向架锁定。

https://en.wikipedia.org/wiki/Gimbal_lock

好的,关于你的 cmets 和视频,你想围绕球的中心旋转。看起来你在m_Ball 中累积了你的旋转,但是做了一个奇怪的transM 乘法。另外,您可能正在transM 中积累翻译。

尽量不要混合您的平移和旋转,并避免将它们累积在您的 m_Ball 中。你可以这样做。

//transformation matrix
transM.translation(velocity.X, 0, velocity.Z);

//accumulate translations in m_BallT
m_BallT = transM * m_BallT;

//final Rotation
Matrix rotation = rotX * rotZ;

//accumulate rotations in m_BallR
m_BallR = rotation * m_BallR;

//now compute your final transformation matrix from accumulated rotations and translation
m_Ball = m_BallT * m_BallR;

注意m_BallR 只是累积的旋转。后乘将确保在m_BallR 中累积旋转后应用新的rotation。最后转换为m_BallT中累积的最终位置。你的球会绕着它的中心旋转并按照m_BallT移动。

您也可以简单地替换 m_BallR 上的转换组件以避免额外的矩阵乘法。

【讨论】:

我编辑了我的帖子。如果我理解正确的话,我很确定我已经这样做了。 我不认为你这样做,从你的球中提取世界空间位置,用旋转矩阵旋转该位置向量,然后将球移动到新位置。不要将复合旋转矩阵与球的矩阵相乘。 gfycat.com/BowedCaringArkshell 这是我使用您的方法时的结果。我的目标是:gfycat.com/LongKindBassethound 适当旋转 @lega OK 从问题中并不清楚。检查更新的答案。【参考方案2】:
Vector newPos(m_Ball.translation().X + velocity.X, terrainNoise.GetHeight(m_Ball.translation().X, m_Ball.translation().Z) + 0.5, m_Ball.translation().Z + velocity.Z);

rotX.rotationAxis(Vector(1, 0, 0), 0.5* M_PI * straight * DeltaTime * abs(velocity.Z) * 100);
rotZ.rotationAxis(Vector(0, 0, 1), 0.5* M_PI * side * DeltaTime * abs(velocity.X) * 100);

m_Rotation = (rotX*rotZ);

m_Ball = (m_Ball.invert() * m_Rotation).invert();


m_Ball.m03 = newPos.X;
m_Ball.m13 = newPos.Y;
m_Ball.m23 = newPos.Z;

这是我在阅读@Spektre 提供的this 链接后提出的解决方案。基本上,您只需反转球的 ModelMatrix 以使其进入世界位置,进行旋转,然后将其转换回本地空间。

旋转前必须设置好newPos Vector,否则会影响以后的变换。

【讨论】:

您可以通过分别累积旋转和平移来避免矩阵求逆。会更有效率。由于无论如何您都覆盖了平移向量,因此实际上您只是在反转旋转和任何比例。 @Harish 实际上效率并不高,因为许多原因它恰恰相反......而且您不能以这种方式堆叠任意转换。顺便说一句,在正交同质变换矩阵上求逆真的很容易,因为它只是 transpose() ... @Spektre 如果你做 A = (A' x B)' 你说它比 A = B x A 更有效?我在这里想念什么?第一种情况你怎么不积累?无论 A' = transpose(A) 与否,您仍在以不同的方式累积旋转。 @Harish 一旦你分离了平移和旋转矩阵,你就会得到 2 个单独的矩阵,而不是 1 个,这使得无法堆叠任意变换。在最终渲染中,您需要将变换应​​用于所有顶点,因此乘以单个矩阵然后在每个顶点基础上应用其中 2 个矩阵会更快。这就是为什么 2 矩阵的情况不是更有效的原因。过去,它用于不存在复杂运动且所有计算都在 CPU/FPU 上完成的特殊情况。这种方法在现代 GPU 架构上表现不佳 @Spektre 不一定是两个矩阵乘法,我只是从他的原始实现中转移了变换矩阵代码,以简化他的理解。您也可以像他在这个答案中所做的那样简单地累积旋转并替换转换部分。我不喜欢做不必要的转置或反转,因为一个 cl 总是单独保持旋转并在翻译之前应用它们以获得他正在寻找的效果。

以上是关于围绕世界轴OpenGL旋转对象的主要内容,如果未能解决你的问题,请参考以下文章

如何在 OpenGL 中围绕 Z 轴中心旋转对象?

使用OpenGL在C ++中同时围绕其(x,y&z)轴旋转对象

围绕对象旋转的矩阵乘法opengl

Push/Pop 矩阵和单个对象在 OpenGL 中围绕其自己的轴旋转

OpenGL - 围绕 Y 轴旋转“曲线”

OpenGL - 使用 glm 围绕 2 个轴旋转