Анатомия игровых движков
Автор: Дмитрий Чеканов
Освещаем мир
Когда освещение работает, вы его почти не замечаете. Но стоит только его убрать - вы сразу же это увидите. Существует несколько подходов к освещению, начиная от простого определения ориентации полигона к источнику света и добавления процента освещения на основе расстояния от полигона до источника, и заканчивая генерацией точных карт освещения для наложения на основные текстуры. А некоторые API имеют сразу несколько встроенных методов освещения. К примеру, OpenGL предлагает освещение по полигонам, по вершинами или по пикселям.
При вершинном освещении определяется, сколько полигонов касаются одной вершины, и затем вычисляется результирующая нормаль к данной вершине (по нормалям всех примыкающих полигонов). Каждая вершина для данного полигона будет иметь несколько отличающуюся нормаль, так что вы можете проградуировать или интерполировать освещение по полигону, что приведет к более точному освещению. Преимущество такого подхода заключается в том, что «железо» может помочь выполнять освещение с помощью модуля аппаратной трансформации и освещения (T&L). Минусом является то, что при вершинном освещении не получается создавать тени. К примеру, обе руки на модели будут освещены одинаково, даже если свет падает справа, и левая рука должна находиться в тени от тела.
Описанный подход вершинного освещения использует затенение (ретуширование). При использовании плоского освещения во время создании полигона движок будет закрашивать полигон нужным цветом. Такой процесс называется плоским затенением (каждый полигон имеет определенное значение освещенности по всей поверхности, что отнюдь не приводит к качественной картинке, не говоря уже о том, что вы сможете легко определить на экране границы каждого полигона).
Что касается вершинного затенения (его также называют затенением по Гуро) движок рендеринга будет закрашивать уже каждую вершину нужным цветом. При отрисовке пикселя полигона его цвет будет вычисляться с помощью интерполяции цветов вершин, на основе удаленности пикселя от вершин. (Именно такой способ и используется на моделях в Quake III).
Более изощренный способ затенения - по Фонгу. В отличие от простой интерполяции цвета вершин для определения цвета пикселя, здесь для каждого пикселя выполняются те же вычисления, что делались раньше для каждой вершины. При затенении по Гуро вы должны были знать, как свет падает на каждую вершину. При затенении по Фонгу вы должны знать это уже для каждого пикселя. Не удивительно, что затенение по Фонгу приводит к более точному освещению, но оно потребляет больше ресурсов, так как вычисления требуется провести уже для каждого пикселя. Самый простой способ - плоское затенение, но он не приведет к хорошему результату. Самый качественный - затенение по Фонгу, причем он позволяет создавать различные зрительные эффекты, типа полировки. Так что вам предстоит нелегкий выбор при разработке игры
В различном свете
Следующий подход к освещению базируется на вторых текстурных картах (картах освещения, light map), которые смешиваются с существующей текстурой для создания эффекта освещения. Все это прекрасно, но перед визуализацией вам нужно создать карты. Если же в игре у вас используетcя динамическое освещение (то есть источник света либо передвигается, либо включается/выключается), то вам будет необходимо создавать карты освещения на каждый кадр, изменяя их при движении динамического источника. Карты освещения можно создавать довольно быстро, но они требуют памяти для хранения соответствующих текстур. Конечно, можно задействовать технологию сжатия текстур для уменьшения занимаемого пространства, или сокращать размер текстур, даже превратив их в двухцветные (но тогда вы не получите цветного освещения) и т.д. Однако если на сцене используется несколько источников динамического освещения, то создание карт освещения может ощутимо ударить по производительности.
Так что во многих играх используется гибридный подход к освещению. Quake III, к примеру, задействует карты освещения для мира и вершинное освещение для анимированных моделей. Предварительно просчитанное освещение не влияет на анимированные модели - они берут общую освещенность для всей модели от полигона, на котором стоит модель - затем на модель накладывается динамическое освещение для придания нужного эффекта. Гибридное освещение - это компромисс между производительностью и качеством, который многие люди не замечают, но который все же позволяет создать правдоподобные эффекты. Собственно, все игры и основываются на подобном принципе - создание максимально правдоподобного эффекта, пусть даже не совсем верного по своей сути. Конечно, в новом движке Doom все будет по-другому, но и сам движок требует 1 ГГц процессор и карточку не ниже GeForce2 для включения всех эффектов. Прогресс налицо, но и его цена - тоже.
Как только сцена была трансформирована и освещена, происходят операции отсечения. Если не вдаваться в детали, при отсечении определяется, какие треугольники находятся полностью внутри сцены (в поле зрения - view frustum), а какие лишь частично заходят на сцену. Треугольники, полностью находящиеся на сцене, принимаются и обрабатываются. Если треугольник захватывается лишь частично, то его часть вне поля зрения должна быть отсечена, а остающаяся внутри сцены часть должна пройти повторную тесселяцию, чтобы новый полигон полностью попал внутрь поля зрения.
Как только операция отсечения будет завершена, на следующей ступени конвейера происходит пересчет координат треугольников (его также называют scan-line conversion), когда сцена переносится на двумерное пространство координат экрана. Затем происходит непосредственно рендеринг.
Текстурирование и кратная фильтрация
Текстуры очень важны для придания 3D сценам реалистичного вида. Они представляют собой маленькие картинки, которые разбиваются на полигоны и накладываются на объект или на какое-либо место в сцене. Большое количество текстур съедает ощутимый объем памяти, поэтому к текстурам применяются различные технологии уменьшения их размера. Сжатие текстур - одна из подобных технологий, при этом сжатие позволяет сохранить необходимое количество информации. Сжатые текстуры занимают меньше места на CD с игрой, и, что более важно, в оперативной памяти и в памяти на видеокарте. Еще одним плюсом является то, что когда карта запрашивает отображение текстуры на экране в первый раз, то из оперативной памяти по шине AGP в память видеокарты отсылается сжатая (меньшая по размеру) версия текстуры, что опять же немного экономит пропускную способность. Сжатие текстур - это хорошо. Ниже мы чуть подробнее остановимся на сжатии текстур.
Кратная фильтрация (Mip-Mapping)
Еще одной технологией, используемой в игровых движках для сокращения потребления памяти и пропускной способности, является кратная фильтрация текстур. При кратной фильтрации создаются несколько копий одной и той же текстуры, причем каждая последующая копия в два раза меньше по размеру предыдущей. Зачем же это надо? Для ответа на поставленный вопрос вам нужно понимать, как 3D карта отображает текстуру. В простейшем случае вы берете текстуру, прилепляете ее к полигону и отображаете его на экран. Здесь мы получаем отношение 1:1, когда один тексель (элемент текстуры) соответствует одному пикселю полигона данного объекта. Если же полигон затем уменьшился на экране в два раза, то на экран будут выведены, скажем, только четные тексели. Обычно ничего плохого в этом нет, но часто такой подход приводит к появлению различных артефактов. Представьте себе кирпичную стену. Скажем, оригинальная текстура - это и есть кирпичная стена с множеством кирпичей, но слой цемента между ними составляет всего один пиксель. Если вы уменьшите полигон в два раза от нормального размера, то при этом будут отображаться только четные пиксели, следовательно, цементная прослойка может вовсе исчезнуть с экрана. Вот мы и получаем нежелательный артефакт.
При использовании кратной фильтрации изображение масштабируется программно, еще до отсылки видеокарте. Таким образом, его можно предварительно обработать, чтобы слой цемента не исчез. Когда 3D карта отображает полигон с текстурой на нем, она определяет масштаб и решает «давай вместо масштабирования самой большой текстуры, я возьму маленькую текстуру - она будет выглядеть лучше». В этом и заключается кратная фильтрация.
Множественные текстуры и наложение карт неровностей
Использование одной текстуры позволяет получить реалистичную 3D графику, однако использование нескольких текстур приводит к еще более потрясающим эффектам. Для этого, конечно, потребуется несколько проходов рендеринга, которые будут потреблять скорость заполнения (fill rate). Однако многоконвейерные 3D ускорители типа ATi Radeon или nVidia GeForce2 и выше позволяют наложить несколько текстур за один проход. При создании многотекстурного эффекта на полигон накладывается первая текстура, затем над ним отображается еще один полигон с другой текстурой и с установленным коэффициентом прозрачности. Благодаря этому можно создать текстуры с эффектом движения, пульсации или даже текстуры с тенями (как мы описывали в части статьи про освещение). Просто нарисуйте первую текстуру, затем нарисуйте сверху полностью черную текстуру с коэффициентом прозрачности - и вы сразу же получите эффект тени. Такая технология называется наложением карт освещения (light mapping, иногда sometimes-dark mapping), и до выхода нового Doom именно такая технология использовалась при освещении уровней в движках Id.
Наложение карт неровностей - это довольно старая технология, которая недавно вновь привлекла к себе внимание. Matrox стала первой компанией, внедрившей технологию наложения карт неровностей в популярных 3D играх несколько лет назад. Технология создает текстуру, которая отражает способ, которым свет попадает на поверхность. Благодаря этому на поверхности можно созi1123500.jpg" width="200" height="216" alt="" border="0">
Наложение карт неровностей позволяет получить более эффектные детализированные текстуры, хотя и здесь требуется определенная ловкость рук, поскольку отображаемые неровности не изменяются относительно угла зрения пользователя. Учитывая по-пиксельные операции новейших карт ATi и nVidia этот недостаток можно устранить. Впрочем, наложение карт текстур до последнего времени не слишком часто использовалось разработчиками игр. Ситуация должна измениться - технология, как видим, полезная и интересная.
Пробуксовка кэша - это плохо
Эффективное управление кэшем текстур жизненно важно для ускорения игровых движком. Как и в любом кэше, хиты - это хорошо, а промахи - это плохо. Представьте себе ситуацию, когда текстуры будут постоянно загружаться и выгружаться из памяти видеокарты, в результате будет наблюi1123700_r.GIF" width="350" height="214" alt="" border="0">
Показана ситуация при использовании тройной буферизации и разрешении дисплея 1024x768x32. Кадровый буфер - 32 Мб. Некоторая память может быть выделена под код драйвера. После выделения памяти под цветовые и Z-буферы, под кэш текстур остается 20 Мб.
Существуют различные технологии минимизации пробуксовки кэша, собственно они и подразумеваются под эффективным управлением кэшем текстур - жизненно важным элементом ускорения движков 3D игр. Управление кэшем текстур - полезная вещь, благодаря ней карта может запрашивать текстуру из основной памяти только один раз, а не много раз. Мы как бы говорим карте: «посмотри на все эти полигоны - они используют одну и ту же текстуру, так что давай мы загрузим ее один раз, и не будем загружать ее с каждым полигоном». Благодаря управлению кэшем текстур API (или программа, стоящая за драйвером видеокарты) не будет загружать текстуру на карту более одного раза. Мощные API типа OpenGL обычно сами управляют кэшированием текстур, то есть API часто само занимается хранением текстур на видеокарте и основной памяти компьютера на основе различных критериев. Однако главные проблемы здесь заключаются в том, что: а) часто вы не знаете правила, которые использует API и б) часто в кадре нужно отобразить больше текстур, чем вмещается в память видеокарты.
Еще одной полезной технологией является сжатие текстур, которое мы уже немного затрагивали чуть выше. Текстуры сжимаются очень похожим на сжатие MP3 файлов способом, хотя о таком уровне сжатия приходится лишь мечтать. Если при сжатии WAV в MP3 мы получаем коэффициент сжатия 11:1, то при аппаратном сжатии текстур обычно получается отношение 4:1, но даже такие цифры заметно улучшают производительность. Кроме всего прочего, карта расжимает текстуры только при необходимости при отображении текстуры «на лету». Это очень хорошо, но мы лишь приоткрыли завесу над будущими технологиями.
Как уже упоминалось, визуализатор заставляет карту отображать одну текстуру в один момент времени. Поэтому нам нужно удостовериться, что все полигоны, использующие одну и ту же текстуру, посылаются последовательно. Следовательно, очень неэффективно отображать сначала все полигоны для одного объекта, потом для другого и т.д. Если мы будем отображать полигоны с одной текстурой последовательно друг за другом, то мы можем передать текстуру только один раз. Quake III осуществляет подобную оптимизацию с помощью системы шейдеров. По мере обработки полигонов, они добавляются во внутренний список шейдеров, и как только все полигоны будут обработаны, визуализатор просматривает список по данной текстуре, и все полигоны с данной текстурой будут отосланы друг за другом.
Описанный процесс не приводит к эффективному использованию T&L модуля на видеокарте (если он там есть). Ведь мы получаем большое количество маленьких групп полигонов, использующих одну и ту же текстуру в разных участках экрана, причем все они базируются на различных матрицах трансформации. То есть в данном случае большее время будет потрачено на настройку аппаратного T&L движка видеокарты. Подобный способ прекрасно работает для персонажей в игре, поскольку они используют одинаковую текстуру по всей модели. Но при отрисовке мира он не является эффективным, поскольку множество полигонов будут использовать одну и ту же текстуру стены. Конечно, обычно подобная ситуация не влияет заметным образом на скорость, поскольку текстуры для мира не слишком велики, так что система кэширования в API легко с ними справится и сохранит текстуру на карте при повторном использовании.
На приставках обычно не используется никакой системы кэширования текстур (конечно, если вы ее не напишите). В случае PS2 лучшего всего использовать подход «текстура за один раз». В Xbox он не актуален, поскольку карта не обладает своей выделенной памятью (архитектура UMA), и текстуры всегда хранятся в основной памяти.
Второе наиболее часто встречающееся узкое место современных стрелялок заключается в потугах прокачать по AGP шине слишком большое количество текстур. Следующее узкое место - обработка геометрии, которая отвечает за то, чтобы объекты появились именно там, где надо. Математика, использующаяся при генерации правильных координат каждой вершины в моделях, на сегодняшний момент отнимает больше всего вычислительных ресурсов 3D стрелялок. Так что вам следует постоянно следить за бюджетом текстур, если вы не хотите, чтобы ваша AGP карта была перегружена. Эффективным шагом будет снижение ограничение уровня кратной фильтрации по максимальному размеру, благодаря чему вы сможете уполовинить размер передаваемых по карте текстур. Конечно, визуальное качество ухудшится, особенно в заставках, зато частота кадров ощутимо возрастет. Такой подход очень хорош для игр по сети. И Soldier of Fortune II, и Jedi Knight II: Outcast используют ограничение кратной фильтрации, то есть они рассчитаны на будущие ускорители, которые пока лишь появляются на рынке. Для того чтобы эти игры работали с нормальным размером текстур, вам нужно минимум 128 Мб памяти на вашем видеоускорителе.
В третьей части мы рассмотрим множество тем, включая управление памятью, эффект тумана, проверку глубины, сглаживание, вершинные шейдеры, API и т.д.
Эффективное использование памяти
Давайте поговорим о том, как память на 3D карте используется сегодня, и какие улучшения нас ждут в будущем. Большинство 3D ускорителей сегодня работают с 32-битным цветом, где 8 бит отводятся на красный, 8 на зеленый, 8 на синий и 8 на атрибут прозрачности для любого пикселя. Такая палитра позволяет получить 256 оттенков для красного, синего и зеленого, что в совокупности дает 16,7 млн цветов - это именно то число цветов, которое человек способен вообще различить на мониторе. Почему же гуру в области дизайна игр Джон Кармак ратует за переход к 64-битному цвету? Если мы не можем увидеть разницы, то зачем нам нужно увеличивать число цветов? Дело здесь совершенно в другом. Предположим, что у нас есть модель, которая освещена несколькими источниками разного цвета. Мы берем оригинальный цвет модели, а затем просчитываем один источника света, который изменяет значение цвета. Затем мы просчитываем еще один источник, который тоже изменяет значение цвета. Здесь и выявляется проблема - мы можем оперировать только 8-ю битами, и после наложения 4-х цветов 8 бит недостаточно для приемлемого представления получившегося света. При этом мы становимся свидетелями ошибок квантизации, которые являются ошибками округления при недостаточном числе битов. То есть вам уже очень скоро будет недоставать битов, и при этом цветовая информация будет теряться. Если бы на каждый цвет отводилось 16 или 32 бита, то можно было бы повысить цветовое разрешение. И даже после наложения оттенка за оттенком мы бы получили адекватное финальное значение цвета. Однако подобное увеличение битов быстро приводит к увеличению потребляемой памяти.
Не следует забывать, что большая часть памяти карты отводится под хранение текстур. Каждый 3D ускоритель несет ограниченное количество памяти, часть которой должна быть отведена под "передний" и "задний" буферы, z-буфер, ну и конечно, текстуры. Если на первой карте Voodoo1 было 2 Мб памяти, то на Riva TNT количество было увеличено до 16 Мб. Затем GeForce2 и ATi Rage получили 32 Мб, а теперь мы стали свидетелями перехода от 64 Мб к 128 Мб. Почему большой объем памяти так важен? Что ж, давайте немножко посчитаем.
Предположим, ваша игра будет работать в 32-битном цвете при разрешении 1280x1024 с 32-битным Z-буфером, поскольку вы желаете получить максимальное визуальное качество. Хорошо, тогда у нас выходит 4 байта на пиксель для экрана, плюс 4 байта на пиксель для Z-буфера, поскольку и там и там размер пикселя составляет 32 бита. При разрешении 1280x1024 мы имеем 1 310 720 пикселей. Умножим на 8 (суммарное число байт для каждого пикселя "переднего" буфера и Z-буфера) и получим 10 485 760 байт. Добавим "задний" буфер и мы получаем 1280x1024x12 байт, или 15 728 640 байт, что равно 15 Мбайт. Если бы на нашем ускорителе было 16 Мб памяти, то под текстуры у нас остался бы жалкий 1 Мб. Если сами текстуры тоже 32-битные, что сегодня является общепринятой практикой, то в 1 Мб мы можем хранить 1 Мб / 4 байта = 262 144 пикселя. Это примерно 4 текстуры размером 256x256.
Приведенный пример ясно показывает, что старые 16 Мб карты не соответствуют требованиям сегодняшних игр. Нам просто придется перезагружать текстуры для каждого кадра в карту. Именно для этого и была предназначена AGP шина, однако AGP все-таки медленнее кадрового буфера 3D карты, поэтому вы получите ощутимое падение производительности. Очевидно, если вы понизите цветовое разрешение текстур до 16-ти бит, то вы сможете передавать их в два раза быстрее по AGP шине. Также если вы уменьшите и экранное цветовое разрешение, то на вашей карте освободится дополнительное место для кеширования текстур. Но вы никогда не сможете определить, как пользователь настроил свою систему. Если их карта может работать на высоком разрешении и глубине цвета, то, скорее всего, именно они и выставлены.
Наведем туман
Теперь давайте перейдем к туману, который является визуальным эффектом. Большинство современных движков поддерживают туман, поскольку он бывает очень полезен для сокрытия мира на удалении, так что вы не видите, как модели и география сцены внезапно появляются на горизонте при попадании в зону видимости. Существует также и объемный туман (volumetric fogging). В данном случае он не служит для сокрытия горизонта, а является физическим объектом, который вы можете видеть, проходить сквозь него или обходить стороной. При этом визуальные эффекты от тумана будут меняться. Подумайте о путешествии сквозь облако - тогда вы сразу поймете, что такое объемный туман. Хорошим примером реализации объемного тумана являются несколько уровней Quake III или версия Lucas Arts Rogue Squadron II для Game Cube. Как нам кажется, там используются одни из самых реалистичных туманов.
При разговоре о тумане неплохо бы упомянуть про альфа-тестирование и альфа-сопряжение (alpha blending) текстур. Когда визуализатор отображает какой-либо пиксель на экране, при условии прохождения пикселем теста Z-буфера (описано ниже), мы должны осуществить альфа-тестирование. При этом мы можем обнаружить, что пиксель должен быть прозрачным, дабы показать, что за ним скрывается. Тогда мы должны знать значение пикселя, который находится за текущим, и далее нам следует обработать цвета двух пикселей и вывести на экран уже результирующий пиксель. Такая операция проходит по сценарию "чтение-модификация-запись", и она потребляет намного больше времени, чем обычная запись пикселя.
Существует несколько алгоритмов сопряжения (смешения) пикселей. Прямое альфа-сопряжение добавляет некоторый процент "теневого" пикселя к оставшимся процентам от нового пикселя. Добавочное альфа-сопряжение берет процент "старого" пикселя и просто добавляет его к новому пикселю (не учитывая процентное соотношение). Последний способ дает более яркие эффекты (к примеру, эффект светового меча Кайла в Jedi Knight II).
С каждым новым поколением 3D ускорителей, мы получаем новые и более сложные виды альфа-сопряжения, которые позволяют создавать умопомрачительные эффекты. Ну и конечно, с учетом пиксельных программ (шейдеров) в GF3+4 и последних платах Radeon, пределом здесь может стать только ваша фантазия.
Трафаретное затенение и проверка глубины
Трафаретное затенение (stencil shadowing) открывает перед нами новые возможности. Мы не будем вдаваться в детали (данная тема заслуживает отдельной статьи), но суть трафаретного затенения заключается в просчете вида модели с перспективы источника света, а затем в использовании полученного результата для создания полигона с результирующей текстурой на нужном предмете. То есть вы, фактически, создаете объемное освещение, которое "падает" на остальные полигоны. В результате мы получаем реалистичное освещение, которое имеет свою перспективу. Но такое освещение потребляет существенные вычислительные ресурсы, поскольку здесь требуется создавать текстуру "на лету" и делать несколько проходов рендеринга для одной сцены. Вы можете создавать тени множеством способов, и, как это обычно бывает, результирующее качество зависит от задействованных вычислительных ресурсов. Здесь разделяют так называемые "четкие" и "мягкие" тени, причем последние более предпочтительны, поскольку они аккуратнее моделируют способ появления теней в реальном мире. К тому же существует несколько других "достаточно хороших" способов отображения теней, которыми пользуются разработчики игр.
http://www.3dnews.ru/documents/3324/03363i1140400.jpg
Проверка глубины
Итак, мы подошли к проверке глубины, при которой закрытые пиксели будут отбрасываться, чтобы избежать избыточной отрисовки (overdraw). Понятие избыточной отрисовки достаточно просто формулируется - чем больше раз вы выводите один и тот же пиксель в кадре, тем больше избыточная отрисовка. Это число зависит от количества элементов на 3D сцене в измерении по глубине (оси Z). Если избыточная отрисовка происходит слишком часто, например, при создании сияющих эффектов заклинаний, как в Heretic II, то частота кадров будет заметно уменьшаться. Некоторые визуальные эффекты в Heretic II, когда несколько людей на экране начинали творить заклинания друг на друга, приводили к ситуациям, в которых один пиксель мог отрисовываться на экране 40 раз! Тут и к гадалке не ходи - необходимо изменить подобную ситуацию, особенно в программном визуализаторе, который просто не может справиться с такой загрузкой, не превратив игру в слайд-шоу. Проверка глубины - это технология, которая используется для определения перекрытия одних объектов другими в данном положении пикселя, чтобы избежать избыточной отрисовки.
Посмотрите на данную сцену и подумайте о том, что вы видите. Другими словами, какие из объектов друг друга перекрывают? На этот вопрос как раз и отвечает проверка глубины.
Достаточно просто объяснить, почему проверка глубины увеличивает частоту кадров. Представьте себе детальную сцену, где находится множество полигонов (или пикселей) друг за другом, и пусть до передачи сцены визуализатору не существует быстрого способа отбрасывания перекрытых полигонов. При сортировке непрозрачных полигонов по оси Z, когда ближние к экрану полигоны будут отображены первыми, вы заполняете сцену самыми близкими к вам пикселями. Когда вы начинаете отображать пиксели за существующими (как определяет проверка глубины), их можно легко отбросить, экономя время и ресурсы. Если бы вы отображали объекты на экране наоборот, от самых дальних к самым ближним, то все перекрытые объекты полностью бы отрисовывались, после чего их бы перекрывали другие объекты. Чем сложнее сцена, тем хуже была бы ситуация. Так что от проверки глубины есть реальная польза.
Сглаживание
Давайте вкратце поговорим про сглаживание. При отрисовке отдельного полигона, 3D карта проверит уже созданную сцену, и "замажет" границы нового полигона, чтобы вы не увидели на экране характерных "лесенок". Технология реализуется двумя способами. Первый подход - на уровне отдельных полигонов, который требует отображения полигонов в порядке от задних к передним, чтобы каждый полигон смог правильно "смазаться" с теми полигонами, что находятся позади его. При беспорядочном рендеринге вы получаете различные артефакты. Второй подход заключается в рендеринге целого кадра в более высоком разрешении, чем нужно, и затем уменьшении изображения для устранения "лесенок" при масштабировании. Второй подход дает хороший результат, но он сильно сказывается на потреблении памяти и на ее пропускной способности, поскольку карте нужно обработать больше пикселей, чем на итоговом кадре. Большинство новых видеоускорителей хорошо справляются с такой работой, причем вы можете выбрать различные режимы сглаживания по отношению качество/производительность.
Вершинные и пиксельные программы (шейдеры)
Перед завершением разговора про технологии рендеринга, давайте немного остановимся на вершинных и пиксельных программах, поскольку в последнее время они привлекают достаточно много к себе внимания. Вершинные программы позволяют напрямую задействовать аппаратные возможности карты, без сильного упора на API. К примеру, если карта имеет аппаратный модуль T&L, вы можете написать DirectX или OpenGL код и надеяться, что вершины пройдут через T&L модуль (удостовериться в этом нельзя, поскольку все происходит на уровне драйвера), или вы можете обратиться к железу и использовать вершинную программу-шейдер напрямую. Благодаря вершинным программам вы можете писать код, напрямую использующий функции карты - T&L модуль и другие функции, предлагаемые видеоускорителем. Фактически подобная возможность уже существует в современном поколении карт ATi и nVidia.
К сожалению, механизм доступа к вершинным программам у них не одинаков. Вы не можете просто написать код вершинной программы и запускать ее на любой карте, как это осуществлялось с кодом OpenGL и DirectX. Однако, поскольку программа работает напрямую с "железом", вы можете рассчитывать на быстрый рендеринг всевозможных эффектов. (Равно как и создание новых потрясающих эффектов - вы можете делать то, что не позволяет API). Фактически вершинные программы переносят 3D карты обратно в мир приставок, где используется прямой доступ к оборудованию, и разработчики выжимают максимум из системы, а не просто уповают на API. Некоторых программистов такой подход шокирует, но таков прогресс. Вообще, вершинные программы - это процедуры, использующиеся для просчета и реализации вершинных эффектов перед передачей их на карту для рендеринга. У вас есть выбор - вы можете выполнить такие же процедуры программно на центральном процессоре или использовать вершинные программы на карте. Трансформация скелета анимированной модели - это основная задача для вершинных программ.
Пиксельные программы (шейдеры) - это процедуры, которые применяются к каждому пикселю при рендеринге текстуры. Вы можете отказаться использовать встроенные режимы сопряжения пикселей и заставить карту применять для этого вашу программу. Благодаря этому вы можете получить новые интересные пиксельные эффекты, типа расфокусировки текстур при удалении, добавлении теплового марева или внутреннего отражения воды.
Поскольку ATi и nVidia пришли к соглашению по версиям пиксельных программ (и DX9 вводит новый язык программирования), будет неудивительно, если DirectX и OpenGL пойдут по пути Glide - с него просто начинать, но он отнюдь не является лучшим способом выжимать максимум ресурсов из карты. В любом случае, будет интересно проследить за развитием ситуации.
В завершение...
Итак, визуализатор - эта та часть игры, за которую программистов сильнее всего ругают. Визуальная привлекательность много значит в игровом бизнесе, так что ее всегда необходимо учитывать. Одним из самых плохих факторов для программистов визуализатора является скорость, с которой развивается индустрия 3D карт. Сегодня вы стараетесь получить картинку с нормально работающей прозрачностью, а завтра nVidia проводит презентацию по вершинному программированию. Все очень быстро меняется, и по большей части тот код, что был написан для 3D карт за последние 4 года, теперь можно выкидывать - он требует полной переработки. Даже Джон Кармак упомянул, что код, написанный им четыре года назад и выжимающий максимум из 3D карты, теперь просто банален - поэтому он решил полностью перерабатывать визуализатор для каждого нового проекта Id. Тим Свини из Epic согласен с Джоном:
"Мы потратили целых 9 месяцев на замену кода визуализатора. Первый Unreal опирался на полностью программный рендеринг, далее он был расширен до аппаратного рендеринга. Следующее поколение движка разрабатывается под GeForce и лучшие карты, в нем обрабатывается в 100 раз больше полигонов, чем в Unreal Tournament.
Мы полностью заменили визуализатор. К счастью, наш движок модульный и мы смогли не ворошить остальную его часть - редактор, физику, AI, сеть, - хотя мы их, конечно же, тоже улучшаем.
API: плохо это или хорошо?
Итак, что же такое API? Это прикладной интерфейс программирования, который является прослойкой между программой и драйвером карты. К примеру, почти каждая 3D карта сегодня по-разному реализует трехмерность. Однако все карты предоставляют разработчику единый интерфейс, что позволяет писать одинаковый код для всех карт. По крайней мере, в теории. Почти три года назад это было верно, но сегодня положение на рынке изменилось, и в лидеры вышла nVidia.
Что касается ПК, то вы можете создать свой собственный программный растеризатор, который будет использовать центральный процессор для отрисовки спрайтов, полигонов и частиц - и люди до сих пор так и делают. Age of Empires II: Age of Kings - это пример хорошего программного растеризатора, точно так же, как и Unreal. Затем вы можете использовать один из двух возможных графических API, OpenGL или DirectX. OpenGL - это кросс-платформенный API (программы, написанные под него, будут работать под Linux, Windows и MacOS), он существует уже достаточно большой период времени, его хорошо знают и понимают, но и его возраст тоже начинает ставить свои барьеры. Еще несколько лет назад над функциональностью OpenGL драйвера работали все производители карт. Однако, как только функциональность была достигнута, перед разработчиками далее не стояло четкого пути для развития, поэтому каждый стал дополнять драйвер своей функциональностью, используя расширения OpenGL.
3dfx создала T-буфер. nVidia озадачилась аппаратной трансформацией и освещением. Matrox пошла в направлении наложения карт неровностей. И так далее. Как видим, за несколько лет в 3D графике произошли существенные изменения.
В любом случае, вторым возможным выбором является DirectX. Он полностью контролируется Microsoft, и по понятным причинам он существует только на ПК с Windows и Xbox. Никаких версий под Apple или Linux. Поскольку Microsoft контролирует DirectX, он встроен во все современные версии Windows.
Основное отличие между DirectX и OpenGL заключается в том, что последний является собственностью "сообщества", а первый - Microsoft. Если вы хотите, чтобы DirectX поддерживал новые функции вашей 3D карты, то вам нужно обращаться к Microsoft, надеяться, что компания к вам прислушается и ждать новой версии DirectX. Что касается OpenGL, то поскольку производитель карты поставляет OpenGL драйвер в комплекте, вы можете сразу же получить доступ к функциям карты через OpenGL расширения. Все, конечно, хорошо, но с точки зрения разработчика игры, вы не можете надеяться на распространенность расширений при написании кода. И хотя расширения могут увеличить скорость на 50%, вы не можете потребовать от каждого пользователя установить GeForce3 в свой компьютер. Нет, конечно, можете, но в следующем году вы будете сушить сухари.
Мы упустили различные детали, но суть ситуации вы должны представлять себе достаточно четко. С DirectX вы всегда знаете, чего вы получите от карты в данный момент времени, так как если функция аппаратно на карте не реализована, DirectX будет программно ее эмулировать (не всегда эффективно, поскольку в ряде случаев такой подход сильно замедляет игру, но мы сейчас говорим не об этом). С OpenGL вы можете выждать из карты больше, однако вы не будете уверены, присутствуют ли на пользовательской машине нужные вам функции.