oldSkool
oldSkool

Reputation: 1232

OpenGL GL_LINES endpoints not joining

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);

Upvotes: 13

Views: 14891

Answers (5)

mpen
mpen

Reputation: 282865

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.

Upvotes: 1

oldSkool
oldSkool

Reputation: 1232

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).

Upvotes: 1

oldSkool
oldSkool

Reputation: 1232

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();

Upvotes: 3

Adam Rosenfield
Adam Rosenfield

Reputation: 400274

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.

Upvotes: 13

Ben Voigt
Ben Voigt

Reputation: 283634

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

Upvotes: 5

Related Questions