Как сделать идеальный прямоугольник с каркасом в 2D режиме с помощью OpenGL?

Изменить:, так что вы знаете: я еще не решил эту проблему, но в настоящее время я использую смещение 0.5px, похоже, это работает, но, как говорили другие, это не "правильное" решение. Таким образом, я ищу реальную сделку, решение для решения проблемы с алмазом не работает вообще.

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

Изменить: Теперь я купил новую карту nvidia (раньше была карта ATI), и я все еще испытываю эту проблему. Я также вижу ту же ошибку во многих играх. Так что я думаю, что это невозможно исправить чистым способом?

Вот изображение ошибки:

enter image description here

Как вы преодолеваете эту проблему? Предпочтительно, если это возможно, не шейдерное решение. Я попытался установить смещение для первой строки, когда я нарисовал 4 отдельных линии самостоятельно, вместо того, чтобы использовать режим каркаса, но это не получилось очень хорошо: если размер прямоугольника изменился, он иногда выглядел идеально прямоугольником, но иногда даже хуже, чем до моего исправления.

Вот как я рисую квад:

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glBegin(GL_QUADS);
    glVertex2f(...);
    glVertex2f(...);
    glVertex2f(...);
    glVertex2f(...);
glEnd();
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

Да, я знаю, что могу использовать вершинные массивы или VBO, но здесь дело не в этом.

Я также пробовал GL_LINE_LOOP, но он не исправил ошибку.

Изменить: Одно из решений, которое работает до сих пор: Идеальный 2D-чертеж Opengl от Lie Ryan:

Обратите внимание, что в пространстве координат OpenGL нет понятия целых чисел, все это поплавок, а "центр" пикселя OpenGL действительно находится на 0,5,0,5 вместо его верхнего левого угла. Поэтому, если вы хотите 1px широкая линия от 0,0 до 10,10 включительно, вам действительно нужно было нарисовать от 0,5,0,5 до 10,5,10,5.

Это будет особенно заметно, если вы включите сглаживание, если вы имеют сглаживание, и вы пытаетесь привлечь от 50,0 до 50 100, вы можете увидеть размытая 2px широкая линия, потому что линия упала между двумя пикселями.

Ответы

Ответ 1

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

Обновление кода

// don't use glPolygonMode, it doesn't
// do what you think it does
glBegin(GL_LINE_STRIP);
    glVertex2f(a);
    glVertex2f(b);
    glVertex2f(c);
    glVertex2f(d);
    glVertex2f(a);
    glVertex2f(a); // resend last vertex another time, to close the loop
glEnd();

Кстати: Вы должны научиться использовать массивы вершин. Непосредственный режим (glBegin, glEnd, вызовы glVertex) были удалены из ядра OpenGL-3.x и далее.

Ответ 2

Несмотря на то, что вы обнаружили, что переключение ваших очков на 0,5 делает проблему уходите не по той причине, о которой вы думаете.

Ответ действительно лежит в правиле выхода алмаза, который также лежит в основе правильно принятого ответа на идеальный 2D-чертеж Opengl.

На приведенной ниже диаграмме показаны четыре фрагмента/пикселя с алмазом, вписанным в каждый. Четыре цветных пятна представляют собой возможные начальные точки для вашего квадранта/линии, т.е. Оконные координаты первой вершины.

Possible vertex points outside a fragment's diamond

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

  • Если вы начинаете с желтой вершины, тогда первая строка проходит через алмаз и выходит из нее, когда она проходит горизонтально вправо. Поэтому фрагмент будет создан в результате растеризации первой линии.

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

  • Если вы начинаете с голубой вершины, тогда первая строка проходит через алмаз и выходит из нее, когда она проходит горизонтально вправо. Поэтому фрагмент будет создан в результате растеризации первой линии.

  • Если вы начинаете с красной вершины, тогда первая строка выходит из фрагмента без прохождения алмаза и, следовательно, никогда не выходит из алмаза. Последняя линия также не пройдет через алмаз и, следовательно, не выйдет из нее, когда она восходит к красной вершине. Поэтому фрагмент не будет создан в результате растеризации линии.

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

Ответ 3

@Трубадур прекрасно описал проблему. Это не ошибка драйвера. GL действует точно так, как указано. Он предназначен для субпиксельного точного представления космического объекта мира в пространстве устройства. Это то, что он делает. Решения: 1) анти-псевдоним, поэтому пространство устройства дает больше верности и 2) организует мировую систему координат, где все преобразованные вершины попадают в середину пикселя устройства. Это "общее" решение, которое вы ищете.

Во всех случаях вы можете достичь 2), перемещая точки ввода. Просто сдвиньте каждую точку достаточно, чтобы преобразовать ее в середину пикселя устройства.

Для некоторых (неизмененных) наборов точек вы можете сделать это, слегка изменив преобразование вида. Примером может служить сдвиг в 1/2 пикселя. Он работает, например. если мировое пространство представляет собой целочисленное преобразование пространства устройства, за которым следует перевод целыми числами, где мировые координаты также являются целыми числами. Однако при многих других условиях +1/2 не будет работать.

** Изменить **

NB, поскольку я сказал, что в преобразование вида можно встроить равномерный сдвиг (1/2 или любой другой). Нет никаких причин для скрипта с координатами вершин. Просто добавьте перевод, например. glTranslatef(0.5f, 0.5f, 0.0f);

Ответ 4

Попробуйте изменить 0.5 на нечетное магическое число, используемое повсеместно 0.375. Используется opengl, и X11 и т.д.

Упоминается упомянутое правило бриллианта и как рисуются рисунки, чтобы избежать лишних переуплотнений пикселей.

Предоставьте некоторую ссылку, но там их много, просто найдите ключевые слова opengl 0.375 diamond rule, если вам нужна дополнительная информация. Это о том, как схемы и заливки обрабатываются алгоритмически в opengl. Это необходимо для пиксельной обработки изображений текстур, например, для спрайтов 2d.

Посмотрите это.

Хотите что-то добавить; Поэтому, делая то, что вы хотите, внедрение правила бриллианта, реализованного в коде, будет просто одним лайнером; измените значение 0.5 на 0.375; И он должен отображаться правильно.

glTranslatef(0.375, 0.375, 0.0);