【问题标题】:How to move incrementally in a 3D world using glRotatef() and glTranslatef()如何使用 glRotatef() 和 glTranslatef() 在 3D 世界中逐步移动
【发布时间】:2011-05-10 10:52:43
【问题描述】:

我有一些 3D 模型,我在 3D 空间中使用 OpenGL 渲染,我在移动 “角色”(即相机 ) 在这个世界中进行旋转和平移。

我从一些外部事件(图像用户输入或来自 GPS + 罗盘设备的一些数据)接收输入(即移动的坐标/要转动的坐标),事件类型是 旋转 或 翻译

我写了这个方法来管理这些事件:

- (void)moveThePlayerPositionTranslatingLat:(double)translatedLat Long:(double)translatedLong andRotating:(double)degrees{

    [super startDrawingFrame];
    if (degrees != 0)
    {
        glRotatef(degrees, 0, 0, 1);
    }

    if (translatedLat != 0)
    {
        glTranslatef(translatedLat, -translatedLong, 0);
    }

   [self redrawView];
}

然后在redrawView 我实际上是在绘制场景和我的模型。是这样的:

glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

NSInteger nModels = [models count];

for (NSInteger i = 0; i < nModels; i++) 
{
    MD2Object * mdobj = [models objectAtIndex:i];
    glPushMatrix();
    double * deltas = calloc(sizeof(double),2);

    deltas[0] = currentCoords[0] - mdobj.modelPosition[0];
    deltas[1] = currentCoords[1] - mdobj.modelPosition[1];

    glTranslatef(deltas[0], -deltas[1], 0);

    free(deltas);
    [mdobj setupForRenderGL];
    [mdobj renderGL];   
    [mdobj cleanupAfterRenderGL];
    glPopMatrix();

}
[super drawView];

问题在于,当平移时,旋转事件一个接一个地被调用:例如,当我增量旋转一些迭代(仍然围绕原点)时,我平移并最终再次旋转,但似乎最后一次旋转不会出现在当前(翻译的)位置周围,而是出现在旧位置(旧的原点)周围。我很清楚当转换的顺序颠倒时会发生这种情况,但我相信在绘制之后,世界的新中心是由翻译系统给出的。

我错过了什么?我怎样才能解决这个问题? (任何对 OpenGL 的引用也将不胜感激)

【问题讨论】:

  • 颠倒变换顺序有什么问题?
  • 如果您先执行 glTranslatef() 然后执行 glRotatef() 您将首先旋转对象而不是平移它,否则如果顺序颠倒您将首先平移它然后旋转它。看看这里glprogramming.com/red/chapter03.html

标签: iphone objective-c opengl-es translate glrotate


【解决方案1】:

听起来您在这里发生了几件事:

首先,您需要注意旋转是围绕原点发生的。因此,当您平移然后旋转时,您不是围绕您认为的原点旋转,而是围绕新原点 T-10 (原点由翻译的倒数变换)。

其次,你让事情变得比你真正需要的要困难得多。您可能要考虑使用gluLookAt。你基本上在你的场景中给它一个位置,在你的场景中给它一个要查看的点和一个“向上”向量,它会正确设置场景。要正确使用它,请跟踪您的相机所在的位置,调用该向量 p 和一个向量 n(对于正常 ... 表示您正在寻找的方向) 和 u (你的向上向量)。如果 nu 是正交向量(即它们彼此正交且具有单位长度),则更高级的特征将变得更容易。如果你这样做,你可以计算 r = n x u,(你的“右”向量),这将是一个正交的法向量给另外两个。然后,您“查看”p+n 并提供 u 作为向上向量。

理想情况下,您的 nur 具有一些规范形式,例如:

n = <0, 0, 1>
u = <0, 1, 0>
r = <1, 0, 0>

然后您逐步累积旋转并将它们应用于您的方向向量的规范。您可以使用Euler RotationsQuaternion Rotations 来累积您的旋转(我已经非常欣赏各种reasons 的四元数方法)。

【讨论】:

  • 首先我认识到我做了并且没有必要的额外翻译,事实上在事件处理程序中我正在旋转和翻译整个场景(= 转换视图)并且在显示模式下我' m 像在本地坐标系中一样绘制我的对象(计算与全局玩家位置和他们的位置之间的差异,并通过这些值进行转换)。我会说,如果我删除事件一它会正常运行。
【解决方案2】:

我建议不要在事件处理程序中进行累积转换,而是在内部存储转换的当前值,然后只转换一次,但我不知道这是否是你想要的行为。

伪代码:

someEvent(lat, long, deg)
{
  currentLat += lat;
  currentLong += long;
  currentDeg += deg;
}

redraw()
{
  glClear()
  glRotatef(currentDeg, 0, 0, 1);
  glTranslatef(currentLat, -currentLong, 0);
  ... // draw stuff
}

【讨论】:

  • 我明白你的意思,这是一种防止转换重叠的方法(我想它需要一个 glPushMatrix()/glPopMatrix() 来隔离每个元素转换)。顺便说一句,如果您在这里谈论性能,那么情况并非如此,因为在每个事件中我都需要重绘视图并且通过使用有限的 NSOperationQueue 来保持较低的频率
  • 是的,问题在于重叠转换。你所做的是这样的:平移一点,旋转,再次旋转,平移,旋转等等。由于前面的平移,现在每个旋转都使用不同的旋转中心。如果我说对了,这不是你想要的。因此,您要么必须累积转换的参数,并且只能按照您想要的确切顺序转换一次。或者您必须在每次旋转之前“撤消”先前的翻译(通过应用反向翻译)。
猜你喜欢
  • 2018-06-22
  • 1970-01-01
  • 1970-01-01
  • 2022-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-30
  • 2021-12-17
相关资源
最近更新 更多