Растеризация - Компьютерная графика и ее аппаратная реализация (обзор видеокарт)

Последний этап конвейера называется растеризацией и обозначается буквой "R". Это единственный этап конвейера, который даже в старых акселераторах выполнялся на аппаратном уровне. Растеризация включает в себя субпиксельную подготовку и собственно рендер. Наиболее сложный этап растеризации -- удаление скрытых поверхностей (HSR -- Hidden Surface Removal).

Казалось бы, чего проще отправить рендериться всю сцену целиком. В итоге получим громадный кадр, который потом можно обрезать до размера экрана. Вот только играли бы мы с вами сейчас не в UT 2004, а в тетрис, если бы разработчики решали проблему таким путем. Ведь этот громадный кадр способен посадить на мель любую современную видеокарту. Поэтому поступают по-другому. Определяются полигоны, которые гарантированно не попадут в кадр. Эти полигоны отсекаются. И рендерится только та часть сцены, которую в данный момент "видит" виртуальная камера. Такой подход экономит системные ресурсы. Но отсечь невидимые поверхности -- задача непростая. Как учитывать полигоны, у которых "в кадре" оказывается только кусочек? А как учесть полигон, который находится где-то там за стенкой и все равно не виден, а нам придется его зачем-то просчитывать?

В разные времена проблему отсечения невидимых поверхностей решали по-разному. Но начало всегда было одинаковым. Шестью плоскостями по трем координатам ограничивается область сцены, которая гарантированно будет видна на экране. Эти плоскости образуют объем отсечения (clipping volume), который берется с некоторым запасом. Затем в дело вступает backface culling -- отбрасывание задних граней. У каждого полигона помимо координат вершин есть важнейшая характеристика -- нормаль. Это вектор, который лежит на перпендикуляре, восставленном из геометрического центра треугольника. С помощью специальной функции можно по координатам вершин треугольника определить его нормаль.

У каждого полигона есть две стороны -- лицевая и обратная. Нормаль определяет, куда "смотрит" полигон. Представьте себе сферу. Нормали сферы направлены во внешнюю сторону, и ее поверхность образована лицевыми сторонами граней. Примерно половина полигонов сферы "смотрит" от экрана, вторая половина -- на экран. А это значит, что все полигоны, смотрящие от экрана, гарантированно не видны. Их можно отсечь. И рендерить акселератору придется только половину полигонов сферы. Конечно, не все игровые объекты такие симметричные, как сфера, но у большинства примерно половина полигонов не видна. На этом принципе основывается backface culling.

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

Речь идет о BSP-деревьях. BSP (Binary Space Partitioning) -- это двоичное дерево предварительной сортировки треугольников сцены. С его помощью в процессе рендера можно очень быстро отсортировать треугольники по расстоянию от наблюдателя. Ясно, что в отсортированном BSP-дереве верхние треугольники будут видимыми, а нижние -- нет. Но построить BSP-дерево -- сама по себе ресурсоемкая задача. Этот метод не идеален.

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

Для ускорения процедуры отсечения невидимых граней разработчики применяют и некоторые допущения и упрощения сцены. Всю сцену разбивают на большие кластеры, например на отдельные здания. Каждый кластер разбивают на кластеры поменьше: здания -- на комнаты, и т. д. В каждой комнате выделяют отдельные предметы: столы, стулья, ящики, шкафы. Сложные предметы (людей, например) представляют в виде параллелепипедов. Из всего этого собирается большое иерархическое дерево уровня с кластерами вместо листьев. Теперь, если весь большой кластер игроку не виден, то и все подкластеры внутри кластера тоже не видны, а значит, их не надо обсчитывать. Такой подход экономит мощности акселератора, но вот применяют его нечасто. Никакой даже самый интеллектуальный акселератор на сегодняшний день не может сам разбить уровень на кластеры и подкластеры. Все тяготы по построению иерархии кластеров ложатся на хрупкие плечи программистов.

Различными способами сцена освобождается от лишних полигонов. С невидимыми полигонами мы разобрались, но остались еще перекрывающиеся полигоны. Представьте себе модель стола. Мысленно разбейте ее на полигоны. Какие-то полигоны будут перекрывать другие. Но это в нашей реальности. В виртуальной реальности не все так однозначно. Если полигоны отрендерятся в произвольном порядке, вполне может получиться такая картина: одна из задних ножек стола перекрывает стол спереди, торец где-то на заднем фоне, а передняя ножка вообще вывернута наизнанку. А все потому, что полигоны нельзя рендерить абы как. Их надо рендерить в строгом порядке. Те, что дальше -- рендерятся раньше. Те, что ближе -- позже.

