Warum ein anderes Ergebnis, wenn die Eingabereihenfolge in GL_LINES geändert wird?

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

Problembeschreibung:

  • Der obige Code setzt die gesamten Pixel der unteren Zeile des Fenster-Client-Bereichs auf Weiß.
  • Wenn ich die Befehlsreihenfolge von glVertex3d(0.5, 0.999, 0.0f);glVertex3d(499.501, 0.999, 0.0f);bis vertausche glVertex3d(499.501, 0.999, 0.0f);glVertex3d(0.5, 0.999, 0.0f);, wird nur das linke untere Pixel nicht gezeichnet.

Mein Verständnis:

  • Die beiden Eckpunkte werden schließlich in 2D-Pixelmittenkoordinaten transformiert, die (0,0, 0,499) und (499,001, 0,499) sind.
  • Der Strichzeichnungsalgorithmus akzeptiert nur ganzzahlige Punkte in der Pixelmitte als Eingabe.
  • Die beiden Eckpunkte verwenden also int (x + 0,5) und sind (0, 0) und (499, 0). Dies entspricht dem ersten Ergebnis, widerspricht jedoch dem Ergebnis, wenn sich die Eingabereihenfolge ändert. Warum?
zombielei
quelle

Antworten:

10

Der Unterschied, in dem Pixel in Abhängigkeit von der Scheitelpunktreihenfolge abgedeckt werden, hängt mit den Rasterungsregeln zusammen . Dies sind die Regeln, nach denen die GPU-Hardware genau bestimmt, welche Pixel von einem Grundelement abgedeckt werden.

Rasterungsregeln sind etwas subtil. Eines ihrer Ziele ist es sicherzustellen, dass beim Zeichnen mehrerer wasserdicht verbundener Grundelemente bei der Rasterung niemals Risse zwischen ihnen entstehen und keine Pixel zweimal abgedeckt werden (dies ist wichtig für das Mischen). Um dies zu erreichen, haben die Regeln einige sehr spezifische Rand- und Eckfälle. Wörtlich ... es handelt sich um Fälle, die mit primitiven Kanten und Ecken zu tun haben. :) :)

Im Fall von Linien funktioniert es wie folgt. Es gibt einen rautenförmigen Bereich um jedes Pixelzentrum, wie in diesem Ausschnitt des Diagramms aus dem oben verlinkten MSDN-Artikel gezeigt.

Diamantregel für die Linienrasterung

Die Regel ist, dass ein Pixel abgedeckt wird, wenn die Linie den Diamanten verlässt , wenn sie vom Anfang bis zum Ende der Linie verläuft. Beachten Sie, dass ein Pixel nicht verdeckt wird, wenn die Linie in den Diamanten eintritt, ihn aber nicht verlässt. Dies stellt sicher, dass, wenn Sie zwei Liniensegmente Ende an Ende verbunden haben, das Pixel an ihrem gemeinsamen Endpunkt nur zu einem der Segmente gehört und somit nicht zweimal gerastert wird.

Sie können im obigen Diagramm auch sehen, wie sich das Ändern der Reihenfolge der Scheitelpunkte auf die abgedeckten Pixel auswirken kann (was bei Dreiecken übrigens nicht der Fall ist). Das Diagramm zeigt zwei Liniensegmente, die bis auf das Vertauschen der Endpunktreihenfolge identisch sind, und Sie können sehen, dass es einen Unterschied macht, welches Pixel abgedeckt wird.

Ihr Liniensegment ist etwas analog dazu. Das linke Ende bei (0,5, 0,999) befindet sich innerhalb des Diamanten des (0, 0) -Pixels, sodass es abgedeckt wird, wenn es der erste Scheitelpunkt ist (die Linie beginnt innerhalb des Diamanten und verlässt ihn dann) und nicht, wenn es der zweite ist Scheitelpunkt (die Linie tritt in den Diamanten ein und endet darin, sodass sie niemals austritt). Tatsächlich werden die Scheitelpunkte vor der Rasterung mit 8 Subpixel-Bits am Festpunkt ausgerichtet, sodass dieser auf (0,5, 1,0) gerundet wird, was genau in der oberen Ecke des Diamanten liegt. Abhängig von den Rasterungsregeln kann dies innerhalb des Diamanten berücksichtigt werden oder nicht. Es scheint, als ob es auf Ihrer GPU im Inneren betrachtet wird, aber dies kann zwischen den Implementierungen variieren, da die GL-Spezifikation die Regeln nicht vollständig festlegt.

Nathan Reed
quelle