Почему разные результаты при изменении порядка ввода в GL_LINES?

8

Код:

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

Описание проблемы:

  • Над кодом будет помещен весь пиксель нижней строки клиентской области окна в белый цвет.
  • Если я поменяю порядок команд с на 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);, то только левый нижний пиксель не будет рисоваться.

Мои понимания:

  • Две вершины будут окончательно преобразованы в координаты центра 2D-пикселя, которые равны (0,0, 0,499) и (499,001, 0,499).
  • Алгоритм рисования линий принимает только целые точки центра пикселя в качестве входных данных.
  • Таким образом, две вершины будут использовать int (x + 0.5) и будут (0, 0) и (499, 0). Это соответствует первому результату, но противоречит результату при изменении порядка ввода. Почему?
zombielei
источник

Ответы:

10

Разница в покрытии пикселей в зависимости от порядка вершин связана с правилами растеризации . Это правила, которые оборудование GPU использует для точного определения того, какие пиксели покрыты примитивом.

Правила растеризации немного тонки. Одна из их целей состоит в том, чтобы при рисовании нескольких примитивов, которые соединены водонепроницаемо, растеризация никогда не приводила к возникновению трещин между ними, а также к тому, чтобы пиксели не перекрывались дважды (это важно для смешивания). Чтобы достичь этого, в правилах есть некоторые очень специфические преимущества. Буквально ... это случаи, связанные с примитивными краями и углами. :)

В случае линий это работает следующим образом. Вокруг каждого пиксельного центра есть ромбовидная область, как показано в этом фрагменте диаграммы из статьи MSDN, указанной выше.

алмазное правило для растеризации линий

Правило состоит в том, что пиксель покрывается, если линия выходит из ромба, при трассировке от начала до конца линии. Обратите внимание, что пиксель не покрывается, если линия входит, но не выходит из ромба. Это гарантирует, что если у вас есть два линейных сегмента, соединенных друг с другом, что пиксель в их общей конечной точке принадлежит только одному из сегментов и, таким образом, не растеризуется дважды.

Вы также можете увидеть на диаграмме выше, как изменение порядка вершин может повлиять на то, какие пиксели покрыты (чего не происходит с треугольниками, кстати). Диаграмма показывает два одинаковых сегмента линии, за исключением смены порядка конечных точек, и вы можете видеть, что имеет значение, какой пиксель покрывается.

Ваш линейный сегмент чем-то аналогичен этому. Левый конец, в точке (0,5, 0,999), находится внутри ромба (0, 0) пикселя, поэтому он покрывается, когда это первая вершина (линия начинается внутри ромба, а затем выходит из него), а не когда это вторая вершина (линия входит в ромб и заканчивается внутри него, поэтому он никогда не выходит). Фактически, перед растеризацией вершины привязываются к фиксированной точке с помощью 8 битов подпикселей, поэтому эта округляется до (0,5, 1,0), что находится точно в верхнем углу ромба. В зависимости от правил растеризации, это может или не может рассматриваться внутри алмаза; кажется, что на вашем графическом процессоре он рассматривается внутри, но это может варьироваться в зависимости от реализации, поскольку спецификация GL не полностью определяет правила.

Натан Рид
источник