Opengl像素完美2D绘图

Posted

技术标签:

【中文标题】Opengl像素完美2D绘图【英文标题】:Opengl pixel perfect 2D drawing 【发布时间】:2012-04-06 08:25:15 【问题描述】:

我正在开发 2d 引擎。它已经很好用了,但我不断收到像素错误。

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

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

这可能是由于不同的原因: 我希望opengl线原语是从一个像素到一个像素(包括)绘制的,最后一个像素实际上是独占的?是这样吗? 我的投影矩阵不正确? 我有一个错误的假设,即当我有一个 960x540 的后备缓冲区时,实际上还有一个像素? 还有什么?

有人可以帮帮我吗?这个问题我研究了很久,每次觉得没问题的时候发现其实不行。

这是我的一些代码,我试图尽可能地精简它。当我调用我的 line-function 时,每个坐标都添加了 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;
    

【问题讨论】:

我开始认为这是我所说的“点独占”的东西。如果您使用 QT 或 C++ Builder/Delphi 甚至 windows API 绘制线条,则绘制的像素不包括端点。所以在opengl中可能会这样做,并且可能在绘制三角形时也是如此?当您使用 QT、builder、...绘制一个矩形时,您还会将右下角排除在外... 以下测试与我上面所说的一致:当我使用 GL_POINT 填充窗口所有角落的像素时,这些坐标与我预期的一样:(0,0) ,(宽度-1,高度-1)。这表明我的绘制方式是正确的,这条线(可能还有三角形)将不包括终点。有人可以同意吗? 相关***.com/questions/5467218/opengl-2d-hud-over-3d 相关但更通用:***.com/questions/7922526/… 【参考方案1】:

在 OpenGL 中,使用“钻石出口”规则对线条进行光栅化。这几乎就等于说结束坐标是独占的,但不完全一样……

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

还可以查看 OpenGL 常见问题解答,http://www.opengl.org/archives/resources/faq/technical/rasterization.htm,项目“14.090 如何获得线条的精确像素化?”。它说“OpenGL 规范允许使用各种线渲染硬件,因此可能根本无法实现精确的像素化。”

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

【讨论】:

谢谢,这个答案很有帮助!这些线用于测试目的,而不是用于最终产品的冷却。所以我现在对 GL_LINES 很满意。我可以假设三角形有相同的结果吗?我的意思是,如果我通过绘制两个三角形来绘制一个实心矩形,我可以期待相同的结果吗? 抱歉:14.120:填充图元和线图元遵循不同的光栅化规则。 >"宽度 >1 的线条几乎不可能以看起来不错的方式使用!"可以将它们绘制为细长的矩形,然后在片段着色器中使用一些魔法,使它们看起来与几乎任何视觉属性一致。但这可能很昂贵。【参考方案2】:

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

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

【讨论】:

我完全理解你在说什么,但这不是问题。通过使用正确的投影矩阵并向每个像素添加 0.375,ATI 和 nvidia 都会对数字进行四舍五入,以便坐标是精确的像素。如果它们不是像素完美的,例如 mac os 会如何绘制像素完美的窗口? @scippie:您实际上应该添加 0.5。 0.5 是像素的中间值。 0.375 不是。 @SigTerm:这不是真的,你应该在 ATI 和 NVidia 硬件上尝试一下,然后噩梦就开始了。 ATI 和 NVidia 的舍入方式不同。通过使用 0.375,两者的舍入相同,结果也很完美(尽管理论上可能不正确) @scippie:一旦启用抗锯齿,0.375 就会导致大问题。 SigTerm 是对的。您的问题不是来自使用 0.5,而是因为您使用的是投影矩阵,该矩阵将其应用于两个维度以及四边形和线条。它应该只应用于线条,并且只能在垂直于线条的轴上。

以上是关于Opengl像素完美2D绘图的主要内容,如果未能解决你的问题,请参考以下文章

使用 OpenGL 进行像素完美的 2D 渲染

在 OpenGL 中绘制大量像素

OpenGL工作流程

OpenGL ES 2.0 (iOS) 中的 2D 绘图

使用 OpenGL v1.4 进行硬件加速的 Qt 2D 绘图?

OpenGL-渲染流程