Понятия свет и освещенность в компьютерных играх идентичны понятиям окружающего нас с вами мира. В компьютерной графике применяются три типа освещения. Первый – это параллельный, или направленный, источник света. Этот тип освещения не имеет определенного источника света и светит отовсюду, но в определенном направлении. Второй тип освещения имеет название точечный. Этот тип освещения уже имеет источник света, но светит во всех направлениях. Пример: лампочка, люстра, торшер и т. д. Третий источник освещения носит название прожекторный. По названию этого типа освещения легко догадаться, что он собой представляет, а именно направленный конусообразный источник света (пример простого прожектора или фонарика). Освещение, или свет, задается цветовой компонентой или просто цветом в формате ARGB, где A – это альфа-канал для определения степени прозрачности цветов, R представляет красный цвет (red), G – это зеленый цвет (green), B – синий цвет (blue). Сумма всех цветов в итоге приводит вас к одному из оттенков того или иного цвета. Здесь все по аналогии со школьными уроками рисования, где все мы смешивали краски разных цветов и получали, как правило, одну большую черную кляксу. К слову, цвет можно использовать не только для освещения, но и для прямого закрашивания вершин полигонов цветом. В этом случае вы можете закрасить всю модель или ее отдельные части (помним, что модель состоит из полигонов) одним из цветов. Для расчета правильности отображения освещения используются нормали. Нормаль – это единичный вектор, расположенный перпендикулярно от плоскости полигона или вершины (рис. 15.5). На базе расположения нормалей происходит расчет освещения объекта. Грубо говоря, те нормали, которые не попадают под источник света, дают понять механизму освещения, какие части объекта будут освещаться источником света, а какие – нет.
Рис. 15.5. Нормали вершин и полигона
15.7. Шейдеры
Это магическое слово приводит большинство начинающих программистов в страшный трепет, но на самом деле шейдеры – это обыкновенная программа, которая пишется на специальном языке программирования и выполняется графическим процессором видеокарты. И не более того. Базис программирования шейдеров аналогичен базису программирования обыкновенных приложений, но, естественно, со своими нюансами. Шейдеры – это программа, состоящая из набора ключевых слов, которая позволяет программисту работать напрямую с вершинами и пикселями. Как и в любом другом языке программирования, понятие и освоение шейдеров сводятся к пониманию общей сущности работы шейдеров и изучению самого языка программирования, на котором пишутся шейдеры.
Суть шейдеров, а точнее шейдерных программ заключается в том, что эта программа исполняется графическим процессором видеоадаптера. Сделано это длятого, чтобы избежать массы повторяющихся операций в коде программы, снизить нагрузку центрального процессора и иметь возможность обращения к процессору видеокарты напрямую. Все эти действия приводят к быстрой работе программы и повышению качества графических эффектов. Графический процессор видеокарты работает несколько иначе, чем центральный процессор приставки Xbox 360, и в связи с этим для доступа к процессору необходимо иметь свой набор команд, или язык рограммирования. Ранее в DirectX для этих целей использовался ассемблероподобный язык программирования шейдеров, состоящий из набора определенных инструкций. В те времена шейдерные программы были небольшими, поэтому языка, основанного на инструкциях, вполне хватало. Со временем шейдерные программы увеличивались, да и сама технология программирования шейдеров развивается и по сей день. Если вас интересует ассемблерный язык программирования шейдеров, а также работа с инструментариями RenderMonkey и FX Composer, то могу вам порекомендовать книгу «Инструментальные средства отладки и программирования шейдеров в DirectX и OpenGL».
15.7.1. Шейдерная модель
Со временем на замену ассемблерного языка программирования шейдеров пришел новый язык с названием HLSL (High-Level Shaders Language, или высокоуровневый язык программирования шейдеров). Этот язык более прост в использовании и, главное, имеет большое сходство с языком программирования С. В момент появления языка HLSL была предложена так называемая Shaders Models 1.0. Это своего рода версия описания шейдерной модели для использования шейдерных программ в программировании графики. Шейдерная модель предъявляет набор требований для графического процессора видеокарты, которые он обязан иметь или исполнять. На сегодняшний день имеются уже три версии Shaders Models. Каждая послеующая шейдерная модель лишь усовершенствует свою предшественницу. То есть имеется набор определенных инструкций, которые может выполнить видеоадаптер с поддержкой Shaders Models 1.0, и если вы попробуете на этом видеоадаптере использовать шейдерную модель третьей версии, то произойдет ошибка в работе программы. Фактически шейдерная модель описывает тот набор операций, который графический процессор видеокарты может или даже способен выполнить. Например, в Shaders Models 1.0 нет возможности использовать циклы, а во второй версии такая возможность (для видеоадаптеров) была добавлена. Технология не стоит на месте и развивается, мощности и возможности графических процессоров растут, поэтому и Shaders Models постепенно усовершенствуется. Первая версия Shaders Models со временем отошла и уже практически не используется в программировании шейдеров, но поддерживается всеми видеокартами без исключения, если, конечно, производитель умышленно не исключил такую возможность. Сейчас отправной точкой в версии модели шейдеров считается версия Shaders Models 2.0, которая используется в консольной приставке Xbox 360 (приставка не может использовать Shaders Models 1.0). Более дорогие компьютерные видеоадаптеры могут работать с Shaders Models 2.0 и с Shaders Models 3.0, но не далек тот день, когда появится Shaders Models 4.0, разработки которой идут полным ходом. Соответственно, появится и новый класс видеокарт, умеющих работать с этой шейдерной моделью. В исходном коде можно легко создать блок кода для проверки оддерживаемой видеоадаптером версии шейдерной модели. Например, в следующем исходном коде происходит проверка видеоадаптера на совместимость с Shaders Models 2.0. Если поддержки этой шейдерной модели нет, то на экран выводится сообщение. Использовать эту конструкции кода необходимо на этапе инициализации программы, а также нужно предусмотреть аварийный выход из приложения, если видеокарта не поддерживает используемую в программе шейдерную модель.
foreach (GraphicsAdapter adapter in GraphicsAdapter.Adapters) { GraphicsDeviceCapabilities caps = adapter.GetCapabilities(DeviceType.Hardware); // проверка второй версии шейдерной модели if (caps.MaxPixelShaderProfile < ShaderProfile.PS_2_0) { System.Diagnostics.Debug.WriteLine(«Нет поддержки Shader Model 2.0.»); } }
Этот код в основном предназначен для компьютерных систем, но есть и более рациональные решения поддержки различных версий шейдеров. Например, написание универсальных шейдерных программ, использующих исключительно синтаксис всех моделей шейдеров, или написание отдельных блоков кода для всех трех или двух последних версий шейдерных моделей. Для приставки Xbox 360 Shaders Models 2.0 – это основная модель, и на базе этой модели необходимо писать шейдерные программы.
15.7.2. Механизм работы шейдерных программ
Любой язык программирования направлен на описание синтаксиса и семантики. На базе этих правил и ключевых слов пишутся все программы. Точно так же и язык HLSL обладает своим уникальным синтаксисом и семантикой. Язык HLSL имеет очень большое количество ключевых слов, как говорится, на все случаи жизни. Но часть ключевых слов может не поддерживаться определенной моделью видеоадаптера. Вот для этого и нужна шейдерная модель (Shaders Models), которая описывает требования, предъявляемые к видеоадаптеру. В результате шейдерная модель является неотъемлемой частью правил использования высокоуровневого языка программирования шейдеров. Шейдерные программы пишутся в обычных инструментариях (продукты семейства Visual Studio, RenderMonkey, FX Composer…) и сохраняются в отдельных файлах с расширением FX. Затем этот файл или файлы подгружаются в программу, и когда в исходном коде программа доходит до места выполнения шейдерного файла, то в этот момент программа переключается на выполнение файла шейдеров. Как только шейдерная программа выполнена, работа приложения опять возвращается к основному исходному коду программы. Но поскольку шейдеры очень «плотно» интегрируются в исходный код программы, то и выполнение шейдерных программ происходит достаточно часто и быстро. Таким образом, все приложение постоянно нагружает работой (в хорошем смысле этого слова) графический процессор видеокарты, минуя основной процессор компьютерной системы. Специфичность архитектуры графического процессора заключается в наличии входных и выходных регистров. С помощью языка HLSL происходит запись определенных данных во входные регистры видеоадаптера и задается набор действий, которые графический процессор должен выполнить. После выполнения заданных операций все итоговые результаты помещаются в выходные регистры графического процессора, откуда посредством все той же шейдерной программы полученные данные считываются и выполняются. В данном контексте выполнение шейдерной программы означает рисование на экране итоговой графической картинки.
15.7.3. Вершинные и пиксельные шейдеры
Шейдерная программа – это исходный код, который располагается в одном файле. Несмотря на это, шейдерная программа незримо делится на две различные по своим направленностям части, которые выполняются строго друг за другом. Первая часть программы работает исключительно с вершинами объектов, поэтому и носит название вершинный шейдер. После того как определенные операции над вершинами были сделаны, в работу включается часть программы, отвечающая за работу с пикселями, или пиксельный шейдер. В юрисдикцию вершинного шейдера входят проход через мировую матрицу, видовую матрицу и матрицу проекции, любые матричные преобразования вершин объектов, нормализация, установка освещения, цвета, текстурных координат. Как только все перечисленные действия выполнены, итоговые данные передаются пиксельному шейдеру. Пиксельный шейдер на основании полученных данных рассчитывает и устанавливает цвет для каждого пикселя поверхности экрана. Дополнительно в пиксельном шрейдере выполняются действия по добавлению в игру различных эффектов на пиксельном уровне. Например, туман, вода, огонь и т. д. Все эти эффекты исполняются непосредственно пиксельным шейдером. Никаких других операций над вершинами объектов в пиксельном шейдере быть не может. Этот шейдер работает исключительно с пикселями! Оба шейдера связаны друг с другом неразрывно, и очередность их выполнения одна: сначала в работу включается вершинный шейдер, а затем пиксельный шейдер. Это как в стряпанье пирогов. Сначала вы замешиваете тесто и придаете форму пирогу, а затем ставите этот пирог в духовку. Не замесив теста, вам соответственно и в духовку нечего будет поставить, как, впрочем, замесив тесто, но не поставив его в духовку, вы не сможете испечь сам пирог.
15.7.4. Подводя итоги шейдерной технологии
Подводя итоги вышеописанного материала, можно констатировать следующее. С помощью шейдеров происходит выполнение различных операций над вершинами и пикселями графической составляющей. Это означает, что посредством регистров графического процессора и кода шейдерной программы вы можете управлять как вершинами, так и пикселями графических компонентов игры. На практике такая методика работы с данными позволяет создавать потрясающие по качеству спецэффекты. Вот лишь небольшой перечень всего того, что можно сделать с графикой, используя шейдерные программы: * закрашивание объектов и полигонов цветом; * установка освещения; * работа с матричными преобразованиями; * любые виды объектной анимации; * текстурная анимация; * наложение текстур; * смешивание текстур; * текстурирование ландшафтов; * любые размытые и искаженные изображения; * любимый некоторыми компаниями эффект черно-белых изображений; * пиксельные взрывы и эффекты; * пар, туман, огонь, вода, облака, зеркала, отражение; * все виды bump mapping; * а также многое, многое, многое другое.
15.8. Графический конвейер
Все то, что мы изучили в этой главе, в компьютерной графике известно под названием графический конвейер. Если представить схематично все стадии представления графики на экране телевизора, то можно получить схему, изображенную на рис. 15.6.
Рис. 15.6. Графический конвейер
Когда графический процессор получает задание нарисовать на экране трех- мерную модель, то от программы он получает так называемый вершинный поток данных. Эти данные содержат позиции вершин в пространстве, цвет каждой вершины, нормали, текстурные координаты. Набор этих данных передается на обработку в вершинный шейдер. В этот момент в работу включается исходный код шейдера, а именно блок кода, отведенный для работы с вершинным шейдером. Операции, проводимые вершинным шейдером, зависят от тех задач, которые ставит сам программист, написавший шейдерную программу на языке HLSL, но как минимум происходят все матричные преобразования (мировые, видовые и проекционные), нормализация и установка освещения. На выходе вершинного шейдера поток имеющихся данных проходит этап растеризации и интерполяции. Растеризация содержит несколько правил представления примитивов на экране телевизора, суть которых заключается в точном заполнении всех примитивов своими пикселями. В свою очередь, интерполяция цвета позволяет добиться плавного перехода из одной цветовой компоненты в другую. Например, две вершины имеют разный цвет. Между двумя этими вершинами пространство должно быть заполнено плавно переходящей из одного цвета в другой цветовой компонентой. Задача интерполяции как раз и заключается в заглаживании или плавном переходе от одного цвета к другому. Далее все данные поступают в пиксельный шейдер. На этом этапе ведется работа попиксельно, на уровне каждого отдельно взятого пикселя. Для каждого пикселя вычисляются цвет и другие эффекты, и затем данные выходят из пиксельного шейдера и проходят так называемый Z-буфер. Этот буфер еще известен как буфер глубины. Суть работы Z-буфера заключается в том, чтобы правильно представить объемную модель на двухмерной плоскости экрана телевизора. Задний буфер расставляет вершины объекта в пространстве как в реальном мире, удаляя от нас более дальние части модели и приближая ближние, создавая тем самым объемность мира. После этого все данные поступают в так называемый фрейм-буфер, или просто кадр. Вот такая модель работы используется графическим конвейером для представления графики на экране телевизора. На этом знакомство с основами программирования 3D-графики подошло к завершению, и мы переходим к работе над новым проектом трехмерной игры. Это не значит, что вы изучили всю концепцию программирования 3D-графики, но полученных знаний вам вполне хватит для дальнейшей работы с книгой.