Это еще не все. Бывает, что два полигона пересекаются в некоторой точке. Кого из них рендерить первым? Нам понятно, что надо отрендерить часть первого и часть второго. Но вот процессору это совсем не понятно. Есть два общепринятых метода. Первый -- это знакомый многим Z-буфер. А второй -- так называемая Z-сортировка. Z-сортировке - это довольно грубый и малоэффективный метод. А вот о Z-буфере поговорим, так как почти в каждой игре есть настройки, связанные с ним.

На большинстве сцен одни объекты находятся впереди и частично блокируют вид на другие объекты. Для таких сцен программное обеспечение должно не только просчитать относительный размер объектов, но и учитывать информацию, какие объекты закрывают другие и насколько сильно. Наиболее часто для этого используется Z-буфер (Z-Buffer). Свое имя этот буфер получил от названия оси Z, или воображаемой линии, идущей за экран через сцену к горизонту. (Две другие оси - это ось X, измеряющая ширину сцены, и ось Y, измеряющая высоту сцены).

Z-буфер присваивает каждому полигону номер в зависимости от того, насколько близко к переднему краю сцены располагается объект, содержащий этот полигон. Обычно меньшие номера присваиваются ближайшим к экрану полигонам, а большие номера - полигонам, примыкающим к горизонту. В настоящем мире, наши глаза не могут видеть объекты закрытые другими, поэтому у нас нет проблем в определении видимых объектов. Но эти проблемы постоянно возникают перед компьютером, и он вынужден непосредственно их решать. При создании каждого объекта, его Z-значение сравнивается со значением других объектов, занимающих те же области по координатам X и Y. Объект с самым маленьким Z-значением будет полностью прорисовываться, другие же объекты с большими значениями будут прорисованы лишь частично. Таким образом, мы не видим фоновых объектов, выступающих через персонажей. Так как Z-буфер задействуется перед полной прорисовкой объектов, скрытые за персонажа части сцены не будут прорисовываться вообще. Это ускоряет графическую производительность.

Z-буфер -- это специальная область видеопамяти. Чаще всего Z-буфер располагается во фрейм-буфере. В Z-буфере хранится значение глубины для каждого пиксела. Когда рендерится новый пиксел треугольника, его глубина сравнивается со значением, которое уже хранится в Z-буфере для точки на экране с такими же координатами X и Y, то есть с соответствующей точкой предыдущего треугольника. Когда новый пиксел "глубже", чем значение в Z-буфере, пиксел не виден. Если значение его глубины меньше значения в Z-буфере, пиксел виден, и значение его глубины записывается в Z-буфер. В современных акселераторах часто используется W-буфер, в котором хранятся значения, обратные Z-глубине. W-координаты тем удобны, что без лишних расчетов корректно соотносятся с перспективой, тогда как для Z-координат приходится корректировать результат интерполяции координат вершин треугольников. Во многих случаях Z-буфер можно отключить. Это не значит, что изображение превратится в беспорядочную мешанину перекрывающих друг друга полигонов. Просто вместо Z-буферизации в дело вступят упрощенные алгоритмы. С одной стороны, освободится значительный кусок фрейм-буфера, и вы сможете играть в более высоких разрешениях. Но за это придется расплачиваться появлением очень неприятного артефакта -- Z-алиасинга (он же Z-алайзинг). Часто вы будете видеть, как полигоны "проваливаются" друг сквозь друга.!

Некоторые игры позволяют задать глубину или разрядность Z-буфера. Чем выше разрешающая способность, тем точнее рендерятся полигоны. Вас могут попросить выбрать между 16-, 24- и 32-разрядным Z-буфером. Если выбрать буфер с малой разрядностью, может появиться Z-алиасинг, потому что порой пикселам двух треугольников будет соответствовать одна и та же Z-координата, а значит -- и глубина.

После того как определено, какие пикселы полигонов входят в конечный кадр, акселератор рендерит сцену. Но тут есть проблема -- мерцанием изображения. Мерцание появляется из-за того, что экран обновляется не мгновенно. Вся графика прорисовывается в реальном времени. Решена она просто и изящно. Каждый новый кадр сначала рендерится не на экран, а в специальный буфер в видеопамяти, который называется фрейм-буфером. У фрейм-буфера два слоя. В заднем -- новый кадр, а в переднем -- кадр текущий. Когда приходит время очередного рендера, содержимое этих слоев меняется местами (swap). В итоге на экране мы видим новый кадр, а старый кадр пересылается в задний буфер, где немедленно затирается очередным свежеотрендеренным. Этот метод получил название двойной буферизации. Уже известный вам Z-буфер часто делит жизненное видеопространство с двойным кадром во фрейм-буфере. Отсюда и некоторые сложности в их совместной работе.

Похожие статьи




Растеризация - Компьютерная графика и ее аппаратная реализация (обзор видеокарт)

Предыдущая | Следующая