Довольно часто возникает задача части грани поверх ранее выведенной всей грани или же повторного
вывода всей грани с другими параметрами (например, вершинной программой).
В подобный случаях довольно часмто возникает довольно неприятная ситуация, пример которой приводится
ниже.
Рис 1. Пример z-figting
Данное явление называется z-fighting и связано как с погрешностями численных вычислений с
плавающей точкой (в случае использования вершинных программ), так и с погрешностями при растеризации
примитивов.
Дело в том, что хотя, с математической точки зрения, выводимая повторно грань и лежит в той же
плоскости, что и исходная, но в силу численных погрешностей может оказаться, что на лежит в слегка
отличающейся плоскости, что может привести к тому, что значения глубины для ее пикселов будут
отличаться от соответствующих значений для ранее выведенной грани.
Кроме того, поскольку для вывода грани (и, соответственно, вычисления глубины) используются растровые
(инкрементальные) алгоритмы, то мы также можем получить другие значения для пикселов при выводи части
грани, чем для соответствующий пикселов всей исходной грани.
В результате значения глубины (z) могут крайне незначительно отличаться от значений глубины
для соответствующих пикселов ранее выведенной грани. А это будет приводить к тому, что для ряда
пикселов не будет выполнен тест глубины, т.е. данные пикселы будут просто не выведены.
Использование закона проверки глубины GL_LEQUAL или GL_EQUAL не решает ситуацию, поскольку досточно
значению глубины быть больше значения в буфере, пусть даже на очень малую величину, то тест
глубины не будет пройден.
Естественным способом борьбы с этим явлением является сдвиг выводимых граней в направлении камеры на
достаточно малую величину. Правильно подобранная величина сдвига компенсирует погрешности при
вычислении глубины и обеспечит прохождение теста глубины.
К сожалению явный сдвиг выводимых граней (как и расчет величины этого сдвига) довольно неудобен.
Поэтмоу вместо явного сдвига можно воспользоваться введенной в OpenGL начиная с версии 1.1 функцией
glPolygonOffset, которая обеспечивает автоматический сдвиг заполненный (filled) примитивов.
Эта функция обеспечивает автоматическое добавление к глубине, полученной при растеризации примитива,
следующей поправки:
Величина m - это максимальная скорость изменения глубины грани при изменении экранных
координат (slope).
Величина r - это минимальное значение (для данных параметров режима и видеоускорителя),
обеспечивающее получение отличного значения глубины.
Как и большинство остальных возможностей в OpenGL, использование polygon offset необходимо
явным оббразом включать и выклчать при помощи команд glEnable и glDisable с параметром
GL_POLYGON_OFFSET_FILL:
Ниже приводится пример использования данной функции .
Здесь массивы p и q задают два многоугольника, лежащих в одной плоскости.
Ниже приводится изображение, аналогичное рис. 1, но с использованием polygon offset.
Рис 2. Результат использования polygon offset
Исходный код к этой статье можно скачать здесь.