Objective c 如何相对于视图中心旋转OpenGL视图,而不是相对于所显示对象的中心旋转?

Objective c 如何相对于视图中心旋转OpenGL视图,而不是相对于所显示对象的中心旋转?,objective-c,opengl,Objective C,Opengl,我正在做一件事 旋转显示的对象时,对象始终围绕同一点相对于自身旋转,即使该点不在视图中心,例如,因为用户已平移视图中的对象 我想对此进行更改,以便视图始终围绕用户看到的视图中心点旋转对象,而不是对象的中心 以下是当前代码的核心,该代码将对象围绕其中心旋转,稍微简化如下: 为了达到我想要的效果,我需要进行哪些转换 到目前为止我试过的一些 据我所知,这就是当前代码的功能: 如果将多个变换应用于来自的顶点,则OpenGL将按相反顺序执行矩阵乘法。这意味着要应用的第一个转换实际上是上面代码中的最后一个转

我正在做一件事

旋转显示的对象时,对象始终围绕同一点相对于自身旋转,即使该点不在视图中心,例如,因为用户已平移视图中的对象

我想对此进行更改,以便视图始终围绕用户看到的视图中心点旋转对象,而不是对象的中心

以下是当前代码的核心,该代码将对象围绕其中心旋转,稍微简化如下:

为了达到我想要的效果,我需要进行哪些转换

到目前为止我试过的一些 据我所知,这就是当前代码的功能:

如果将多个变换应用于来自的顶点,则OpenGL将按相反顺序执行矩阵乘法。这意味着要应用的第一个转换实际上是上面代码中的最后一个转换。它将视图的中心移动0,0到对象的中心

然后,该点将用作下两个变换(即旋转)的旋转中心

最后,以相反的方式执行中间平台平移,以将中心移回原始位置,并应用用户执行的XY平移平移。在此,相机也从对象移动到cameraOffset指示的正确位置

这似乎很简单。所以我需要改变的不是将视图的中心转换为对象的中心,而是将它转换为用户看到的当前视图中心,对吗

不幸的是,这就是转换开始以有趣的方式相互影响的地方,我遇到了麻烦

我尝试将代码更改为:

glLoadIdentity(); 

glTranslatef(0, 
             0, 
             (GLfloat)cameraOffset);

if (trackBallRotation[0] != 0.0f) {
    glRotatef (trackBallRotation[0], trackBallRotation[1], trackBallRotation[2], trackBallRotation[3]);
}
// accumlated world rotation via trackball
glRotatef (worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]);

glTranslatef(cameraTranslateX, cameraTranslateY, 0.);
换句话说,我将视图的中心平移到上一个中心,围绕该中心旋转,然后应用摄影机偏移以将摄影机移到正确的位置。这使得旋转完全按照我希望的方式进行,但它引入了一个新问题。现在,用户所做的任何平移都是相对于对象的。例如,如果旋转对象,使相机沿X轴末端观看,如果用户从左向右平移,则对象似乎离用户更近/更远,而不是向左或向右移动

我想我能理解为什么在旋转之前应用XY相机平移,我想我需要做的是找出一种方法,在旋转之前,在旋转之后,取消平移,以避免奇怪的平移效果,然后再做另一个平移,相对于观察者的眼睛坐标空间,而不是物体坐标空间,进行平移,但我不确定具体怎么做

我在OpenGL常见问题解答中找到了一些线索http://www.opengl.org/resources/faq/technical/transformations.htm,例如:

9.070如何围绕固定坐标系而不是对象的局部坐标系变换对象

如果围绕对象的Y轴旋转对象,则会发现X轴和Z轴随对象旋转。随后围绕其中一个轴的旋转将围绕新变换的轴而不是原始轴旋转。通常需要在固定坐标系而不是对象的局部坐标系中执行变换

