Загрузка...
 
Печать
ИГРОКОДИНГ  »  ИГРОКОДИНГ: Учебный курс  »  Программируем 3D-шутер от первого лица (FPS) (Win32, Cpp, DirectX9)  »  Часть 1. Создание движка  »  1.16 Управление сценой
Программируем 3D-шутер от первого лица (FPS) (Win32, C++, DirectX9)

1.16 Управление сценой (Scene Management)


В этой Главе:

  • Изучим различные виды отсечения (culling): усечённая пирамида вида (view frustum) и отсечение при перекрытии (occlusion culling).
  • Рассмотрим темы определения столкновений (collision detection), реакции на них (response) и гравитации (gravity) в игре.
  • Создадим еффективную систему рендеринга с использованием т.н. дерева октантов (октодерева, octree(external link)).


Наш движок почти закончен и скоро он будет готов к действию. В этой Главе мы сфокусируемся на разработке т.н. системой управления сценой (scene management system) и её последующей интеграции в движок.

Что из себя представляет управление игровой сценой?

Ты должно быть уловил основную идею управления сценой ещё в Главе 1.1. Если помнишь, игровая сцена представляет собой игровой уровень или карту (map, мапу) в игре. Она полностью определяет 3D-окружение, ограниченное каким-либо способом и отделённое от других сцен игры (вспомним экраны загрузки с надписью "Loading" в классическом Half-Life), прямо как сцены в кино.
Термин "управление сценой" включает в себя все аспекты, связанные с управлением сценой в игре. Задачи по управлению сценой могут сильно отличаться в разных играх. Тем не менее существуют несколько общих элементов, присущих всем играм:

  • Возможность корректной загрузки и уничтожения сцены.
  • Управление данными сцены, как например игровые объекты.
  • Показ сцены игроку, который в большиснтве случаев происходит путём рендеринга её 3D-геометрии.

Список далеко не полный. Мы можем разбить его на несколько более специфичных задач. Вообще необходимо заранее определить задачи, которые потребуются для выполнения в ходе создания движка и игры. Ведь управление сценой в одной игре может сильно отличаться от оного в другой по своим методам. Кроме того нам необходимо определить, что должно управляться движком, а какие задачи оставить для выполнения непосредственно игрой. К счастью мы знаем, что наш движок разработан специально для игр жанра FPS (First Person Shooter) поэтому мы можем сделать массу предположений и разработать управление сценой, "заточенное" именно под игры данного типа. Это вовсе не значит, что наш движок будет совершенно неприменим для игр других жанров. Просто в этом сучае он будет не настолько хорош и потребует соответствующего модифицирования.
А теперь более подробно остановимся на задачах, которые должен выполнять наш движок для управления сценой в FPS-игре. Во-первых нам необходимо чтобы наш движок обладал возможностью загружать сцену с использованием простого скрипта. Скрипт будет определять такие свойства, как например какой 3D-меш для этого использовать, как будет выглядеть освещение и т.д. Движок также должен уметь считывать иерархию фреймов из файла меша сцены а также идентифицировать определённые типы фреймов, которые в него входят. Это позволит нам хранить прямо в файле меша сцены такие данные, как точка спауна (появление на игровой сцена) объекта игрока и координаты спавн-объектов. Также не забываем о том, что наш движок долен уметь корректно уничтожать сцену при завершении работы игрового приложения с целью не допустить утечек памяти.
Когда дело касается управления данными игровой сцены, мы хотим чтобы движок контролировал все объекты сцены и вовремя их обновлял. Помимо этого он должен уметь определять столкновения объектов друг с другом и со сценой а также давать возможность объектам взаимодействовать друг с другом. Мы также хотим, чтобы движок рендерил сцену и все объекты на ней. Движок также должен уметь разбивать сцену на составляющие и эффективно рендерить те из них, которые видны игроку в данном кадре.
Как только мы реализуем все эти фичи в движке, это снимет с нас основной груз связанных с этим забот при разработке непосредственно игры. Часть материала в этой Главе считается наиболее хардкорной для изучения. Но без этого никуда. Читаем медленно и вникаем в суть написанного.

Отсечение (Culling)

