Главная -
Статьи -
Проекты -
Ссылки -
Скачать -
Из гельминтов -
Юмор, приколы -
Почитать -
Обо мне -
Мысли -
Гостевая -

Что такое z-fighting и как с ним бороться ?

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

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

Пример z-figting

Рис 1. Пример z-figting

Данное явление называется z-fighting и связано как с погрешностями численных вычислений с плавающей точкой (в случае использования вершинных программ), так и с погрешностями при растеризации примитивов.

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

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

В результате значения глубины (z) могут крайне незначительно отличаться от значений глубины для соответствующих пикселов ранее выведенной грани. А это будет приводить к тому, что для ряда пикселов не будет выполнен тест глубины, т.е. данные пикселы будут просто не выведены.

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

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

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

	void glPolygonOffset ( GLfloat factor, GLfloat units );

Эта функция обеспечивает автоматическое добавление к глубине, полученной при растеризации примитива, следующей поправки:

	offset = m * factor + r * units

Величина m - это максимальная скорость изменения глубины грани при изменении экранных координат (slope).

Величина r - это минимальное значение (для данных параметров режима и видеоускорителя), обеспечивающее получение отличного значения глубины.

Как и большинство остальных возможностей в OpenGL, использование polygon offset необходимо явным оббразом включать и выклчать при помощи команд glEnable и glDisable с параметром GL_POLYGON_OFFSET_FILL:

    glEnable ( GL_POLYGON_OFFSET_FILL );

Ниже приводится пример использования данной функции .

	glPolygonOffset ( -1, -1 );

    drawPoly ( p, 4 );

    glEnable ( GL_POLYGON_OFFSET_FILL );

    glColor3f ( 0, 0, 1 );

    drawPoly ( q, 3 );

    glDisable ( GL_POLYGON_OFFSET_FILL );

Здесь массивы p и q задают два многоугольника, лежащих в одной плоскости. Ниже приводится изображение, аналогичное рис. 1, но с использованием polygon offset.

Результат использования polygon offset

Рис 2. Результат использования polygon offset

Исходный код к этой статье можно скачать здесь.


Copyright © 2004 Алексей В. Боресков