Pourquoi un résultat différent lorsque vous modifiez l'ordre de saisie dans GL_LINES?

8

Code:

#include <math.h>
#include <GL/glut.h>
#pragma comment(lib, "opengl32")
#include <gl/gl.h>
#include <gl/glu.h>

//Initialize OpenGL 
void init(void) {
    glClearColor(0, 0, 0, 0);

    glViewport(0, 0, 500, 500);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    glOrtho(0, 500, 0, 500, 1, -1);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
} 

void drawLines(void) {
    glClear(GL_COLOR_BUFFER_BIT);  
    glColor3f(1.0,1.0,1.0); 

    glBegin(GL_LINES);

    glVertex3d(0.5,         0.999,  0.0f);
    glVertex3d(499.501,     0.999,  0.0f);

    glEnd();

    glFlush();
} 


int _tmain(int argc, _TCHAR* argv[])
{
    glutInit(&argc, argv);  
    glutInitWindowPosition(10,10); 
    glutInitWindowSize(500,500); 
    glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); 

    glutCreateWindow("Example"); 
    init(); 
    glutDisplayFunc(drawLines); 
    glutMainLoop();

    return 0;
}

Description du problème:

  • Le code ci-dessus mettra en blanc l'ensemble des pixels de la ligne inférieure de la zone cliente de la fenêtre.
  • Si j'échange l'ordre des commandes, de glVertex3d(0.5, 0.999, 0.0f);glVertex3d(499.501, 0.999, 0.0f);à glVertex3d(499.501, 0.999, 0.0f);glVertex3d(0.5, 0.999, 0.0f);, alors seul le pixel inférieur gauche ne dessinera pas.

Mes compréhensions:

  • Les deux sommets seront finalement transformés en coordonnées de centre de pixels 2D qui sont (0,0, 0,499) et (499,001, 0,499).
  • L'algorithme de dessin au trait accepte uniquement les points entiers du centre des pixels en entrée.
  • Ainsi, les deux sommets utiliseront int (x + 0,5) et seront (0, 0) et (499, 0). Ceci est conforme au premier résultat mais en contradiction avec le résultat lorsque l'ordre d'entrée a changé. Pourquoi?
zombielei
la source

Réponses:

10

La différence dans laquelle les pixels sont couverts en fonction de l'ordre des sommets est liée aux règles de pixellisation . Ce sont les règles que le matériel GPU utilise pour déterminer exactement quels pixels sont couverts par une primitive.

Les règles de pixellisation sont un peu subtiles. L'un de leurs objectifs est de garantir que chaque fois que vous dessinez plusieurs primitives connectées de manière étanche à l'eau, la pixellisation ne produit jamais de fissures entre elles, et aucun pixel n'est couvert deux fois (ce qui est important pour le mélange). Pour y parvenir, les règles ont des cas de bord et d'angle très spécifiques. Littéralement ... ce sont des cas liés aux bords et coins primitifs. :)

Dans le cas des lignes, cela fonctionne comme suit. Il y a une région en forme de losange autour de chaque centre de pixels, comme le montre cet extrait du diagramme de l'article MSDN lié ci-dessus.

règle de diamant pour la pixellisation de ligne

La règle est qu'un pixel est couvert si la ligne quitte le losange, lors du traçage du début à la fin de la ligne. Notez qu'un pixel n'est pas couvert si la ligne entre, mais ne sort pas, le diamant. Cela garantit que si vous avez deux segments de ligne connectés bout à bout, que le pixel de leur point de terminaison partagé n'appartient qu'à l'un des segments et ne soit donc pas tramé deux fois.

Vous pouvez également voir dans le diagramme ci-dessus comment le changement de l'ordre des sommets peut affecter les pixels couverts (ce qui ne se produit pas avec les triangles, BTW). Le diagramme montre deux segments de ligne qui sont identiques, sauf pour permuter l'ordre des points de terminaison, et vous pouvez voir que cela fait une différence quel pixel est couvert.

Votre segment de ligne est quelque peu analogue à cela. L'extrémité gauche, à (0,5, 0,999) est à l'intérieur du diamant du pixel (0, 0), donc il est couvert quand c'est le premier sommet (la ligne commence à l'intérieur du diamant, puis le quitte) et pas quand c'est le deuxième sommet (la ligne entre dans le diamant et se termine à l'intérieur, donc elle ne sort jamais). En fait, les sommets sont accrochés en virgule fixe avec 8 bits de sous-pixel avant la pixellisation, donc celui-ci finit par être arrondi à (0,5, 1,0), ce qui est exactement dans le coin supérieur du losange. Selon les règles de pixellisation, cela peut ou non être pris en compte à l'intérieur du diamant; il semble que sur votre GPU, il soit considéré à l'intérieur, mais cela peut varier entre les implémentations, car la spécification GL ne définit pas complètement les règles.

Nathan Reed
la source