问题的根本原因是OpenGL矩阵操作在矩阵堆栈上进行了后乘,从而导致在对象空间中发生变换。要影响屏幕空间变换,需要预乘。OpenGL不提供矩阵乘法顺序的模式开关,因此需要手动预乘法。应用程序可以通过在每个帧后检索当前矩阵来实现这一点。应用程序将下一帧的新变换乘以单位矩阵,并使用glMultMatrix将上一帧的累积当前变换乘以这些变换

您需要知道,每帧检索一次ModelView矩阵可能会对应用程序的性能产生不利影响。但是,您需要对该操作进行基准测试,因为不同的实现的性能会有所不同

9.120如何找到仅通过ModelView矩阵变换的顶点坐标

获取顶点的眼睛坐标空间值(即通过ModelView矩阵变换的对象空间顶点)通常很有用。您可以通过检索当前ModelView矩阵并执行简单的向量/矩阵乘法来获得此信息


但我不知道如何将这些应用到我的情况中。

您需要将视点中心转换为原点,旋转,然后反转 平移,返回到对象的变换。这在线性代数中称为a

如果您有一个合适的3d数学库,那么这将更容易使用,我假设您确实有一个3d数学库,这也有助于远离不推荐的固定管道API。稍后再谈

我是这样做的:

在世界坐标系中找到视点中心的变换,算出它,然后绘制它以确保其正确,还有x、y、z轴,因为轴应该是正确的w.r.t.视图。如果使用视点中心,且旋转通常与摄影机旋转相反,则这将是从世界原点到视图中心的变换。将其存储在4x4矩阵变换中

应用上述变换的逆,使其成为原点。glMultMatrixfvcenter(视图反方向的中心)

围绕该点旋转,但要旋转

将所有内容转换回世界空间glMultMatrixfvcenter(视图的中心)

应用对象自己的世界变换glTranslate/glRotate或glMultMatrix并绘制它

关于固定功能管道

在过去,有单独的晶体管用于变换顶点或其纹理坐标,计算光线与顶点的关系,应用多达8个灯光,并以多种不同方式对碎片进行纹理处理。简单地说,glEnable使固定的硅块能够在硬件图形管道中进行一些计算。随着性能的提高、芯片尺寸的缩小以及人们对更多功能的需求,专用硅的数量也随之增加,而且大部分没有被使用

最终,它变得如此先进,以至于你可以用相当猥亵的方式编程。然后,为所有顶点级别的变换上传一个小的汇编程序就变得可行了。然后,在那里保留大量的硅是有意义的,它们只做了一件事,特别是当你可以使用这些晶体管使可编程的东西更快,所以一切都变得可编程。如果需要固定函数渲染,驱动程序只需将状态X灯光、纹理投影等转换为着色器代码,并将其作为顶点着色器上载

因此,目前,即使片段处理是可编程的,也只有很多固定的功能选项被无数的OpenGL应用程序使用,但GPU上的硅只是并行运行着色器和很多着色器

为了使OpenGL更高效、驱动程序体积更小、硬件更简单且可在移动/控制台设备上使用,并充分利用OpenGL目前运行的可编程硬件,API中的许多函数现在被标记为不推荐使用。它们在OpenGL ES 2.0及更高版本的移动设备上不可用,即使在桌面系统上,您也无法从中获得最佳性能,因为在桌面系统中,它们仍将在驱动程序中使用很长一段时间,为同样古老的代码库提供服务,这些代码源于加速3d图形的曙光

固定功能主要关注OpenGL中默认情况下如何进行变换/照明/纹理等,即glEnableGL_照明,而不是在自定义着色器中指定这些操作

在新的可编程OpenGL中,变换矩阵在着色器中是统一的。在上传到OpenGL之前,任何类似上述的旋转/平移/多重/反向操作都应该由客户端代码完成。仅使用glLoadMatrix是开始考虑它的一种方法,但是不要在着色器中使用gl_ModelViewProjectionMatrix和类似的工具,而是使用您自己的制服

