设置一个不对称的梯形台体

4
我有一个程序,可以跟踪用户的位置并设置视锥(将相机设置在用户的位置),以根据用户的位置改变场景的透视图。直到现在,我将显示屏的四个角放在同一深度,我能够设置非对称的视锥并根据用户的视角改变场景。
当前代码看起来像以下内容:
UserCam::begin(){
    saveGlobalMatrices();  
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
        glFrustum(_topLeftNear.x, _bottomRightNear.x, _bottomRightNear.y, _topLeftNear.y, _camZNear, _camZFar);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();

    gluLookAt(_wcUserHead.x, _wcUserHead.y, _topLeftScreen.z, _wcUserHead.x, _wcUserHead.y, _topLeftScreen.z-1, 0, 1, 0);
}

UserCam::end(){
loadGlobalMatrices();
}

UserCam::setupCam(){
    this->_topLeftScreen = _wcTopLeftScreen - _wcUserHead; //wcTopLeftScreen, wcBottomRightScreen and wcUserHead are in the same frame of reference
    this->_bottomRightScreen = _wcBottomRightScreen - _wcUserHead;

    this->_topLeftNear = (_topLeftScreen/ _topLeftScreen.z) * _camZNear;
    this->_bottomRightNear = (_bottomRightScreen/_bottomRightScreen.z )) * _camZNear;
}

然而,我希望能够在一个被倾斜向用户的显示器上或者不是所有顶点都在相同Z轴的情况下,实现与该问题相同的效果。
以上可以想象成一种倾斜的窗口,其顶点将从用户位置定义截锥体。如何实现这样的截锥体,其中显示屏没有所有顶点位于相同的Z轴上?
编辑:考虑到设置中有三个平面。中间的平面给出了正确的非对称锥体,因为所有顶点都在相同的Z轴上,而左右两个平面则各有两个顶点处于不同的Z轴上。其顶点如下所示:
Plane1: TL : (-426.66, 0, 200), TR: (0, 0, 0), BL : (-426.66, 320.79, 200), BR : (0, 320.79, 0)
Plane2: TL : (0, 0, 0), TR: (426.66, 0, 0), BL : (0, 320.79, 0), BR: (426.66, 320.79, 0)
Plane3: TL:  (426.66, 0, 0), TR: (853.32, 0, 200), BL : (426.66, 320.79, 0), BR : (853.32, 320.79, 200)

我认为你需要第四个维度来理智地完成那件事。 - Bartek Banachewicz
1个回答

7

这个设定的想法是将其转化为所有角落具有相同 z 坐标的情况。通常,这可以通过使用视图矩阵来完成,从而得到:

overall_transform = (projection) * (view * world)

或者按照OpenGL的说法
overall_transform = projection * modelview

如果您不想篡改原始的模型视图矩阵,您应该在其间引入另一个矩阵:

overall_transform = (projection * adaption) * (view * world)

其中,adaption是一个旋转矩阵,它将屏幕的角落映射到具有恒定z坐标的平面上。

为了找到正确的projection参数,您需要使用adaption转换屏幕。

编辑

我们从一个任意场景开始,摄像机的位置、方向和屏幕已知。我们假设每个对象的模型矩阵已经存在:

Initial situation

然后我们需要视图变换V将摄像机与原点对齐。这个矩阵可以很容易地用gluLookAt计算得出。整个矩阵就是T = V * M

After view transformation

在此步骤之前,所有三个屏幕的矩阵都是相同的。因此,这部分应该在模型视图矩阵中。现在我们添加的内容进入投影矩阵,因为每个屏幕都不同。

我们需要应用一个旋转R,使屏幕垂直于z轴。在此步骤中,摄像机的位置不应更改,因为它代表投影中心。整个变换现在是T = R * V * M

为了计算角度,我们可以使用例如atan2

dx = right.x - left.x
dz = right.z - left.z
angle = atan2(dz, dx)

可能需要稍微调整此计算以适应您的实际需求。

旋转适应后

现在是应用实际透视变换的时候了,可以使用glFrustum来完成。

我们需要找到屏幕的本地边缘。您可以使用当前变换(R * V)来转换屏幕坐标。

TL' = R * V * TL
BL' = R * V * BL
BR' = R * V * BR

现在所有三个坐标应该具有相同的z坐标。我们可以按如下方式使用它们:
common_z = TL'.z = BL'.z = BR'.z
glFrustum(TL'.x / common_z * z_near,
          BR'.x / common_z * z_near,
          BL'.y / common_z * z_near,
          TL'.y / common_z * z_near,
          z_near, z_far)

综上所述,T = glFrustum * R * V * M:

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...);
//any further model transforms

glMatrixMode(GL_PROJECTION);
glFrustum(...);
glRotate(...);    

那么,这是否意味着我可以首先使用glFrustum创建一个投影矩阵,假设最初所有四个顶点的z值相同,然后将先前的输出乘以适当的调整矩阵(该矩阵将根据我的显示方向等进行平移、旋转变换),以将投影转换为实际世界中的投影。如果我上面说的是正确的,那么在计算投影矩阵时,应该保持哪个相同的Z值? - user1240679
使用MV矩阵进行乘法可以将用户垂直于平面,但是尝试使用投影矩阵却得到了奇怪的输出。在上面的帖子中添加了关于平面顶点的信息。具有不同Z顶点的平面位于左侧和右侧。是的,这些平面是矩形的,并且对于当前目的,旋转只发生在Y轴周围。 - user1240679
这是一个非常出色的解释,并且我理解了使用glRotate在投影矩阵中旋转投影平面的部分。如果我用这样的旋转来旋转摄像机,那么在gluLookAt之后,我就需要用glRotate进行后乘法运算。然而,在你上面的代码中,glRotate仍然留在glFrustum调用之后,这实际上应该是预先乘以的。我按照这里 http://bit.ly/12EZvKR 中描述的进行了预先乘法的部分,但是当我将glRotate与视图矩阵一起使用时得到的结果与我得到的结果并不相同。 - user1240679
另外,我想了解为什么投影平面不需要旋转中心点。为了使相机垂直于屏幕旋转,我在我的ModelView矩阵中所做的是将旋转的中心点设置到我想要从特定侧面旋转的位置,使用glTranslate(pivtoSide); glRotate(angle, 0,1,0); glTranslate(-pivotSide);。与此相反,为什么我们不需要在某个中心点旋转投影平面呢? - user1240679
@user1240679,你不需要一个中心点,因为你旋转的相机已经在原点了。投影矩阵的顺序倒置没有任何理由。在这种情况下,旋转已经投影的场景毫无意义。 - Nico Schertler
但是在那种情况下,当我在我的ModelView矩阵中执行操作并得到正确结果时,我不应该将其转换为旋转轴,旋转,然后再转换回来。这是我正在测试代码的应用程序中的一个快速而简单的代码 - http://pastebin.com/2HgADJUR。请注意,我正在进行的旋转具有从我尝试旋转的侧面的枢轴。如果我从中心删除平移,则确实会从中心旋转,但是您会注意到链接中的代码中还有其他平面需要拼接的图像。 - user1240679

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