...
...

Разработка компьютерных игр. Часть 5

Чем дальше в лес, тем больше дров.

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

Как мы уже часто говорили, в эпоху после пиксельной графики разработчики игр стали оперировать блоками, виртуальные пространства разделились на многослойные прямоугольные или ромбовидные графические ячейки, в которые подгружались данные. Изначально подразумевались абсолютно плоские игровые пространства, после чего появились неровности ландшафта за счет использования специальных надстроек в виде слоев, отвечающих за уровень, а по существу это были всего лишь скривленные прямоугольники. Дальше — больше. Но многие спросят: "ОК. А почему же вокруг все время твердят о каких-то треугольниках? Миллионах и миллиардах?" На этот вопрос мы ответим в процессе.

Что показано на рисунке 1?

Простой и достаточно иллюстративный пример-опыт. Представим, что мы находимся на самой заре компьютерной мультимедийной эры (в начале 90-х, а то и намного раньше), и перед нами стоит задача сделать интерактивные шарообразные элементы (не мелкие). Цель — они должны восприниматься объемно и изменять блик от света в процессе движения, а также масштабироваться. Напомню, что в прошлом материале серии мы рассматривали наше восприятие объема, то есть оно осуществляется мозгом при анализе множества составляющих, среди которых для нашего случая сначала используем тень, то есть понадобится специализированная закраска. Конечно, многие могут отметить, что можно все сделать и на уровне точек (пиксельная графика), то есть представить объект как их множество и при каждом движении рассчитывать цвета. Ход мыслей верный, но на самом деле, поскольку объект не мелкий, это подразумевает огромный пласт расчетов, не говоря уже о требуемом большом куске кода, а с масштабируемостью вообще будет завал, поэтому в данном случае моделлеры используют аппроксимацию, то есть упрощение графического представления объектов. Опять же, почему? Допустим, в любом из графических редакторов вы нарисовали треугольник. Теперь его нужно закрасить — выбираем инструмент "Заливка" и нужный цвет, производим действие. Что произошло на самом деле? Рисуя треугольник на плоскости, вы обозначили границы некоторой области. Точки, находящиеся внутри оной, принадлежат треугольнику, а те, что вне — фону. Давая команду закрасить фигуру, вы просто меняете свойство "Цвет" для точек, находящихся внутри треугольника. Теперь представьте себе очень сложный многоцветный объект, который нужно не только раскрасить, но и смоделировать. Рассчитывать каждую цветовую точку? Нет, лучше разбивать все на одноцветные примитивы — мелкие составляющие элементы. Так и стали поступать в самом начале. Из школьного курса геометрии мы знаем три примитива среди геометрических фигур: круг, квадрат (четырехугольник) и треугольник. Использовать круги в качестве составных элементов, из которых мы можем получить цельную фигуру, неприемлемо. Остаются два варианта — делаем. Изначально попробуем с четырьмя четырехугольниками (на рисунке первый вертикальный ряд). В итоге с их помощью мы получили многогранник, издалека напоминающий окружность. После этого начинаем делать закраску элементов в однотонные цвета, изначально предположив, что источник света находится справа сверху. Для простоты эксперимента используем только четыре цвета: белый, светло-серый, темно-серый и черный. Что-то получилось. Возвращаемся к прошлому материалу — перечислению составляющих нашего объемного восприятия — и вспоминаем: анализ изменения пропорций. Предположим, что источник света или сам наблюдатель двигается. Следовательно, бликующая (самая яркая) область изменится по размерам, и все остальные элементы должны поменять пропорции своих сторон. Делается это программно, но в данном случае мы смоделируем одну из ситуаций, сместив опорную точку (назовем ее так) нашей модели на некое расстояние. Закрасим все, согласно тому, как это было раньше. Что получилось в итоге? Наш "шар" отказался выглядеть как шар, и даже более того: он выглядит как прямоугольный контейнер. Что делать? Нужно разбивать его на большее количество граней. Во втором вертикальном ряде рисунка 1 вы видите, как сказалось разбиение четырехугольников на треугольники. Мы получили нечто пикообразное, но оно уже воспринимается как круглый предмет — это очень хорошо. Разбиваем дальше. Из рисунка вы видите, что чем больше элементов мы внедряем (разбиваем на большее количество составных примитивов), тем более реалистичным становится наш шар, и на контейнер он уже совсем не похож. В данном случае имеет смысл сказать о том, что, если бы мы таким образом рисовали конус, то при изменении движения (в нашем случае изменения положения опорной точки) все элементы предусматривали другой алгоритм преобразований. То есть в данном случае практически чистым рисованием мы из плоской фигуры только за счет различного изменения пропорций примитивов (составных элементов — трех- и четырехугольников) можем подчеркнуть характер объема общей фигуры. В последнем вертикальном ряду рисунка 1 показан некий фрагмент неровной поверхности. Вы видите, что, смещая нашу опорную точку и изменяя пропорции примитивов по специальным законам, можно достичь весьма реалистичного результата. То есть что можно вынести из данного примера в качестве выводов: наличие теней и изменения пропорций примитивов может указать на качество и характер объемности моделируемого предмета. В принципе, как задаются пропорции — вернее, алгоритмы их изменения, — должно быть понятно. Самое главное, что видно даже из этих рисунков, — все составные элементы проектируемой модели связаны по вершинам.

