使用Eigen计算2D相机的模型视图矩阵

4

我试图计算我的二维相机的模型视图矩阵,但我无法得到正确的公式。 我使用Affine3f变换类,因此矩阵与OpenGL兼容。 这是我通过试错所能得到的最接近的结果。 该代码可以正确旋转和缩放相机,但如果我同时应用平移和旋转,则相机的运动会混乱:相机以旋转的方式移动,这不是我想要的。(这可能是由于我先应用旋转矩阵,然后再进行平移)

Eigen::Affine3f modelview;
modelview.setIdentity();
modelview.translate(Eigen::Vector3f(camera_offset_x, camera_offset_y, 0.0f));
modelview.scale(Eigen::Vector3f(camera_zoom_x, camera_zoom_y, 0.0f));
modelview.rotate(Eigen::AngleAxisf(camera_angle, Eigen::Vector3f::UnitZ()));
modelview.translate(Eigen::Vector3f(camera_x, camera_y, 0.0f));
[loadmatrix_to_gl]

我想实现的是,相机围绕屏幕中心点(在此情况下,(0,0)是屏幕中心)旋转和缩放,并最终被定位在全局xy轴上的世界空间中(初始时,(0,0)也在屏幕中心)。我该如何实现呢?
请注意,我还设置了一个正交投影矩阵,可能会影响这个问题。
2个回答

1
如果您想要使用OpenGL在XY平面上渲染2D图像,并按以下方式操作:(1)围绕点P逆时针旋转a(2)进行S比例缩放,然后(3)平移,以便在新缩放和旋转的图像中,像素C位于原点处,则可以使用以下转换:

  1. 通过-P进行平移(这将把P像素移动到原点)
  2. 进行a旋转
  3. 通过P进行平移(将原点移回其原来的位置)
  4. 按比例缩放S(如果提前执行���操作,则会破坏旋转效果)
  5. 通过-C进行平移

如果2D图像正在原点处呈现,则还需要最后通过沿负z轴移动一定值来进行平移,以便能够看到它。

通常,您只需使用OpenGL基础知识(glTranslatefglScalefglRotatef等)即可完成此操作。并且您需要按照我列出的相反顺序执行它们。由于您想要使用glLoadMatrix,因此您需要按照我在Eigen中描述的顺序执行操作。重要的是要记住,OpenGL期望一个列主要矩阵(但这似乎是Eigen的默认设置;因此可能不是问题)。

1
谢谢,现在看起来效果好多了。然而相机的移动方式并不是我期望的:增加x坐标也会影响y坐标,因此相机沿着旋转后的xy轴移动,而不是未旋转的xy轴。这是我解决问题的第二部分,我想让相机只是围绕点旋转和缩放,然后将相机移动到其位置,使得应用的旋转不会影响这个平移。PS:我将尽可能避免使用固定管线,所以改为GL 3.0 +着色器及更高版本将是无痛的。 - JATothrim
@JATothrim 我看了一下Eigen的规范。它说你正在使用的变换是应用于矩阵的右边。我原本以为它们应该应用在左边。由于它们是应用在右边的,所以你需要按相反的顺序应用它们。或者,你可以使用“prerotate”和“pretranslate”。 - JCooper
非常接近但不完全正确。相机现在围绕点旋转和缩放,如果没有旋转,它的平移是可以的。旋转会破坏平移(或反之亦然),相机不会像我期望的那样“移动”。旋转90度,y轴变成了相机运动中的x轴!我该如何防止这种情况发生? - JATothrim
我想我终于解决了这个移动问题。错误在于每一帧都重置了模型视图为身份矩阵,即使相对于当前位置移动相机。因此,从模型视图中提取X向量,然后沿着该方向进行平移是无效的!现在感觉好愚蠢。教训是:可以无限地链接变换而不必回到身份矩阵。 - JATothrim

0

JCooper解释了构建初始矩阵的步骤,做得非常好。

然而,我最终以稍微不同的方式解决了这个问题。当时有一些额外的事情和步骤对我来说并不明显。请参见JCooper答案的评论。首先要意识到所有矩阵操作都是相对的。

因此,如果您想使用绝对xy轴定位或移动相机,则必须首先分解矩阵以提取其具有不变轴的绝对位置。然后,您将矩阵平移与旧位置和新位置之间的差异。

以下是使用Eigen执行此操作的方法:

首先计算Affine2f矩阵cmat标量行列式D。使用Eigen可以通过D = cmat.linear().determinant();完成。接下来,使用D计算当前旋转+缩放矩阵R的“反向”矩阵matrevmatrev = (RS.array() / (1.0f / determ)).matrix());其中RS是cmat.matrix().topLeftCorner(2,2)。然后,绝对相机位置P由P = invmat * -C给出,其中C是cmat.matrix().col(2).head<2>()

现在我们可以沿着绝对轴重新定位相机,并保持旋转+缩放不变:V = RS *(T-P),其中RS与之前相同,T是新的位置向量,P是分解的位置向量。 然后,cmat只需通过V进行平移以移动相机:cmat.pretranslate(V)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接