Opengl像素完美2D绘图

sci*_*pie 30 opengl drawing pixel-perfect

我正在研究2d引擎.它已经工作得很好,但我不断得到像素错误.

例如,我的窗口是960x540像素,我从(0,0)到(959,0)画一条线.我希望扫描线0上的每个像素都会被设置为一种颜色,但是没有:最右边的像素没有被绘制.当我垂直绘制到像素539时,同样的问题.我真的需要绘制到(960,0)或(0,540)来绘制它.

由于我出生在像素时代,我确信这不是正确的结果.当我的屏幕大于320x200像素时,我可以从0到319以及从0到199进行绘制,我的屏幕将会满.现在我最终得到一个没有绘制右/底像素的屏幕.

这可能是由于不同的原因:我期望将opengl线基元从像素绘制到包含像素,最后一个像素实际上是独占的?是吗?我的投影矩阵不正确?我有一个错误的假设,当我有一个960x540的后备缓冲区时,实际上有一个像素更多?别的什么?

有人可以帮帮我吗?我一直在研究这个问题很长一段时间,每当我认为它没问题的时候,我看了一会儿它实际上没有.

这是我的一些代码,我试图尽可能地删除它.当我调用我的线函数时,每个坐标都加上0.375,0.375,以使它在ATI和nvidia适配器上都正确.

int width = resX();
int height = resY();

for (int i = 0; i < height; i += 2)
    rm->line(0, i, width - 1, i, vec4f(1, 0, 0, 1));
for (int i = 1; i < height; i += 2)
    rm->line(0, i, width - 1, i, vec4f(0, 1, 0, 1));

// when I do this, one pixel to the right remains undrawn

void rendermachine::line(int x1, int y1, int x2, int y2, const vec4f &color)
{
    ... some code to decide what std::vector the coordinates should be pushed into
    // m_z is a z-coordinate, I use z-buffering to preserve correct drawing orders
    // vec2f(0, 0) is a texture-coordinate, the line is drawn without texturing
    target->push_back(vertex(vec3f((float)x1 + 0.375f, (float)y1 + 0.375f, m_z), color, vec2f(0, 0)));
    target->push_back(vertex(vec3f((float)x2 + 0.375f, (float)y2 + 0.375f, m_z), color, vec2f(0, 0)));
}

void rendermachine::update(...)
{
    ... render target object is queried for width and height, in my test it is just the back buffer so the window client resolution is returned
    mat4f mP;
    mP.setOrthographic(0, (float)width, (float)height, 0, 0, 8000000);

    ... all vertices are copied to video memory

    ... drawing
    if (there are lines to draw)
        glDrawArrays(GL_LINES, (int)offset, (int)lines.size());

    ...
}

// And the (very simple) shader to draw these lines

// Vertex shader
    #version 120
    attribute vec3 aVertexPosition;
    attribute vec4 aVertexColor;
    uniform mat4 mP;
    varying vec4 vColor;
    void main(void) {
        gl_Position = mP * vec4(aVertexPosition, 1.0);
        vColor = aVertexColor;
    }

// Fragment shader
    #version 120
    #ifdef GL_ES
    precision highp float;
    #endif
    varying vec4 vColor;
    void main(void) {
        gl_FragColor = vColor.rgb;
    }
Run Code Online (Sandbox Code Playgroud)

小智 15

在OpenGL中,使用"Diamond Exit"规则对行进行栅格化.这几乎与说结束坐标是独占的,但不完全相同......

这就是OpenGL规范所说的:http: //www.opengl.org/documentation/specs/version1.1/glspec1.1/node47.html

另请参阅OpenGL FAQ,http://www.opengl.org/archives/resources/faq/technical/rasterization.htm,item "14.090如何获得行的精确像素化?".它说"OpenGL规范允许使用各种线性渲染硬件,因此根本不可能进行精确的像素化."

许多人会争辩说你根本不应该在OpenGL中使用线条.他们的行为是基于古老的SGI硬件的工作原理,而不是基于什么是有意义的.(宽度> 1的线几乎不可能以看起来很好的方式使用!)

  • 对不起:14.120:填充的基元和线基元遵循不同的光栅化规则. (3认同)

Lie*_*yan 6

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

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

  • @SigTerm:这不是真的,你应该在ATI和NVidia硬件上试一试,噩梦就开始了.ATI和NVidia不同.通过使用0.375,两者的舍入是相同的,结果是完美的(虽然理论上可能不正确) (4认同)
  • @scippie:一旦你启用了抗锯齿,0.375会引起很大的问题.SigTerm是对的.您的问题不是来自使用0.5,而是因为您正在使用投影矩阵将其应用于两个维度以及四边形和线条.它应该仅应用于线条,并且仅应用于垂直于线条的轴线. (2认同)