这有点麻烦,因为您必须实现GL驱动程序以前所做的很多工作,但是如果您有自己的对象列表/图形,其中包含变换和某个地方的变换等,那么就不需要做那么多工作。OTOH,如果您的代码中有很多glTranslate/glRotate,那么它可能是。。。。正如我所说,一个好的3d数学库在这里是不可或缺的

-

因此,要将上述代码更改为可编程管道样式,您只需在自己的代码中执行所有这些矩阵乘法,而不是由GL驱动程序执行,仍然在CPU上,然后在激活着色器并从VBOs绘制对象之前,将生成的矩阵以统一形式发送到opengl

请注意,现代卡没有固定的函数代码,只是驱动程序中有很多代码,用于将固定函数渲染状态编译到执行此任务的着色器。难怪经典的GL驱动程序是巨大的


关于这个过程的一些信息可以在谷歌上找到,可能也可以在谷歌上找到。

您需要将视点中心转换为原点,旋转,然后反转该转换,返回到对象的转换。这在线性代数中称为a

如果您有一个合适的3d数学库,那么这将更容易使用,我假设您确实有一个3d数学库,这也有助于远离不推荐的固定管道API。稍后再谈

我是这样做的:

找到 在世界坐标系中变换视点的中心,计算出它,然后绘制它以确保它是正确的,还有x、y、z轴,因为轴应该是正确的w.r.t.视图。如果使用视点中心,且旋转通常与摄影机旋转相反,则这将是从世界原点到视图中心的变换。将其存储在4x4矩阵变换中

应用上述变换的逆,使其成为原点。glMultMatrixfvcenter(视图反方向的中心)

围绕该点旋转,但要旋转

将所有内容转换回世界空间glMultMatrixfvcenter(视图的中心)

应用对象自己的世界变换glTranslate/glRotate或glMultMatrix并绘制它

关于固定功能管道

在过去,有单独的晶体管用于变换顶点或其纹理坐标,计算光线与顶点的关系,应用多达8个灯光,并以多种不同方式对碎片进行纹理处理。简单地说,glEnable使固定的硅块能够在硬件图形管道中进行一些计算。随着性能的提高、芯片尺寸的缩小以及人们对更多功能的需求,专用硅的数量也随之增加,而且大部分没有被使用

最终,它变得如此先进,以至于你可以用相当猥亵的方式编程。然后,为所有顶点级别的变换上传一个小的汇编程序就变得可行了。然后,在那里保留大量的硅是有意义的,它们只做了一件事,特别是当你可以使用这些晶体管使可编程的东西更快,所以一切都变得可编程。如果需要固定函数渲染,驱动程序只需将状态X灯光、纹理投影等转换为着色器代码,并将其作为顶点着色器上载

因此,目前,即使片段处理是可编程的,也只有很多固定的功能选项被无数的OpenGL应用程序使用,但GPU上的硅只是并行运行着色器和很多着色器

为了使OpenGL更高效、驱动程序体积更小、硬件更简单且可在移动/控制台设备上使用,并充分利用OpenGL目前运行的可编程硬件,API中的许多函数现在被标记为不推荐使用。它们在OpenGL ES 2.0及更高版本的移动设备上不可用,即使在桌面系统上,您也无法从中获得最佳性能,因为在桌面系统中,它们仍将在驱动程序中使用很长一段时间,为同样古老的代码库提供服务,这些代码源于加速3d图形的曙光

固定功能主要关注OpenGL中默认情况下如何进行变换/照明/纹理等,即glEnableGL_照明,而不是在自定义着色器中指定这些操作

在新的可编程OpenGL中,变换矩阵在着色器中是统一的。在上传到OpenGL之前,任何类似上述的旋转/平移/多重/反向操作都应该由客户端代码完成。仅使用glLoadMatrix是开始考虑它的一种方法,但是不要在着色器中使用gl_ModelViewProjectionMatrix和类似的工具,而是使用您自己的制服