Один из наиболее важных аспектов управления сценой это её рендеринг. В данный момент мы не будем говорить конкретно о рендеринге. Вместо этого мы обсудим, как этого самого рендеринга избежать... Да, всё верно. Мы рассмотрим методы, которые позволят нашему движку избежать ренедринга как можно большей части сцены, которая, как правило, остаётся за кадром. Это делается с целью снизить нагрузку на видеокарту и таким образом сделать рендеринг максимально эффективным, в том числе для снижения системных требований игры.
Производительность игры (game's perfomance) часто измеряется по числу кадров в секунду (fps), которое она может выдать в данной ситуации. Подобные замеры называют бенчмарком (benchmark). Они производятся для изучения того, насколько хорошо движок проделывает свою работу по управлению сценой. В ситуации когда движок должен обрабатывать и рендерить множество различных объектов, нередки случаи "проседания" показателя fps. Когда движок обрабатывает и рендерит малое количество данных, fps, как правило, повышается. Таким образом имеет смысл изучить специальные техники, позволяющие минимизировать объём данных, которые движок будет обрабатывать и рендерить в каждом кадре.

Закрыть
noteОбрати внимание

Вообще одно лишь измерения частоты кадров (фреймрейта, fps) считается технически неточным методом. Причиной этого является тот факт, что падение фреймрейта со 100 до 90 fps это совсем не одно и то же что падение с 40 до 30 fps. В этом случае лучше взять процентное соотношение падения. В этом случае падение фреймрейта со 100 до 90 fps образует снижение производительности в 10%, в то время как падение фреймрейта с 40 до 30 fps составит снижение производительности в 25%, что гораздо хуже.

Одним из самых распространённых методов увеличить производительность работы видеокарты является т.н. отсечение (culling). Иногда эту технику называют удалением скрытых поверхностей (hidden surface removal). Отсечение является общим термином, означающим фильтрацию граней, которые не требуется рендерить в данном кадре. Это означает, что любая поверхность игровой сцены которая не подлежит рендерингу в данном кадре, не должна отправляться на конвейер Direct 3D. Это совсем не означает, что данные грани не существуют или удалены из сцены. Это просто означает, что движок не отправит их в DirectX-конвейер для последующего рендеринга. Вопрос лишь в том, как определить какие из граней должны быть отсечены?

Рис.1 Соотношение между аккуратностью отсечения невидимых граней и производительностью
Рис.1 Соотношение между аккуратностью отсечения невидимых граней и производительностью

Существует несколько общих методов, которые мы можем применить для выполнения отсечения. Одни из них простые, другие - сложные. Наиболее эффективным решением, как правило, является комбинирование двух или более методов для достижения наиболее аккуратного отсечения. Причиной этого является тот факт, что каждый метод имеет свои сильные и слабые стороны. Одни методы быстры и нетребовательны к ресурсам компьютера, но как правило неаккуратны. Другие - наоборот, работают медленнее, но более точны. Чаще всего внедряют быстрый и грубый метод отсечения для отсечения большей части невидимых граней (т.е. граней, которые не могут быть видимы игроком в данном кадре). Затем в дело вступает более медленный, но аккуратный метод, который дополнительно удаляет грани, которые не были удалены быстрым методом.
Разработка эффективного алгоритма отсечения невидимых граней являет собой некий компромисс, сбалансированное решение. В современном компьютере есть как минимум 2 основных вычислительных модуля: центральный процессор (CPU) и графический чип видеокарты (GPU). При этом они не должны простаивать на протяжении всего процесса работы игрового приложения. В то же время не следует перегружать какой-либо из них, чтобы не создавать эффекта "бутылочного горла" (bottleneck). Чем больше методов отсечения применяется в данном приложении, тем большая нагрузка идёт на CPU. С другой стороны, чем меньше методов отсечения применяется, тем большая нагрузка идёт на GPU (см. Рис.1). На Рис.1 можно видеть, что чем больше алгоритмов отсечения мы применяем, тем выше производительность. Это справдливо, пока кривая на графике не достигнет точки максимума. После этого производительность начнёт снижаться. Это происходит от того, что CPU образует эффект "бутылочного горла" в тех случаях, когда объём вычислений алгоритмов отсечения достигнет определённого предела вычислительных возможностей процессора. Также на этом графике видно, что чем больше алгоритмов отсечения ты применяешь, тем меньший прирост производительности это даёт. Суть в том, чтобы найти идеальный баланс между объёмом вычислений отсечения, который выполняет CPU, и объёмом геометрии, которую рендерит GPU. Ну и объёмом работы, которую необходимо проделать для достижения удовлетворительных результатов, конечно же.
Как мы до этого упомянули, сегодня в игрокодинге существует несколько общепринятых методов отсечения. Вот самы популярные из них:

Метод отсечения Достоинства Недостатки
Удаление граней, на невидимой наблюдателю стороне объектов (Back Face Culling) DirectX выполняет это автоматически. Достаточно только включить его в настройках. Практически нет. Прирост производительности с лихвой окупает включение этого метода отсечения в графический конвейер.
Усечёная пирамида (Frustum) Относительно легко реализовать. Может быть достаточно нетребовательным к ресурсам компьютера. Откровенно неаккуратен. Многие грани, оказавшиеся слишком близко к наблюдателю (перед т.н. Far Plane) будут отрендерены.
Отсечение скрытых поверхностей (Occlusion Culling) Отлично подходит для удаления поверхностей в поле зрения игрока, заслонённых чем-либо. Может значительно нагружать CPU в сложных сценах.

Для нашей системы отсечения мы будем использовать все 3 метода, представленных выше. Мы уже говорили об отсечении граней со стороны, противоположной направлению взгляда наблюдателя (Back Face Culling) в Главе 1.5. К счастью в DirectX этот метод встроен по умолчанию и для того, чтобы его применить, достаточно просто активировать его. Более того, по умолчанию он всегда включен.
Оставшиеся два метода пока нам незнакомы. Поэтому рассмотрим их более подробно.

Отсечение методом усечёной пирамиды (Frustum Culling)

В предыдущей Главе мы говорили о матрицах вида и проекции. Мы знаем, что матрица вида (view matrix) определяет виртуальную камеру, используемую для просмотра игровой сцены. А матрица проекции (projection matrix) действует подобно линзам этой камеры, контролируя проекцию 3D-сцены на плоский экран монитора. При комбинировании этих двух матриц (читай, перемножении) образуется новая матрица, контролирующая т.н. поле видимости (Field of View, FOV). FOV просто определяет, что именно будет видно в виртуальной камере вида. Для лучшего понимания данного понятия лучше всего обратиться к полю зрения твоих глаз. В то время как твоя голова и глаза остаются неподвижными, смотри прямо перед собой, вытяни свою правую руку перед собой, а затем отведи в сторону так, чтобы ты не мог её видеть даже периферийным зрением. Это означает, что твоя рука при этом находится за пределами твоего поля видимости (FOV). Если ты вернёшь руку и она вновь окажется вытянутой перед твоим лицом, то таким образом она вернётся в твоё поле видимости и ты вновь будешь видеть её.

Рис.2 Усечённая пирамида видимого пространства (View Frustum)
Рис.2 Усечённая пирамида видимого пространства (View Frustum)
Рис.3 Верхняя и фронтальная (передняя) стороны усечённой пирамиды вида, образованные пересечением бесконечно продолжающихся плоскостей
Рис.3 Верхняя и фронтальная (передняя) стороны усечённой пирамиды вида, образованные пересечением бесконечно продолжающихся плоскостей
Рис.4 Сцена частично перекрыта объектами на ней
Рис.4 Сцена частично перекрыта объектами на ней

Тот же самый принцип мы будем использовать для отсечения граней, который не входят в поле видимости виртуальной камеры вьюера (camera's FOV). Для этого мы создадим т.н. усечённую пирамиду видимого пространства(external link) (view frustum), образованную из матрицы поля видимости камеры (camera's FOV matrix). Ты можешь представить себе усечённую пирамиду в виде обычной пирамиды, чья центральная вершина (apex) расположена близко перед камерой (но не касается её "линз"), а основание удалено в направлении, совпадающем с направлением камеры. (См. Рис.2).
Исходный код реализации усечённой пирамиды вида мы рассмотрим чуть позднее в данной Главе. А сейчас рассмотрим как именно усечённая пирамида вида применяется для отсечения невидимых граней. Как только усечённая пирамида вида вычислена, мы можем определить т.н. набор плоскостей (set of planes). Плоскость (plane, реализованная в библиотеке D3DX в виде структуры D3DXPLANE) представляет собой плоскую двухмерную поверхность (surface), которая неограниченно простирается в 3D-пространстве. Она (условно) не имеет толщины и границ. У плоскости 2 стороны (иногда их называют позитивная (лицевая) сторона и негативная (изнанка)). И любая вершина в 3D-пространстве расположена либо на данной плоскости, либо по обе стороны от неё. Наша усечённая пирамида вида может быть определена шестью такими плоскостями (в то же время ты можешь обойтись лишь пятью, если проигнорируешь ближнюю плоскость, что мы и сделаем в рамках данного курса).
Плоскости не заключают в себе какой-либо конкретной формы, т.к. они бесконечно продолжаются вдоль своих осей. Форма усечённой пирамиды образуется только при пересечении этих плоскостей друг с другом, когда они таким образом создают усечённую пирамиду в 3D-пространстве (См. Рис.3).
Теперь, когда ты понял как работает отсечение на основе усечённой пирамиды вида, у тебя не должно возникнуть затруднений при рассмотрении принципа работы отсечения на основе перекрытия.

Отсечение на основе перекрытия (Occlusion Culling)

  • грубо говоря, означает отсечение фрагментов сцены, которые спрятаны за т.н. оклюдерами (от англ. occluder - закрывающий объект, укрытие).

Так что же собой представляет оклюдер? В качестве оклюдера может выступать любой объект, который обладает возможностью скрывать части сцены от глаз игрока. Типичный пример - большое здание или твёрдая стена. Если игрок видит сцену и большая твёрдая стена заслоняет приличную часть игровой сцены, то имеет смысл отсечь всё, что находится за этой стеной (См. Рис.4).
Но как же определить, что тот или иной объект спрятан за стеной? Здесь применяется тот же принцип, что и при отсечении с помощью усечённой пирамиды видимости (view frustum). В этом случае достаточно лишь создать усечённую пирамиду, продолжив линии от оклюдера, направленные от наблюдателя. Теперь, вместо того, чтобы отсечь все объекты, расположенные за пределами этих усечённых пирамид, мы наоборот, отсекаем всё, что находится внутри них (усечённых пирамид) (См.Рис.5).


ИГРОКОДИНГ  »  ИГРОКОДИНГ: Учебный курс  »  Программируем 3D-шутер от первого лица (FPS) (Win32, Cpp, DirectX9)  »  Часть 1. Создание движка  »  1.16 Управление сценой

Contributors to this page: slymentat .
Последнее изменение страницы Понедельник 11 / Декабрь, 2017 02:27:04 MSK автор slymentat.

Последние комментарии

No records to display

Хостинг