¿Por qué resultados diferentes cuando se cambia el orden de entrada en GL_LINES?

8

Código:

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

Descripción del problema:

  • El código anterior pondrá en blanco todos los píxeles de la fila inferior del área del cliente de la ventana.
  • Si cambio el orden del comando, de glVertex3d(0.5, 0.999, 0.0f);glVertex3d(499.501, 0.999, 0.0f);a glVertex3d(499.501, 0.999, 0.0f);glVertex3d(0.5, 0.999, 0.0f);, solo el píxel inferior izquierdo no se dibujará.

Mis entendimientos:

  • Los dos vértices se transformarán finalmente en coordenadas del centro de píxeles 2D que son (0.0, 0.499) y (499.001, 0.499).
  • El algoritmo de dibujo lineal solo acepta puntos enteros de centro de píxel como entrada.
  • Entonces los dos vértices usarán int (x + 0.5) y serán (0, 0) y (499, 0). Esto se ajusta al primer resultado pero se contradice con el resultado cuando cambia el orden de entrada. ¿Por qué?
zombielei
fuente

Respuestas:

10

La diferencia en la que se cubren los píxeles según el orden de los vértices está relacionada con las reglas de rasterización . Son las reglas que usa el hardware de la GPU para determinar exactamente qué píxeles están cubiertos por una primitiva.

Las reglas de rasterización son un poco sutiles. Uno de sus objetivos es garantizar que cada vez que dibuje múltiples primitivas que estén conectadas herméticamente, la rasterización nunca produzca grietas entre ellas, ni se cubran los píxeles dos veces (eso es importante para la fusión). Para lograr eso, las reglas tienen algunos casos de borde y esquina muy específicos. Literalmente ... son casos que tienen que ver con bordes y esquinas primitivas. :)

En el caso de las líneas, funciona de la siguiente manera. Hay una región en forma de diamante alrededor de cada centro de píxeles, como se muestra en este fragmento del diagrama del artículo de MSDN vinculado anteriormente.

regla de diamante para rasterización de línea

La regla es que se cubre un píxel si la línea sale del diamante, cuando se traza desde el principio hasta el final de la línea. Tenga en cuenta que un píxel no queda cubierto si la línea entra, pero no sale, del diamante. Esto garantiza que si tiene dos segmentos de línea conectados de extremo a extremo, el píxel en su punto final compartido solo pertenece a uno de los segmentos y, por lo tanto, no se rasteriza dos veces.

También puede ver en el diagrama anterior cómo cambiar el orden de los vértices puede afectar qué píxeles están cubiertos (lo que no sucede con los triángulos, por cierto). El diagrama muestra dos segmentos de línea que son idénticos, excepto para intercambiar el orden del punto final, y puede ver que hace una diferencia qué píxel se cubre.

Su segmento de línea es algo análogo a esto. El extremo izquierdo, en (0.5, 0.999) está dentro del diamante del (0, 0) píxel, por lo que se cubre cuando es el primer vértice (la línea comienza dentro del diamante, luego sale) y no cuando es el segundo vértice (la línea entra en el diamante y termina dentro de él, por lo que nunca sale). En realidad, los vértices se ajustan a un punto fijo con 8 bits de subpíxeles antes de la rasterización, por lo que este termina redondeado a (0.5, 1.0), que está exactamente en la esquina superior del diamante. Dependiendo de las reglas de rasterización, esto podría o no considerarse dentro del diamante; parece que en su GPU se considera dentro, pero esto puede variar entre implementaciones, ya que la especificación GL no establece las reglas por completo.

Nathan Reed
fuente