这有点麻烦,因为您必须实现GL驱动程序以前所做的很多工作,但是如果您有自己的对象列表/图形,其中包含变换和某个地方的变换等,那么就不需要做那么多工作。OTOH,如果您的代码中有很多glTranslate/glRotate,那么它可能是。。。。正如我所说,一个好的3d数学库在这里是不可或缺的

-

因此,要将上述代码更改为可编程管道样式,您只需在自己的代码中执行所有这些矩阵乘法,而不是由GL驱动程序执行,仍然在CPU上,然后在激活着色器并从VBOs绘制对象之前,将生成的矩阵以统一形式发送到opengl

请注意,现代卡没有固定的函数代码,只是驱动程序中有很多代码,用于将固定函数渲染状态编译到执行此任务的着色器。难怪经典的GL驱动程序是巨大的


关于这一过程的一些信息可以在谷歌上找到,也可能在谷歌上找到。

Ugh。。。。不推荐使用的功能和固定的功能管道。你不需要任何麻烦,我愿意考虑从头开始重写这个逻辑,如果这是我可能能够正确地做的事情,而不需要花太多的时间在项目的这一部分上,特别是如果我现在只需交换转换逻辑并留下绘图逻辑就行了。请记住,这是我第一次进入OpenGL,有没有一个好的教程,介绍如何在不使用[d]eprecated功能和固定功能管道的情况下正确地完成这项工作?如果它解释了什么是固定函数管道以及为什么它是一件坏事,那么就有好处了。不过老实说,我更喜欢基于现有代码的解决方案和关于固定函数管道的教程
用正确的方法做。啊。。。。不推荐使用的功能和固定的功能管道。你不需要任何麻烦,我愿意考虑从头开始重写这个逻辑,如果这是我可能能够正确地做的事情,而不需要花太多的时间在项目的这一部分上,特别是如果我现在只需交换转换逻辑并留下绘图逻辑就行了。请记住,这是我第一次进入OpenGL,有没有一个好的教程,介绍如何在不使用[d]eprecated功能和固定功能管道的情况下正确地完成这项工作?如果它解释了什么是固定函数管道以及为什么它是一件坏事,那么就有好处了。不过老实说,我更喜欢基于现有代码的解决方案和正确方法的教程。这看起来像是我需要的信息。谢谢。一旦我在代码中实现了它并确保它能正常工作,我就会接受它。固定函数管道信息也很有趣,不过在我理解你所说的很多内容之前,我还需要做更多的研究/学习。我确实明白,现在应该在代码中进行平移/旋转,以生成矩阵,然后将其提供给OpenGL。我还没有做到这一点,但这肯定有助于我前进,所以我将继续接受它。我想我已经根据OpenGL FAQ 9.120找到了视图中心的顶点,但还没有找到如何包含正确的相机平移。这看起来像是我需要的信息。谢谢。一旦我在代码中实现了它并确保它能正常工作,我就会接受它。固定函数管道信息也很有趣,不过在我理解你所说的很多内容之前,我还需要做更多的研究/学习。我确实明白,现在应该在代码中进行平移/旋转,以生成矩阵,然后将其提供给OpenGL。我还没有做到这一点,但这肯定有助于我前进,所以我将继续接受它。我想我已经根据OpenGL FAQ 9.120找到了视图中心的顶点,但还没有找到如何包含正确的相机平移。
glLoadIdentity(); 

glTranslatef(0, 
             0, 
             (GLfloat)cameraOffset);

if (trackBallRotation[0] != 0.0f) {
    glRotatef (trackBallRotation[0], trackBallRotation[1], trackBallRotation[2], trackBallRotation[3]);
}
// accumlated world rotation via trackball
glRotatef (worldRotation[0], worldRotation[1], worldRotation[2], worldRotation[3]);

glTranslatef(cameraTranslateX, cameraTranslateY, 0.);