Главная » Программирование игр под Android » Z-БУФЕР: НАВОДИМ ПОРЯДОК – РАЗРАБОТКА ИГР ДЛЯ ОС ANDROID

0

 

Что такое z-буфер? Когда OpenGL ES отрисовывает треугольник в кадровый буфер, он просто меняет цвет пикселов, составляющих этот треугольник. Z-буфер очень похож на кадровый буфер в том, что он также имеет хранилище для каждого пиксела на экране. Однако вместо того, чтобы хранить цвета, он хранит значения глубины. Значение глубины пиксела – это приблизительно нормализованное расстояние от соответствующей точки в 3D до ближней плоскости отсечения области видимости.

OpenGL ES запишет значение глубины для каждого пиксела треугольника в буфер по умолчанию (если z-буфер был создан вместе с кадровым буфером). Нам остается приказать OpenGL ES использовать эту информацию для того, чтобы решить, будет ли отрисовываемый пиксел ближе к ближней плоскости отсечения, чем тот, который уже присутствует. Для этого следует лишь вызвать метод gl Enabl е, передав ему соответствующий параметр:

Это все, что необходимо сделать. Далее OpenGL ES сравнит значение глубины входящего пиксела со значением глубины пиксела, уже находящегося в z-буфере. Если оно меньше, то пиксел будет ближе к ближней плоскости отсечения, поэтому его следует отрисовывать перед тем пикселом, который уже находится в кадровом и z-буфере.

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

Рис. 10.5. Изображение в кадровом буфере (слева); содержимое z-буфера после отрисовки каждого треугольника (справа)

Как и в случае с кадровым буфером, нам приходится очищать z-буфер каждый кадр, в противном случае значения глубины пикселов последнего кадра все еще будут находиться там. Чтобы сделать это, мы можем вызвать метод glС1еаг следующим образом:

Это очистит кадровый буфер (или буфер цветов), как и z-буфер (или буфер глубины), оба за один раз.

 

Исправляем последний пример

Решим проблемы последнего примера с использованием z-буфера. Я просто скопировал весь код в новый класс, который называется ZBuf ferTest, и изменил метод present нового класса ZBuf ferScreen так, как показано в листинге 10.4.

Листинг 10.4. Фрагменты класса ZBufferTest.Java: использование z-буфера

Первое, что мы изменили, – аргументы, передаваемые методу glClearO. Теперь мы очищаем оба буфера вместо одного кадрового буфера. Мы также разрешаем тестирование глубины перед тем, как отрисуем два треугольника. После того как отрисовка заканчивается, мы запрещаем тестирование. Почему? Представьте, что мы хотим отрисовать двухмерные элементы пользовательского интерфейса в верхней части нашей ЗО-сцены, например текущий счет или кнопки. Поскольку для этого необходимо использовать класс Spri teBatcher, который работает только в двух измерениях, нам не нужно иметь никаких осмысленных координат по оси z для вершин 2В-элементов. Нам также не нужно проводить тестирование глубины, поскольку мы будем явно задавать порядок отрисовки вершин на экране. Результат работы этого примера показан на рис. 10.6. Теперь он выглядит так, как мы и ожидали.

Рис. 10.6. Z-буфер в действии, теперь нет необходимости задавать порядок отрисовки

Наконец-то зеленый треугольник посередине корректно отрисовывается позади красного треугольника. Это происходит благодаря нашему новому лучшему ДРУГУ – 2буферу- Но, как и в случае со многими другими друзьями, наступают времена, когда дружба несколько страдает от некоторых недостатков. Узнаем, какие подводные камни вас могут ожидать, если вы будете использовать z-буфер.

Смешиваем: за вами ничего нет

Предположим, что мы хотим разрешить смешивание для красного треугольника, имеющего координату z = -3. Установим, скажем, альфа-компонент цвета каждой вершины равным 0,5f, что заставит этот треугольник просвечиваться. В нашем случае должен быть виден зеленый треугольник, имеющий координату z, равную -5. Подумаем, что же будет делать OpenGL ES, а также о том, что еще может произойти.

OpenGL ES отрисует первый треугольник в z-буфер и буфер цвета.

