2012-04-06 86 views
27

我正在使用2D引擎。它已经相当不错了,但我一直在获取像素错误。Opengl像素完美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; 
    } 
+0

我开始认为这是我所说的“点独家”的事情。如果您使用QT或C++ Builder/Delphi甚至Windows API绘制线条,则绘制像素不包括端点。所以有可能在opengl中完成同样的工作,也可能在绘制三角形的时候呢?当你用QT,builder,...绘制一个矩形时,你也会排除右下角的...... – scippie 2012-04-06 08:48:37

+0

以下测试显示了与我上面所说的一致:当我使用GL_POINT来填充窗口所有角落处的像素时,这些坐标与我预期的一样:(0,0),(width-1,height -1)。这表明我的绘图方式是正确的,并且该线(也可能是三角形)将不包含终点。有人可以同意吗? – scippie 2012-04-06 09:11:02

+0

相关http://stackoverflow.com/questions/5467218/opengl-2d-hud-over-3d – 2016-04-13 15:03:33

回答

15

在OpenGL中,使用“Diamond Exit”规则对行进行了栅格化。这是几乎相同的话称,其最终坐标是排他性的,但不太...

这是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的线几乎不可能以一种看起来不错的方式使用!)

+0

谢谢,这个答案非常有帮助!这些生产线用于测试目的,而不是用于最终产品的冷却。所以我现在对GL_LINES非常满意。我能否假设三角形具有相同的结果?我的意思是,如果我通过绘制两个三角形绘制一个填充的矩形,我可以期待相同的结果吗? – scippie 2012-04-06 10:09:47

+3

对不起:14.120:填充原语和线原语遵循不同的光栅化规则。 – scippie 2012-04-06 19:44:29

+0

>“宽度大于1的线几乎不可能以一种看起来不错的方式使用!” 可以将它们绘制成薄矩形,然后在片段着色器中做一些魔术,使它们看起来像几乎所有视觉属性一样。但是这可能很昂贵。 – 2013-05-26 08:49:27

6

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

如果打开反锯齿功能,如果您有抗锯齿功能,并且您尝试从50,0到50,100绘制,则可能会看到模糊的2像素宽线,因为线落在两个之间像素。

+1

我完全理解你在说什么,但这不是问题。通过使用正确的投影矩阵并将0.375添加到每个像素,ATI和nvidia都将对数字进行四舍五入,以使坐标成为精确的像素。如果没有像素完美的话,mac os会如何绘制像素完美的窗口? – scippie 2012-04-06 08:46:03

+0

@scippie:你实际上应该加0.5。 0.5是像素的中间。 0.375不是。 – SigTerm 2012-04-06 10:04:36

+4

@SigTerm:这不是真的,你应该在ATI和NVidia硬件上试用它,并且恶梦开始。 ATI和NVidia有所不同。通过使用0.375,两者的舍入是相同的,并且结果是完美的(尽管从理论上讲它可能不是正确的) – scippie 2012-04-06 10:12:19