旋转一个不对称的棱台

3

在我的程序中,我定义了一个不对称的梯形视景体在投影矩阵中,并随着用户的位置而改变。在所有z值都在同一位置的平面的情况下,我的程序可以正常工作。然而,如果观看平面稍微旋转一点(沿Y方向旋转),我将通过对ModelView矩阵进行glRotate来旋转相机。

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
    glFrustum(topLeftX, bottomRightX, bottomRightY, topLeftY, camNear, camFar);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
    gluLookAt(xHeadPosition, yHeadPosition, zHead, xHeadPosition, yHeadPosition, zHead -1 , 0, 1, 0);
glTranslatef(400, 0); //Rotating with the right side of the plane as the pivot
    glRotatef(yRotation, 0, 1, 0);
glTranslatef(-400, 0);

然而,我希望在投影中做同样的事情,以保持MV矩阵的清洁。我尝试按如下方式进行:

glMatrixMode(GL_PROJECTION);
glLoadIdentity();
    glTranslatef(400, 0); // Pre-multiplication here as opposed to post-mulitplication in MV matrix
        glRotatef(yRotation, 0, 1, 0);
    glTranslatef(-400, 0);
glFrustum(topLeftX, bottomRightX, bottomRightY, topLeftY, camNear, camFar);
glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
        gluLookAt(xHeadPosition, yHeadPosition, zHead, xHeadPosition, yHeadPosition, zHead -1 , 0, 1, 0);

然而,以上内容并未给出我所需的输出,似乎也没有将投影平面旋转以使视野平面垂直于用户/相机。如何旋转投影平面,使其成为垂直于用户的平面?

1
那样做是行不通的。 - Bartek Banachewicz
为什么不使用你另一个问题的答案中的链接(https://dev59.com/nnLYa4cB1Zd3GeqPYXsJ#17451331)?它提供了您需要的一切,以在空间中定位屏幕并确定方向。 - Andreas Haferburg
@AndreasHaferburg:我在那段代码中找不到处理任意方向的部分。除此之外,那个答案帮了我很多。你能具体指出代码中处理任意方向的位置吗?顺便说一句,我正在考虑多屏幕的情况,其中一些屏幕的四个角可能没有相同的Z值。 - user1240679
1个回答

1

我认为你真的需要理解glLoadIdentity的作用。它会丢弃矩阵堆栈顶部的内容,并将其替换为单位矩阵。在那些glLoadIdentity调用之间,glTranslate和glRotate没有任何效果(除了消耗一些CPU时间和内存带宽)。类似地,在gluLookAt之前的glLoadIdentity会使glFrustum所做的工作无效。

我的建议:找一本关于线性代数的书,特别是矩阵数学方面的内容,好好学习一下。然后再找一本关于3D图形数学的书,了解如何将前一本书中学到的抽象概念付诸实践。

接下来,你应该熟悉OpenGL顶点变换管道。

最后,请明白拥有一个“干净”的模型视图矩阵是没有意义的。你在第一个代码片段中拥有的就是你想要的:一个可以构建的基础矩阵。


更新

好的,你在思考中有一个小误解:

glLoadIdentity();
// Pre-multiplication here as opposed to post-mulitplication in MV matrix
glTranslatef(400, 0);
glRotatef(yRotation, 0, 1, 0);
glTranslatef(-400, 0);
glFrustum(topLeftX, bottomRightX, bottomRightY, topLeftY, camNear, camFar);

你为什么认为这里需要进行预乘?变换链为:
p_view = MV · p_local

p_clip =  P · p_view

你可以将其缩减为。
p_clip = P · MV · p_local

分成模型视图和投影矩阵并不是为了变换位置,而是为了变换法线。后面这一步仅用于光照目的。但要使其起作用,投影矩阵必须仅包含投影部分,而不包含任何视口放置。换句话说,这个“glTranslate → glRotate → glTranslate”部分不应该在那里。无论如何,如果你决定“哦,我不需要光照”(出于任何原因),你必须在调用glFrustum之后放置它。为什么?因为这样:
在第一种情况下,您的模型视图MV和投影P矩阵如下:
MV = I · gluLookAt · glTranslate · glRotate · glTranslate

 P = I · glFrustum

由于 I · x = x,我们可以省略那个 I。因此位置变换将会是:

v_clip = P · MV · v_local =

       = glFrustum · gluLookAt · glTranslate · glRotate · glTranslate · glRotate · v_local

关于位置(而非法线),您可以在上述变换链中的任何一点上将拆分放置在模型视图和投影变换之间。在您所需的情况下(顺便说一句,这并没有太多意义),应该放在*v_local*之前。

P = glFrustum · gluLookAt · glTranslate · glRotate · glTranslate · glRotate

但正如我已经说过的:只有在您不需要正确转换法线时才有效。


我忘记在第二个代码中写入glMatrixMode,因为我只是手打它,而不是复制粘贴。我对glLoadIdentity和从模型->视图->投影->屏幕的转换管道很熟悉。但是由于缺少了glMatrixMode,看起来像是在投影矩阵中调用了gluLookAt。我希望现在代码有意义了,但我正努力实现与第一个代码相同的旋转,以构建适应一个我的需求的适应矩阵。 - user1240679
谢谢您的解释。我理解得更清楚了,但是我注意到即使按照您所说将glTranslate(..); glRotate(..); glTranslate(..)'放在glFrustum之后,仍然没有输出。根据我在这里http://bit.ly/1b4b2fC中获得的解释,旋转矩阵乘法是为了旋转不同的屏幕而进行的,即对于旋转的屏幕,我应该选择glFrustum()*旋转*平移。这是错误的吗?在我的情况下,如何理想地旋转投影,使其垂直于用户? - user1240679
@user1240679:嗯,从技术上讲,旋转和平移的顺序是正确的,但老实说,用这种方式做起来相当麻烦。更简单的方法是直接写下正确的矩阵。但这个话题可能需要我在一个专门的教程中详细解释,因为它涉及到几个中间级别的知识,在StackOverflow的回答中无法完全涵盖(而且我的猫刚刚占据了我的图形板,所以我现在无法画出漂亮的图片)。 - datenwolf

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