Далее OpenGL ES отрисует зеленый треугольник, поскольку он следует после красного треугольника в экземпляре класса Vertices3.

Часть зеленого треугольника, находящаяся позади красного, не будет показана на экране, поскольку ее пикселы не пройдут тест глубины.

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

Когда мы используем смешивание в комбинации с z-буфером, нам следует убедиться в том, что все прозрачные объекты упорядочены по возрастанию расстояния от камеры, и отрисовывать их от последнего к первому. Все непрозрачные объекты должны быть отрисованы перед Прозрачными. Их не обязательно сортировать.

Напишем простой пример, демонстрирующий это. Мы сохраним нашу текущую сцену, состоящую из двух треугольников, и установим альфа-компонент цветов вершин первого треугольника (z = -3) равным 0,5£ В соответствии с правилом сначала нам необходимо отрисовать непрозрачные объекты, в нашем случае зеленый треугольник (z = -5), а затем – все прозрачные, от дальнего к ближнему. В нашей сцене присутствует только один прозрачный объект: красный треугольник.

Мы скопируем весь код предыдущего примера в новый класс, который называется ZB1 endi ngTest, и переименуем ZBuf f erScreen, находящийся в нем, в ZB1 endi ngScreen. Нам нужно изменить цвета вершин первого треугольника, а также разрешить смешивание и отрисовку двух треугольников в заданном порядке в методе present . В листинге 10.5 показан измененный код.

Листинг 10.5. Фрагменты класса ZB1endingTest.java: смешивание при доступном z-буфере

В конструкторе класса ZB1 endi ngScreen изменим лишь альфа-компоненты цветов вершин первого треугольника, теперь они будут равны 0,5. Это сделает первый треугольник прозрачным. В методе present  выполняются привычные операции вроде очистки буферов и установки значений матриц. Мы также разрешаем смешивание и устанавливаем подходящую для этого функцию. Более интересно то, как именно мы будем отрисовывать два треугольника. Сначала отрисуем зеленый треугольник, который является вторым в экземпляре класса Verti ces3. Он является непрозрачным. Все непрозрачные объекты должны быть отрисованы перед прозрачными. Далее отрисовываем прозрачный треугольник, который является первым в экземпляре класса Vertices3. Для обоих вызовов метода отрисовки verti ces. Draw просто используем подходящее смещение и счетчики вершин, передавая их как второй и третий параметры. На рис. 10.7 показан результат работы этой программы.

Рис. 10.7. Смешивание при включенном z-буфере

Изменим порядок, в котором будут отрисовываться два треугольника, следующим образом:

Рисуем первый треугольник, начиная с вершины 0, а затем второй, начиная с вершины 3. В результате сначала будет отрисован красный треугольник спереди, а затем зеленый треугольник сзади. На рис. 10.8 показан результат работы такой программы.

Рис. 10.8. Смешивание выполнено неверно; дальний треугольник должен быть виден сквозь ближний

Все наши объекты являются треугольниками, что делает примеры несколько примитивными. Мы вернемся к рассмотрению связки смешения и z-буфера, когда будем отрисовывать более сложные фигуры. Теперь подытожим все рассмотренное в виде последовательности действий.

1. Отрисовка непрозрачных объектов.

2. Сортировка всех прозрачных объектов от самого далекого от камеры до самого близкого.

3. Отрисовка всех прозрачных объектов в отсортированном порядке от самого далекого до самого близкого.

Сортировка может быть основана на расстоянии от центра объекта до камеры (так происходит в большинстве случаев). Вы столкнетесь с проблемами, если один из ваших объектов велик и может перекрывать несколько других объектов. Без довольно сложных трюков мы не сможем разобраться с этой проблемой. Существует несколько пуленепробиваемых решений, которые отлично сработают на обычных персональных компьютерах, но не могут быть реализованы на устройствах с ОС Android из-за того, что их функциональность GPU ограничена. К счастью, такая проблема довольно редка, и в большинстве случаев можно обойтись простой сортировкой.

Источник: Mario Zechner / Марио Цехнер, «Программирование игр под Android», пер. Егор Сидорович, Евгений Зазноба, Издательство «Питер»

По теме:

  • Комментарии