В нижнем правом углу иллюстрации вы еще можете увидеть метод изменения формы предметов с сопутствующим изменением форм примитивов и их окраски. На самом деле скажем вот что:). Ваш покорный слуга рисовал этот рисунок в программе Paint около часа — просто решил вспомнить часть молодости, проведенной за математическим моделированием объемных графических объектов. В то время о 3D ходили только слухи на уровне суперкомпьютеров и фразы "виртуальная реальность", которая являлась чем-то фантастическим. И представьте, сколько времени могло уйти, например, на построение математической модели отображения сферы либо еще более сложного объекта с последующим перенесением в программный код, внесением коэффициентов по изменению пропорций при движении и т.д. По существу, я в то время практически с нуля создавал программу двумерного отображения движущихся трехмерных объектов (то есть их можно было вращать, передвигать и т.п.), и, кстати, не одну, поскольку каждый 3D-объект требовал написания отдельной программы. Причем на данном примере мы фактически работали с полусферой, а для полноценного объема к ней нужно виртуально подсоединить вторую такую же сзади, а при повороте объемной фигуры уже анализировать, какие из примитивов должны быть видимы и как, а какие — нет, причем изменение пропорций будет другим. Вот так непросто. Сейчас для того, чтобы получить нарисованный результат, вам достаточно открыть любой пакет создания трехмерной анимации и создать сферу, выбрав соответствующий инструмент. Это займет около минуты. Градации цвета и тени у вас будут расставляться автоматически с помощью специальных программных модулей.

Отдельно хочется отметить, что любую объемную поверхность можно описать примитивами — за единицу такового в Direct3D принято считать треугольник. Причем в ряде 3D-пакетов многие поверхности часто формируются и отображаются так же, как и в нашем примере, то есть совокупностью треугольников и четырехугольников. Это может быть весьма оправданным для проектирования, но, по существу, зачем нам делать два типа примитивов при учете того, что любой четырехугольник можно создать из двух треугольников. Многие из тех, кто уже далеко продвинулся дальше, спросит о том, а как же происходит тот же экспорт моделей из профессиональных пакетов в формат Direct3D (файлы с расширением *.x), если при их проектировании есть четырехугольные полигоны? На самом деле все просто — чтобы убедиться в этом, откройте файл с расширением *.x в текстовом редакторе (Блокноте). Что вы увидите? Правильно, просто список координат вершин. А уже в свою очередь Direct3D все самостоятельно дробит на треугольники. Насчет оптимизации таких моделей поговорим в последующих материалах. Треугольник как фигура очень удобен не только в проектировании трехмерных моделей, но и в программных расчетах.

Сглаживание и свойства человеческого зрения

Вспоминая давние игры, в которых впервые стали использовать примитивы для создания сложных трехмерных объектов, стоит отметить, что последние там смотрелись весьма угловато и неправдоподобно. Почему это так происходило, вы могли увидеть уже на описанном выше примере — на чем большее количество примитивов разбивается модель, тем более естественно она воспринимается. Первые трехмерные объекты в играх смотрелись очень угловато и неестественно. Это ощущение имеет место по двум существенным причинам. Во-первых, человеческий глаз может детализировать все окружающие предметы до одной угловой минуты — далее все сливается. Таким образом, вы не можете рассмотреть каждый кирпич в далеко расположенном здании, а на экранах мониторов не видите каждый отдельный пиксель (ЖКИ) либо строчку (ЭЛТ). Но системы из треугольников ранней эпохи предусматривали гораздо меньший предел детализации, то есть вы могли отчетливо рассмотреть каждый элемент (треугольник), причем даже шар, нарисованный нами, при достаточно близком рассмотрении выглядел как многогранник. То есть нужны, говоря хорошим языком, элементы градации формы. А по существу речь идет об улучшенной детализации. Во-вторых, сглаживание. Если вы рассмотрите поближе пиксельное изображение, то оно покажется вам также неестественным, а что уж говорить об объектах, каждая грань которых предусматривает резкий переход от одного цвета к другому. Сглаживание реализуется различными путями, но основной из них — добавление промежуточных элементов изменения градации цвета. И хотя с цветом, особенно для линий, графических элементов и точек, все решается и обычными методами antialiasing (сглаживание), такой предусмотрен и в DirectX, на этом все прелести и заканчиваются. То есть внутреннюю структуру моделей все равно нужно усложнять, то есть при необходимости дробить треугольники на малые, добавлять градиентные составляющие и так далее.

