Issue: GL_TRIANGLE_STRIP and GL_TRIANGLE_FAN lines visible on some platforms

[ATTACH=CONFIG]1789[/ATTACH]
I’m hoping someone has seen this before. My search didn’t reveal this in a previous thread. I may just not know the proper term for it.

I wrote a function that allows me to draw wide rings using GL_TRIANGLE_STRIPS, and a circle object using GL_TRIANGLE_FAN primitives. It looks great on every computer I’ve run it on, except for my new desktop with an Nvidia GeForce GTX 1050 Ti video card.

It looked seamless on Jetson TK1s, TX1s and even IBM laptops.

The bulk of the code is similar to the DOUBLE.C sample, just with antialiasing added. Nothing too involved.

Has anyone seen this happen, inconsistently, and discovered a resolution?

Many thanks!

A few questions:
Are you sharing vertex positions and using the same connections for triangles sharing an edge?
And are they being transformed by the same vertex shader with the same uniform values with the same GL state active?
Are you transforming them with the usual MVP-type matrix, or doing something special in computing vertex positions?
Are you rendering with multisampling or single sampling?
Have you declared your gl_Position invariant? (not that you should have to unless multiple shaders are involved)
Are you doing anything unusual in your shaders or GL state settings?

Post some code.

Thank you for the reply.

The following is the code for just one of the objects. The “ball” object has the same problem. It seems any object that has an array of vertices shows the vertices when drawn.

This is the bulk of the code for the big ring object:


void CompileBigRing(GLint nIndex,       /* numeric id */
                       GLuint nColor,      /* rgb color */
                       GLfloat radius,     /* radius of ring */
                       GLint nSegs,        /* number of segments (resolution) */
                       GLint nDraw,        /* number of drawn segments */
                       GLfloat w,          /* ring line width */
                       GLushort nStipple){    /* stipple pattern (0 = none) */
int nCount;
GLint *nCoords = NULL;
GLfloat twicePi = 2.0f * PI;

    glHint (nIndex, GL_DONT_CARE);
    glEnable (GL_LINE_SMOOTH);

    glNewList (nIndex, GL_COMPILE);
    glPushMatrix ();
    glPushAttrib (GL_CURRENT_BIT);

    /* set line width */
    glLineWidth (1);
    
    /* check to see if we're doing line stippling */
    if (nStipple) {
        glEnable (GL_LINE_STIPPLE);
        glLineStipple (1, nStipple);
        }
    
    /* set model color */
    glColor3ub (GetRValue (nColor), GetGValue (nColor), GetBValue (nColor));

    glBegin (/*GL_LINE_STRIP*/ GL_TRIANGLE_STRIP);

    for (nCount = 1; nCount < nDraw; nCount++) { 
        glVertex2f(  /* add width to radius for odd counts (alternating) */
           (radius + (w * (nCount & 1))) * cos (nCount *  twicePi / nSegs), 
           (radius + (w * (nCount & 1))) * sin (nCount * twicePi / nSegs)
           );
        }

    glEnd ();
    
    glPopAttrib ();
    glPopMatrix ();

    glEndList ();
    }

The code setting up the environment is this:


  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
    glutInitWindowSize (nXSize, nYSize); 
    glutInitWindowPosition (0, 0);
    glutCreateWindow (szTitle);

    /* set background color */
    glClearColor (0.0, 0.0, 0.0, 1.0);
    glColor3ub (128, 20, 200);
    glShadeModel (GL_FLAT);

    /* antialiasing routines */
    glEnable (GL_BLEND);
    glDisable (GL_DEPTH_TEST);
    glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
    glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
    glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
    glDisable (GL_LINE_SMOOTH);
    glDisable (GL_POLYGON_SMOOTH);

I got most of it from the DOUBLE.C sample and made some changes to add antialiasing.

Thank you again for checking back with me. I hope something in here is obvious to you.

JP

[QUOTE=jpfulks;1291638]Thank you for the reply.

The following is the code for just one of the objects.[/QUOTE]

You didn’t really answer my questions, though the answers to some of my questions are obvious from your code.

You’re using the ancient immediate mode drawing and a fair amount of unusual state, increasing your chances of tripping over a driver bug.

The first thing I would suggest doing is: comment out all of these state and draw a single opaque fan with a single glDrawElements call where you are sharing vertex definitions across triangles. See if you have a problem then.

Good idea. I put together all the pertinent calls into one source file.

Sadly, I won’t be able to run this code on the machine that showed the issue again, but I’m hoping something in the following code jumps out as a problem.

Thanks again for looking at this for me.


#include "hud.h"

#define SCREENX 1000
#define SCREENY 500

void myReshape (int w, int h) {
  glViewport (0, 0, w, h);
  glMatrixMode (GL_PROJECTION);
  glLoadIdentity ();
  if (w <= h)
    glOrtho (-50.0, 50.0, -50.0*(GLfloat)h/(GLfloat)w,
             50.0*(GLfloat)h/(GLfloat)w, -1.0, 1.0);
  else
    glOrtho (-50.0*(GLfloat)w/(GLfloat)h,
             50.0*(GLfloat)w/(GLfloat)h, -50.0, 50.0, -1.0, 1.0);
  glMatrixMode (GL_MODELVIEW);
  glLoadIdentity ();
}

