13

alt textI'm having problems with the GL_LINES block... the lines in the sample below do not connect on the ends (although sometimes it randomly decides to connect a corner or two). Instead, the endpoints come within 1 pixel of one another (leaving a corner that is not fully squared; if that makes sense). It is a simple block to draw a solid 1-pixel rectangle.

 glBegin(GL_LINES);

  glColor3b(cr, cg, cb);

  glVertex3i(pRect->left, pRect->top, 0);
  glVertex3i(pRect->right, pRect->top, 0);

  glVertex3i(pRect->right, pRect->top, 0);
  glVertex3i(pRect->right, pRect->bottom, 0);

  glVertex3i(pRect->right, pRect->bottom, 0);
  glVertex3i(pRect->left, pRect->bottom, 0);

  glVertex3i(pRect->left, pRect->bottom, 0);
  glVertex3i(pRect->left, pRect->top, 0);

 glEnd();

The sample below seems to correct the problem, giving me sharp, square corners; but I can't accept it because I don't know why it's acting this way...

 glBegin(GL_LINES);

  glColor3b(cr, cg, cb);

  glVertex3i(pRect->left, pRect->top, 0);
  glVertex3i(pRect->right + 1, pRect->top, 0);

  glVertex3i(pRect->right, pRect->top, 0);
  glVertex3i(pRect->right, pRect->bottom + 1, 0);

  glVertex3i(pRect->right, pRect->bottom, 0);
  glVertex3i(pRect->left - 1, pRect->bottom, 0);

  glVertex3i(pRect->left, pRect->bottom, 0);
  glVertex3i(pRect->left, pRect->top - 1, 0);

 glEnd();

Any OpenGL programmers out there that can help, I would appreciate it :)

The picture is a zoomed-in view of a screenshot. As you can see, the top left corner is not connected. The top right corner is. Not see are the bottom left and right, which are not connected.

The viewport is setup to a 1 to 1 pixel per coordinate.

glDisable(GL_DEPTH_TEST);
glDisable(GL_LIGHTING);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0, __nRendererWidth, __nRendererHeight, 0, -1, 100);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glEnable (GL_TEXTURE_2D);
Cœur
  • 37,241
  • 25
  • 195
  • 267
oldSkool
  • 1,212
  • 5
  • 14
  • 29

5 Answers5

13

From question 14.090 of the OpenGL FAQ:

14.090 How do I obtain exact pixelization of lines?

The OpenGL specification allows for a wide range of line rendering hardware, so exact pixelization may not be possible at all.

You might want to read the OpenGL specification and become familiar yourself with the diamond exit rule. Being familiar with this rule will give you the best chance to obtain exact pixelization. Briefly, the diamond exit rule specifies that a diamond-shaped area exists within each pixel. A pixel is rasterized by a line only if the mathematical definition of that line exits the diamond inscribed within that pixel.

Then from section 3.5 of the OpenGL Core Profile specification:

Because the initial and final conditions of the diamond-exit rule may be difficult to implement, other line segment rasterization algorithms are allowed, subject to the following rules:

  1. The coordinates of a fragment produced by the algorithm may not deviate by more than one unit in either x or y window coordinates from a corresponding fragment produced by the diamond-exit rule.
  2. The total number of fragments produced by the algorithm may differ from that produced by the diamond-exit rule by no more than one.
  3. For an x-major line, no two fragments may be produced that lie in the same window-coordinate column (for a y-major line, no two fragments may appear in the same row).
  4. If two line segments share a common endpoint, and both segments are either x-major (both left-to-right or both right-to-left) or y-major (both bottom-to- top or both top-to-bottom), then rasterizing both segments may not produce duplicate fragments, nor may any fragments be omitted so as to interrupt continuity of the connected segments.

So, the answer is the the specification does not require lines to be joined as you might expect. If your lines were all x-major or all y-major, then rule #4 above would give you the expected output, but in your rectangle, you're alternating between x-major and y-major lines.

If you want to guarantee connected lines with no gaps or overlaps, you should render with GL_LINE_LOOP (or GL_LINE_STRIP for a connected series that does not end where it starts) instead of GL_LINES.