Как это реализуется в компьютерных играх? Мы знаем, что чем больше треугольников, тем лучше, но тогда на обработку такого объекта будет уходить очень много машинных ресурсов. Поэтому в рамках каждой сцены рассчитывается дальность (обычно три уровня) по уровням детализации, также можно применить понятие "планов", известных нам из работы, связанной с кино и видео, а в игровой терминологии применяется понятие "плоскостей проекций". В рамках той, которая расположена ближе всего к пользователю, загружаются модели с максимальной детализацией, на среднем плане — со средней детализацией, на дальнем — с минимальной. То есть на самом деле 3D-моделлеры каждую из моделей готовят в трех видах с различной детализацией. Как это увидеть наиболее наглядно? Очень просто. Возьмите какую-нибудь экономическую стратегию с видом сверху. Покрутите колесико мыши, измените масштаб от самого крупного до самого мелкого. Посмотрите, как меняется детализация движущихся объектов на дорогах, и т.п. И в большинстве случаев то, что раньше в играх выводилось на передний план, на современном уровне годится только для среднего и дальнего. Уровень детализации неуклонно растет, а количество треугольников уже может исчисляться миллионами для каждого отдельного персонажа.

Вы думаете, это реальная машина? Нет, это трехмерная модель дизайнера Pascal Wenzel, созданная в Maxon Cinema 4D

Так как же хранятся трехмерные объекты?

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

Посему подробно вникать в вопросы математических основ по реализации оси Z (как для нашего примера, так и для вообще "внутренней кухни") и выводу такого трехмерного объекта на 2D-плоскость (поверхность экрана), скорее всего, нет смысла, потому как эта тема больше относится к написанию ядер 3D-движков, конвейера визуализации и софта трехмерного моделирования. У нас, как вы помните, в качестве такой основы выступает Direct3D с готовыми алгоритмами и библиотеками. Поэтому не будем углубляться в специфические вопросы. В данном случае важно одно: в рамках каждой трехмерной сцены на базе ее геометрического описания формируется изображение в 2D. Происходит это с помощью системы последовательных действий, именуемых конвейером визуализации (rendering pipeline). Именно об этом мы будем говорить подробно и не раз.

На самом деле в *.х-файлах хранятся только координаты вершин, а сетку из треугольных примитивов Direct3D строит самостоятельно

Примитивы в Direct3D

Уф-ф, сегодня для подраздела "В завершение" просто нет места, и это хорошо. Чтобы материал не выглядел совсем уж теоретическим, возьмемся за практику, то есть уже будет над чем поработать и вам. Помимо треугольника, в Direct3D предусмотрено еще два типа примитивов, а именно линия и точка. Их мы в счет принимать не будем. Еще треугольники, кроме закрепившимся за ними постыдного понятия "примитивы", по общепринятой терминологии могут называться полигонами или ячейками сетки. Почему сетки, думается, вам понятно, ведь "внутри" все так и выглядит. Это уже потом мы ее начинаем закрашивать, нанизывать определенную текстуру, добавляем освещение, создаем тени. То есть работы хватает. Итак, смотрим на структуру примитива под именем треугольник — он состоит из трех вершин и трех граней. Вершины могут подразумевать под собой не только координаты собственного месторасположения, то есть это не просто математический термин — Direct3D наделяет их дополнительными особыми свойствами. Формат представления вершин позволяет к самим координатам добавлять множество дополнительных данных — к примеру, информацию о цвете, нормали, координатах текстуры. Сам же формат вершин является достаточно гибким. Он так и называется — Flexible Vertex Format (FVF). Давайте рассмотрим конкретный пример. Чтобы создать собственный формат вершин, изначально создаем структуру:

struct MYVERTEX
{
FLOAT x, y, z; // Задаем координаты месторасположения
DWORD color; // Задаем цвет
};

После этого нам нужно описать формат хранения с помощью комбинации флагов FVF, а именно:

#define FVF_COLOR (D3DFVF_XYZ | D3DFVF_DIFFUSE)

То есть то, что вы задаете, потом просто описывается флагами. Какие бывают их доступные разновидности, можно найти в документации DirectX SDK с поиском по ключевому слову "D3DFVF". Кстати, я неспроста не(!) стал опираться на пример вывода треугольника по вершинам из уроков Direct3D (можно найти в установленном SDK в директории: …\Microsoft DirectX SDK\Samples\C++\Direct3D\Tutorials\Tut02_Vertices). На самом деле там очень небольшой и простой листинг, но вместо флага D3DFVF_XYZ используется D3DFVF_XYZRHW, и формат задается соответственно. В чем их различие, предлагаю разобраться самостоятельно. И в конце этого материала условимся на том, что основные кирпичики трехмерной графики — это все-таки не треугольники, а вершины.

Кристофер, christopher@tut.by

© Компьютерная газета

полезные ссылки
Аренда ноутбуков