16

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

我相信这可能是显卡中的一个错误,但如果是这样,那么任何专业程序员都应该有他们的防弹解决方案,对吧?

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

这是错误的图像:

在此处输入图像描述

你如何克服这个问题?如果可能,最好使用非着色器解决方案。当我自己绘制 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了,但它并没有修复错误。

编辑:一种解决方案,到目前为止有效: Lie Ryan 的Opengl 像素完美 2D 绘图

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

如果您打开抗锯齿,这一点尤其明显,如果您有抗锯齿并且您尝试从 50,0 到 50,100 绘制,您可能会看到一条模糊的 2px 宽线,因为该线位于两个像素之间。

4

4 回答 4

10

This is not a bug, this is exactly following the specification. The last pixel of a line is not drawn to prevent overdraw with following line segments, which would cause problems with blending. Solution: Send the last vertex twice.

Code Update

// 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: You should learn how to use vertex arrays. Immediate mode (glBegin, glEnd, glVertex calls) have been removed from OpenGL-3.x core and onward.

于 2012-04-25T22:10:31.367 回答
10

尽管您发现将您的分数移动 0.5 会使问题消失,但这并不是您认为的原因。

答案确实在于钻石退出规则,这也是正确接受Opengl 像素完美 2D 绘图答案的核心。

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

碎片菱形之外的可能顶点

你没有说你画四边形的方式,但这没关系。为了争论,我假设你是顺时针绘制的。问题是显示的四个片段的左上角是否将通过光栅化您的第一行或最后一行(不能同时是两者)来产生。

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

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

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

  4. 如果您从红色顶点开始,那么第一行会退出片段而不通过菱形,因此永远不会退出菱形。最后一行也不会穿过菱形,因此不会在它上升回到红色顶点时退出它。因此,任何一条线的光栅化都不会产生片段。

请注意,菱形内部的任何顶点都会自动导致生成片段,因为第一行必须退出菱形(当然,前提是您的四边形实际上足够大以离开菱形)。

于 2012-04-26T21:44:01.803 回答
4

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

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

对于某些(未更改的)点集,您可以通过稍微修改视图转换来实现。1/2 像素偏移就是一个例子。例如,如果世界空间是设备空间的整数比例变换,然后是整数平移,则它可以工作,其中世界坐标也是整数。但是,在许多其他条件下,+1/2 不起作用。

** 编辑 **

注意,正如我所说的,可以在视图变换中构建统一移位(1/2 或任何其他)。没有理由摆弄顶点坐标。只需添加翻译,例如glTranslatef(0.5f, 0.5f, 0.0f);

于 2012-08-25T00:47:01.620 回答
-1

尝试将 0.5 更改为到处使用的奇数幻数 0.375。曾经是opengl,和X11等。

由于提到的钻石规则以及图形卡如何绘制以避免不必要的像素过度绘制。

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

看看这个

想加点东西;所以做你想做的事,在代码中实现钻石规则将只是一条线;像这样将 0.5 更改为 0.375;它应该正确渲染。

glTranslatef(0.375, 0.375, 0.0);
于 2013-03-14T10:25:40.553 回答