Adam Rosenfield
  • 390,455
  • 97
  • 512
  • 589
  • I get similar results with textured quad polygons, but I can correct it by clamping to the edge... is there any way to specify clamping for lines? – oldSkool Dec 26 '10 at 05:13
  • 2
    `GL_LINE_LOOP` and `GL_LINE_STRIP` are equivalent to a sequence of `GL_LINES`. This does not "guarantee connected lines with no gaps or overlaps". – Yakov Galka Dec 01 '21 at 00:28
5

Since you are drawing a closed polygon, you might want to use GL_LINE_LOOP instead.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
3

In result, it's best to just draw the lines using GL_QUADS. Not as straightforward as using GL_LINES but it works:

glBegin(GL_QUADS);

    glColor3b(bRed, bGreen, bBlue);

    // Draw the top line
    glVertex2i(left, top);
    glVertex2i(right + 1, top);
    glVertex2i(right + 1, top + 1);
    glVertex2i(left, top + 1);

    // Draw the right line
    glVertex2i(right, top);
    glVertex2i(right + 1, top);
    glVertex2i(right + 1, bottom + 1);
    glVertex2i(right, bottom);

    // Draw the bottom line
    glVertex2i(left, bottom);
    glVertex2i(right + 1, bottom);
    glVertex2i(right + 1, bottom + 1);
    glVertex2i(left, bottom + 1);

    // Draw the left line
    glVertex2i(left, top);
    glVertex2i(left + 1, top);
    glVertex2i(left + 1, bottom + 1);
    glVertex2i(left, bottom);

glEnd();
oldSkool
  • 1,212
  • 5
  • 14
  • 29
  • This works but is not really performant or am I missing something? In my test it's roughly 4 times slower than using LINE_STRIP (which produces the gaps however). – Wolf Mar 17 '18 at 12:28
  • @Wolf It can be performant if you use an ELEMENT_ARRAY_BUFFER, but unfortunately I'm seeing the same results with this method too. – mpen Nov 28 '21 at 01:50
1

I couldn't solve it completely, but these links were a bit of help for anyone having the same problem... I realized it is a broad problem that many people experience (and it's a shame such a powerful library makes it so difficult to perform such a primitive operation like drawing a line with pixel unit precision).

Yakov Galka
  • 70,775
  • 16
  • 139
  • 220
oldSkool
  • 1,212
  • 5
  • 14
  • 29
1

Turns out pixel centers are on half-integer boundaries per these docs. You can change this by adding

layout(pixel_center_integer​) in vec4 gl_FragCoord;

to your fragment shader but it's not advised. It didn't work for me anyway (just moved the missing pixel to a different corner).

What worked instead was just to add 0.5 in my vertex shader, after the model transform but before the projection transform. i.e.

#version 460 core
layout (location = 0) in vec2 inPos;

uniform mat4 model;
uniform mat4 projection;

void main() {
    vec4 modelPos =  model * vec4(inPos, 0.0, 1.0);
    // Pixel centers are on half-integer boundaries. Add 0.5 for pixel-perfect corners.
    modelPos.x += 0.5;
    modelPos.y += 0.5;
    gl_Position = projection * modelPos;
}

Credit to Yakov Galka for figuring this out.

mpen
  • 272,448
  • 266
  • 850
  • 1,236
  • Yeah, `pixel_center_integer​` doesn't change rasterization (it's weird that you observe any difference at all). If you just want to shift the coordinates by half (rather than snapping to pixel centers like I described) then instead of changing the fragment shader you can set your `projection` matrix to apply the translation. – Yakov Galka Dec 01 '21 at 15:43
  • @YakovGalka My projection matrix is just using `glm::ortho`.. I guess maybe if I add or subtract 0.5 from the edges it might work? – mpen Dec 02 '21 at 04:01
  • Yes; However take into consideration that you want to do that only for GL_LINES, not for GL_TRIANGLES. – Yakov Galka Dec 02 '21 at 04:23
  • @YakovGalka What would it do to the triangles? Wouldn't it be nicer if the 3 corners are 'centered' on a pixel? – mpen Dec 03 '21 at 03:45
  • 1
    No. For triangles the rasterizer picks the pixels with the center inside the mathematical shape. If your vertex or an edge fall exactly on the center, you are again in the numerically sensitive zone. You can think of lines as-if they are parallelograms that result from extruding your mathematical line by half `glLineWidth`. For odd width it's desirable to have the line center coincide with pixel centers, so the edges fall between the pixels. For even width it's the other way around... – Yakov Galka Dec 03 '21 at 05:24