void keyboard(unsigned char key, int x, int y) {
  if (key == 27)
    exit (0);
}

int HUDInit (char *szTitle, int nXSize, int nYSize, int nRBg, int nGBg, int nBBg) {
  glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
  glutInitWindowSize (nXSize, nYSize);
  glutInitWindowPosition (0, 0);
  glutCreateWindow (szTitle);

  glClearColor (0.0, 0.0, 0.0, 1.0);
  glColor3ub (128, 20, 200);
  glShadeModel (GL_FLAT);

/* antialiasing */
  glEnable (GL_BLEND);
  glDisable (GL_DEPTH_TEST);
  glBlendFunc (GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  glHint (GL_LINE_SMOOTH_HINT, GL_NICEST);
  glHint (GL_POLYGON_SMOOTH_HINT, GL_NICEST);
  glDisable (GL_LINE_SMOOTH);
  glDisable (GL_POLYGON_SMOOTH);

  glutReshapeFunc (myReshape);
  glutIdleFunc (HUDDisplay);
  glutDisplayFunc (HUDDisplay);
  glutKeyboardFunc (keyboard);
}

void HUDDisplay (void) {
  GLfloat twicePi = 2.0f * PI;
  GLfloat radius = 50;
  int i;

  glClear (GL_COLOR_BUFFER_BIT| GL_DEPTH_BUFFER_BIT);

  glPushMatrix ();

  glBegin(GL_TRIANGLE_FAN);
  glColor3ub (150, 50, 192);
  glVertex2f(0, 0);
  for(i = 0; i <= 200;i++) {
    glVertex2f(
      0 + (radius * cos(i * twicePi / 200)),
      0 + (radius * sin(i * twicePi / 200)));
  }
  glEnd();

  glPopMatrix ();

  glFlush ();
  glutSwapBuffers ();
}

int main (int argc, char **argv) {
#ifdef FREEGLUT
  glutInit (&argc, argv);
#endif
/* set up window settings */
  HUDInit ("Test", SCREENX, SCREENY, 0, 0, 0);

#ifdef FREEGLUT
  HUDMainLoop ();
#else
  HUDMainLoop (HUDDisplay);
#endif

  return (0);
}

If you’re using glEnable(GL_POLYGON_SMOOTH) with that glBlendFunc() setting, it will result in edges being visible.

Alpha-based polygon anti-aliasing requires glBlendFunc(GL_SRC_ALPHA_SATURATE, GL_ONE) and the framebuffer must have an alpha channel. And for 3D rendering, you need to order the polygons from front to back.

GL_POLYGON_SMOOTH is enough of a nuisance to use correctly that it’s essentially deprecated now that MSAA exists.

Yay! Thank you!

I was pretty sure someone had seen this behavior before.

I changed my source to use the glBlendFunc arguments you posted, but I get a blank window as a result. So long as I have an idea of where to look for the culprit, I’m way ahead of the game.

I’ll do some more testing with it.

Thanks again!

JP

[QUOTE=jpfulks;1291673]
I changed my source to use the glBlendFunc arguments you posted, but I get a blank window as a result. [/QUOTE]
In this part:

you need to set the alpha to 0.0 when clearing. Otherwise, you’re effectively flagging the entire framebuffer as having been filled, which will mask out any subsequent rendering (due to the use of GL_SRC_ALPHA_SATURATE).

Essentially, the usual (A[sub]src[/sub], 1-A[sub]src[/sub]) factors are only correct if the contributions from distinct triangles are uncorrelated. If you fill a pixel with a specific colour and an alpha of 0.5, then fill it again with the same colour and an alpha of 0.5, the effect is equivalent to filling it once with an alpha of 0.75. It’s like how if you flip two coins, the odds of the first coin being a head OR the second coin being a head is 1-(1-0.5)*(1-0.5)=0.75, not 0.5+0.5=1.0.

For a region tessellated into triangles, any pixel inside the region should be completely filled. If a pixel lies on the boundary between two triangles, any portion not filled by one will be filled by the other; the background colour won’t (or rather shouldn’t) contribute. So the blending needs to assume that the portion already in the framebuffer (the destination) is disjoint from the the portion being rendered (the source), hence the use of GL_ONE for the destination factor (rather than GL_ONE_MINUS_SRC_ALPHA). The use of GL_SRC_ALPHA_SATURATE for the source factor clamps the source alpha to 1-A[sub]dst[/sub] so that the total alpha never exceeds 1.0 (in the case where you never have overlapping polygons, that shouldn’t happen anyhow).

[QUOTE=GClements;1291677]In this part:

you need to set the alpha to 0.0 when clearing. Otherwise, you’re effectively flagging the entire framebuffer as having been filled, which will mask out any subsequent rendering (due to the use of GL_SRC_ALPHA_SATURATE).
[/QUOTE]
You called it. That did it, of course. Your changes have it looking as it should on the PC platform and I’m sure on the others, too.

I haven’t had the time to really invest in learning the finer points of OpenGL, so a couple days ago I snagged one of those online courses on it to give myself a kick start. I’ve been wanting to make use of OpenGL for a while. I actually have some projects that will make good use of it.

Again, thanks for helping me out with this issue.

JP