OpenGL拉伸形状-纵横比

10

我的OpenGL视图大小为(假设是...)800(宽)x 600(高)。 然后我有一个坐标为:

0.0 , 0.0
0.1 , 0.0
0.1 , 0.1
0.0 , 0.1

你需要理解的是,这个图形应该是一个正方形(根据类比推断),但在我的OpenGL视窗中,它被拉伸了。我明白这是为什么,因为我使用的是默认矩阵-1,1。而由于我的分辨率为800x600不具有1的纵横比,所以我的形状被拉伸了。

现在我更想知道如何解决这个问题。我已经阅读了一些函数,比如glFrustum、glOrtho、glViewPort,它们发生在投影矩阵上,并可以解决这样的问题。但问题是,我不确定如何使用它们。我基本上想要的是,在绘制正方形时仍保留上述坐标,并且在我的视图中实际呈现为一个正方形。

如何正确地解决这个问题?


可能是保持窗口调整大小时2D对象的纵横比的重复问题。 - genpfault
1个回答

24
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION);
float aspect = (float)width / (float)height;
glOrtho(-aspect, aspect, -1, 1, -1, 1);

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();

更新:解释发生的事情

OpenGL是一个状态机,在OpenGL-2.1及以下版本中维护一组变换矩阵。一个顶点↑v首先与modelview矩阵相乘,得到一个眼坐标顶点↑v',用于光照计算。然后↑v'乘以projection矩阵,达到所谓的剪裁空间↑v'',在这里(名称暗示)执行剪裁操作(或者至少定义了它们的结果)。从剪裁空间到达所谓的“归一化设备坐标”(NDC),通过计算↑v#{x,y,z,w} = ↑v{x,y,z,w}/↑v_w。

↑v#被定义为 [-1 … 1] 范围内,并映射到填充所选的视口矩形。视口不影响变换矩阵!它只影响从NDC到窗口坐标的转换。

在上面的代码中,我将modelview矩阵设置为身份矩阵,即顶点按原样进入投影。投影本身是正交投影,将x值范围[-aspect...aspect]映射到[-1 … 1],将y值范围[-1…1]映射到[-1…1](即y没有变化)。因此,顶点通过窗口宽高比被转换为适合NDC到视口值范围的值。

那么我为什么要告诉你OpenGL是状态机呢?因为这意味着您可以随时切换投影和变换。因此,如果您需要渲染一个迷你地图,请不要试图在3D场景中放置它。只需为其切换视口和投影即可。通常,应该在绘制函数中设置所有渲染状态(也就是所有矩阵)。窗口事件处理程序(除了重新绘制处理程序)不应执行任何OpenGL操作。


2
这确实给我带来了我想要的结果。然而,我担心它会产生其他我目前没有意识到的问题。例如,我的矩阵刚刚改变了哪些坐标。1024/600等于1.7...所以我的矩阵是-1.7->1.7吗(也许我在这里不应该使用矩阵关键字)。如果您能提供更多关于上述情况的信息,我将不胜感激。对于迄今为止提供的有效解决方案,我给出+1。 - apoiat
1
非常好的解释,事实上我想到了一些不错的点子,迫不及待地想尝试。谢谢。 - apoiat
我有点困惑。正交投影不是将[-aspect,aspect] -> [-1,1]映射到另一个范围吗? - Jo So
@JoSo:确实如此,当我写下这句话时,我在某种程度上是在“反向思考”(本质上,您想将NDC坐标范围[-1…1]映射到[-aspect…aspect],但当然从剪辑空间顶点位置的角度来看,情况正好相反)。 - datenwolf
谢谢。这确实帮了很多忙。 - kiranpradeep

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