Por que resultado diferente ao alterar a ordem de entrada em 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;
}

Descrição do Problema:

  • O código acima colocará os pixels da linha inferior inteira da área do cliente da janela em branco.
  • Se eu trocar a ordem dos comandos, de glVertex3d(0.5, 0.999, 0.0f);glVertex3d(499.501, 0.999, 0.0f);para glVertex3d(499.501, 0.999, 0.0f);glVertex3d(0.5, 0.999, 0.0f);, somente o pixel inferior esquerdo não será desenhado.

Meus entendimentos:

  • Os dois vértices serão finalmente transformados em coordenadas centrais do pixel 2D, que são (0,0, 0,499) e (499,001, 0,499).
  • O algoritmo de desenho de linha aceita apenas pontos inteiros no centro de pixels como entrada.
  • Portanto, os dois vértices usarão int (x + 0,5) e serão (0, 0) e (499, 0). Isso está de acordo com o primeiro resultado, mas contradiz o resultado quando a ordem de entrada foi alterada. Por quê?
zombielei
fonte

Respostas:

10

A diferença na qual os pixels são cobertos, dependendo da ordem do vértice, está relacionada às regras de rasterização . São as regras que o hardware da GPU usa para determinar exatamente quais pixels são cobertos por uma primitiva.

As regras de rasterização são um pouco sutis. Um de seus objetivos é garantir que, sempre que você desenha várias primitivas conectadas à prova d'água, a rasterização nunca produz rachaduras entre elas, nem pixels são cobertos duas vezes (isso é importante para a mistura). Para conseguir isso, as regras têm alguns casos de borda e canto muito específicos. Literalmente ... são casos que têm a ver com arestas e cantos primitivos. :)

No caso de linhas, funciona da seguinte maneira. Há uma região em forma de diamante em torno de cada centro de pixel, conforme mostrado neste trecho do diagrama do artigo do MSDN vinculado acima.

regra de diamante para rasterização de linha

A regra é que um pixel é coberto se a linha sair do diamante, ao rastrear do início ao fim da linha. Observe que um pixel não é coberto se a linha entrar, mas não sair, do diamante. Isso garante que, se você tiver dois segmentos de linha conectados, ponto a ponto, que o pixel no ponto de extremidade compartilhado pertença apenas a um dos segmentos e, portanto, não seja rasterizado duas vezes.

Você também pode ver no diagrama acima como alterar a ordem dos vértices pode afetar quais pixels são cobertos (o que não acontece com triângulos, BTW). O diagrama mostra dois segmentos de linha idênticos, exceto para trocar a ordem do nó de extremidade, e você pode ver que faz diferença qual pixel é coberto.

Seu segmento de linha é um pouco análogo a isso. A extremidade esquerda, em (0,5, 0,999), está dentro do diamante do (0, 0) pixel, então fica coberta quando é o primeiro vértice (a linha começa dentro do diamante e sai dele) e não quando é o segundo vértice (a linha entra no diamante e termina dentro dele, para nunca sair). Na verdade, os vértices são ajustados ao ponto fixo com 8 bits de subpixel antes da rasterização, então este termina arredondado para (0,5, 1,0), que fica exatamente no canto superior do diamante. Dependendo das regras de rasterização, isso pode ou não ser considerado dentro do diamante; parece que na sua GPU é considerado interno, mas isso pode variar entre as implementações, pois a especificação GL não define completamente as regras.

Nathan Reed
fonte