如何使用OpenGL在2D模式下渲染完美的线框矩形?

时间:2012-04-25 16:57:45

标签: c++ opengl

编辑:只是让你知道:我还没有完全解决这个问题,目前我正在使用0.5px偏移,似乎可以工作,但正如其他人一样说,这不是“适当的”解决方案。所以我正在寻找真正的交易,钻石退出规则解决方案根本不起作用。

我认为它可能是显卡中的一个错误,但如果是这样,那么任何专业程序员都应该为此设置防弹解决方案,对吗?

编辑:我现在买了一张新的nvidia卡(以前有ATI卡),我仍然遇到这个问题。在很多很多游戏中我也看到了同样的错误。所以我想不可能以干净的方式修复?

这是错误的图片:

enter image description here

你如何克服这个问题?如果可能的话,最好是非着色器解决方案。当我自己绘制4条单独的线而不是使用线框模式时,我尝试设置第一条线的偏移量,但是效果不是很好:如果矩形大小改变了,它有时看起来很完美,但有时甚至比修复之前更糟糕

这是我渲染四边形的方式:

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_QUADS);
    glVertex2f(...);
    glVertex2f(...);
    glVertex2f(...);
    glVertex2f(...);
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

是的,我知道我可以使用顶点数组或VBO,但这不是重点。

我也试过GL_LINE_LOOP,但它没有修复错误。

编辑:到目前为止,有一个解决方案是:Opengl pixel perfect 2D drawing由Lie Ryan提供:

  

请注意,OpenGL坐标空间没有整数概念,   一切都是浮动的,OpenGL像素的“中心”实际上是   0.5,0.5而不是它的左上角。因此,如果你想要一个   1px宽线从0,0到10,10包含,你真的不得不画一个   从0.5,0.5到10.5,10.5。

     

如果您打开抗锯齿,这将特别明显   有抗锯齿,你试图从50,0到50,100画你可能会看到   模糊的2px宽线,因为线条落在两个像素之间。

4 个答案:

答案 0 :(得分:10)

这不是错误,这完全符合规范。不绘制线的最后一个像素以防止使用以下线段过度绘制,这会导致混合问题。 解决方案:发送两次最后一个顶点。

代码更新

// don't use glPolygonMode, it doesn't
// do what you think it does
glBegin(GL_LINE_STRIP);
    glVertex2f(a);
    glVertex2f(b);
    glVertex2f(c);
    glVertex2f(d);
    glVertex2f(a);
    glVertex2f(a); // resend last vertex another time, to close the loop
glEnd();

BTW:你应该学习如何使用顶点数组。立即模式(glBegin,glEnd,glVertex调用)已从OpenGL-3.x内核中移除。

答案 1 :(得分:9)

虽然您已经发现将点数移动0.5会使问题消失,但这并不是您认为的原因。

答案确实存在于钻石退出规则中,这也是Opengl pixel perfect 2D drawing正确接受的答案的核心。

下图显示了四个片段/像素,每个片段/像素都刻有一颗钻石。四个彩色斑点代表四/线环的可能起点,即第一个顶点的窗口坐标。

Possible vertex points outside a fragment's diamond

你没有说你正在绘制四边形的方式,但这并不重要。为了论证的缘故,我会假设你正在顺时针绘制它。问题是所显示的四个片段的左上角是否会通过光栅化你的第一行或最后一行产生(它不能同时产生)。

  1. 如果从黄色顶点开始,则第一条线穿过菱形并在水平向右移动时退出。因此,片段将作为第一行光栅化的结果而产生。

  2. 如果从绿色顶点开始,那么第一行退出片段而不通过钻石,因此永远不会退出钻石。然而,最后一行将垂直穿过它,并在它上升回绿色顶点时退出。因此,片段将作为最后一行光栅化的结果而产生。

  3. 如果从蓝色顶点开始,则第一条线穿过菱形并在水平向右移动时退出。因此,片段将作为第一行光栅化的结果而产生。

  4. 如果你从红色顶点开始,那么第一行退出片段而不通过钻石,因此永远不会退出钻石。最后一条线也不会穿过钻石,因此当它上升回红色顶点时不会退出钻石。因此,片段将由于任一行的光栅化而产生。

  5. 请注意,钻石内的任何顶点都会自动生成碎片,因为第一条线必须退出钻石(假设您的四边形实际上足够大,可以留下钻石)。

答案 2 :(得分:3)

@Troubadour完美地描述了这个问题。这不是驱动程序错误。 GL的行为完全符合规定。它专为设备空间中的世界空间物体的亚像素精确表示而设计。这就是它正在做的事情。解决方案是1)抗锯齿,因此设备空间提供更高的保真度,2)安排世界坐标系,其中所有变换的顶点都落在设备像素的中间。这是您正在寻找的“通用”解决方案。

在所有情况下,您都可以通过移动输入点来实现2)。只需将每个点移动到足以将其变换到设备像素的中间位置。

对于某些(未改变的)点集,您可以通过轻微修改视图转换来完成。 1/2像素移位就是一个例子。它有效,例如如果世界空间是设备空间的整数变换后跟整数平移,其中世界坐标也是整数。但是,在许多其他条件下,+1 / 2将无效。

**编辑**

NB,因为我说可以在视图转换中构建均匀移位(1/2或任何其他)。没有理由摆弄顶点坐标。只需添加翻译,例如glTranslatef(0.5f, 0.5f, 0.0f);

答案 3 :(得分:0)

尝试将0.5更改为0.375处使用的奇数幻数。 用过opengl和X11等。

因为所提到的钻石规则以及图形卡如何绘制以避免不必要的过度抽取像素。

提供一些链接,但有很多链接,如果您需要更多信息,只需搜索关键字opengl 0.375菱形规则。这是关于如何在opengl中以算法方式处理轮廓和填充。它需要像素完美渲染纹理,例如2d精灵。

看看this

想要添加一些东西;所以做你想要的,实现代码中实现的菱形规则只是一个班轮;像这样将0.5变成0.375; 它应该正确呈现。

glTranslatef(0.375, 0.375, 0.0);