ДонНТУ   Портал магистров

Шейдеры и их применение в современном мире


Всем кто хоть немного интересуется развитием компьютерной техники должно быть знакомо слово «шейдер». Особенно оно хорошо знакомо тем кто постоянно играет в видеоигры на персональных компьютерах, ведь для них это повод сменить видеокарту. Но мало кто знает что на самом деле обозначает этот термин. Лучше всего с ним знакому те кто имеет непосредственное отношение к компьютерной графике. В этом разделе я расскажу вам о том, что же такое эти самые шейдеры, где их можно применять, а в конце приведу пару простеньких шейдеров, созданных мной несколько лет назад.

Итак, что же такое эти самые загадочные шейдеры.

Шейдер (англ. Shader) — это программа для одной из ступеней графического конвейера, используемая в трёхмерной графике для определения окончательных параметров объекта или изображения. Она может включать в себя произвольной сложности описание поглощения и рассеяния света, наложения текстуры, отражение и преломление, затенение, смещение поверхности и эффекты пост-обработки. [1]

Программируемые шейдеры гибки и эффективны. Сложные с виду поверхности могут быть визуализированы при помощи простых геометрических форм. Например, шейдеры могут быть использованы для рисования поверхности из трёхмерной керамической плитки на абсолютно плоской поверхности.

Где же можно применить эти самые программы? И кто и как их применяет? Толчком к развитию шейдеров послужили как раз таки видеоигры когда создатель легендарной игры Quake Джон Кармак решил попробовать использовать в графическом движке технологию OpenGL вместо DirectX. И как выяснилось не прогадал, код игры сократился в десять раз, а в Quake смогли играть пользователи MacOS и unix-подобных систем. Небезызвестный режиссер и создатель шедевральной гексалогии «Звездные войны» - Джордж Лукас при создании фильмов также использовал технологию OpenGL. Ну и не нельзя забывать о человеке который создал два самых кассовых фильма благодаря этой технологии - Джеймсе Кэмероне, создателе «Титаника» и «Аватара». Он показал на что способна технология OpenGL в полной мере и как и для чего нужно ее развивать. Ну а любителям 3d-мультфильмов стоит знать что именно компания Pixar стоит у истоков развития и создания шейдеров. Благодаря созданной ее проекту RenderMan мы получили шейдеры, применяющиеся в фильмах, играх, анимациях, и везде где присутствует компьютерная графика.

Но вернемся к нашей теме и рассмотрим подробнее, что же такое шейдеры и как они пирменялись и применяются.В программных графических движках вся цепочка рендеринга — от определения видимых частей сцены до наложения текстуры — писалась разработчиком игры. В эту цепочку можно было включать собственные нестандартные видеоэффекты. Но с появлением видеоакселераторов разработчик оказался ограничен тем набором эффектов, который заложен в аппаратное обеспечение. Вот два примера.

Вода в Quake 2 на программном и на OpenGL-рендеринге. При всём качестве аппаратно ускоренной картинки, вода там — просто синий светофильтр, в то время как в программном есть эффект плеска воды.

В Counter-Strike 1.6 эффект ослепления от светошумовой гранаты на аппаратном рендеринге — белая вспышка, на программном — белая вспышка и пикселизированный экран.

Для того, чтобы составлять сложные видеоэффекты из атомарных операций, и были изобретены шейдеры. Предшественниками шейдеров были процедурная генерация текстур (широко применявшаяся в Unreal для создания анимированных текстур воды и огня) и мультитекстурирование (на нём был основан язык шейдеров, применявшийся в Quake 3). Но и эти механизмы не обеспечивают такой гибкости, как шейдеры.

Типы шейдеров

В настоящее время шейдеры делятся на три типа: вершинные, геометрические и фрагментные (пиксельные).

Вершинный шейдер оперирует данными, сопоставленными с вершинами многогранников. К таким данным, в частности, относятся координаты вершины в пространстве, текстурные координаты, тангенс-вектор, вектор бинормали, вектор нормали. Вершинный шейдер может быть использован для видового и перспективного преобразования вершин, генерации текстурных координат, расчета освещения и т. д. [1]

Вершинные шейдеры являются программами, выполняемыми видеочипами, которые производят математические операции с вершинами (vertex), иначе говоря, они предоставляют возможность выполнять программируемые алгоритмы по изменению параметров вершин и их освещению. Вершинные шейдеры представляют собой естественное развитие идей «геометрического сопроцессора» T&L. Программист получает полный контроль и возможность гибко управлять ядром T&L, и может использовать вершинные шейдеры для расчета специфической геометрической информации, которую потом будут использовать пиксельные шейдеры, получает широкие возможности по аппаратному ускорению обработки вершин полигонов. Каждая вершина определяется несколькими переменными, например, положение вершины в 3D пространстве определяется координатами: x, y и z. Вершины также могут быть описаны характеристиками цвета, текстурными координатами и т.п. Вершинные шейдеры, в зависимости от алгоритмов, изменяют эти данные в процессе своей работы, например, вычисляя и записывая новые координаты и/или цвет. То есть, входные данные вершинного шейдера - данные об одной вершине геометрической модели, которая в данный момент обрабатывается. Соответственно вершинные шейдеры можно использовать разными способами. В первом способе программист пишет относительно небольшую программу и указывает, что ее следует использовать для определенных вершин треугольников. Для каждого кадра ускоритель исполняет эту программу. Примером может служить листочек на дереве, который поворачивается на определенный угол и уже в таком виде отображается на экране,или создание волн при помощи динамической деформации. Другой способ заключается в том, что программа изменяет текстурные координаты вершины. Примером может служить момент, когда уголки губ героини слегка поднимаются и персонаж улыбается. Настройке поддаются практически все параметры, связанные с обработкой вершин, - помимо уже названных геометрических и текстурных координат можно задавать цвет вершины, ее размеры, «фактор тумана». Вершинные шейдеры позволяют детализировать на лету близкие объекты и огрублять дальние. Например, для имитации развевающейся на ветру одежды или волос персонажа, мимики, переливов меха и прочих «тонкостей» геометрии. [3]

Геометрический шейдер, в отличие от вершинного, способен обработать не только одну вершину, но и целый примитив. Это может быть отрезок (две вершины) и треугольник (три вершины), а при наличии информации о смежных вершинах (adjacency) может быть обработано до шести вершин для треугольного примитива. Кроме того геометрический шейдер способен генерировать примитивы «на лету», не задействуя при этом центральный процессор. Впервые начал использоваться на видеокартах Nvidia серии 8. Геометрический шейдер - это шейдер, которому доступны уже собранные из вершин треугольники перед отрисовкой, как целостные объекты. Он может производить какие-либо операции над треугольниками целиком. В том числе, учитывая какие-то контрольные или дополнительные параметры вершин. Можно изменить параметры, можно рассчитать новые, специфичные для всего треугольника, и передать их затем в пиксельный шедер. Можно пометить треугольник предикатом (чтобы затем обработать его по разному, в зависимости от значения предиката), или выбросить его из кандидатов на отрисовку. К сожалению этот шейдер не может, как ожидалось ранее, произвольно создавать новую геометрию и новые треугольники на выходе, но сразу за ним следует еще одна новая стадия - вывод потока. Можно вернуть обратно в буфер данные, прошедшие обработку в вершинной части конвейера, до передачи и отрисовки их в пиксельной части. Затем их можно снова использовать по своему усмотрению. Таким образом можно реализовать множество различных вещей, ранее невозможных или возможных но очень неудобным образом. Например, можно плодить новую геометрию, в том числе реализовать тесселяцию поверхностей на большее кратное число треугольников по тому или иному алгоритму. Для этого надо воспользоваться двумя проходами вершинной части, где первый экспортирует поток данных и коэффицентами деления у потоков между ними (еще в DX 9с появилась возможность устанавливать коэффициент, на который будут делиться индексы вершин при собирании их из нескольких независимых потоков, отдельный для каждого потока). Это несколько менее естественно, чем просто генерация новых вершин и новой геометрии в вершинном шейдере, но теперь это есть и, наконец, то это стало возможным. Можно даже сразу зациклить данные по маршруту выход-вход и воспользовавшись предикатами осуществить циклический отбор и деградирование геометрии, например, упростив модель. Эта технология может создавать очень привлекательные эффекты в реальном времени, такие как карта смещения, выдавливание трафарета тени, точка-спрайт, размытость изображения движущегося объекта и много других, которые будут доступны на оборудовании с поддержкой DirectX 10. [3]

Фрагментный он же пиксельный шейдер работает с фрагментами изображения. Под фрагментом изображения в данном случае понимается пиксель, которому поставлен в соответствие некоторый набор атрибутов, таких как цвет, глубина, текстурные координаты. Фрагментный шейдер используется на последней стадии графического конвейера для формирования фрагмента изображения. Пиксельные шейдеры предоставляют гибкие возможности для программирования блока мультитекстурирования и работают уже с отдельными пикселями экрана, тем самым обеспечивая широкие возможности по обработке фрагментов. Пиксельные шейдеры позволяют по шагам управлять процессом наложения текстур, определения глубины и вычисления цвета фрагментов. Пиксельный шейдер, определяющий функционирование «фундаментальных блоков» ускорителя сымитировать нельзя. Поэтому, говоря о шейдерах и их версиях, как правило, имеют в виду именно пиксельные шейдеры. С вычислительной точки зрения пиксельный шейдер обычно задает модель расчета освещения отдельно взятой точки Например, в модели Гуро, используемой видеокартами поколения DirectX 7, и представляющей из себя расчет освещенности сначала в вершинах треугольника и в зависимости от этих значений рассчет уже для всей грани, практически невозможно было задать «металлический» компонент освещения в формуле Фонга, определяющей количество света, отраженного в сторону наблюдателя, потому что возникающие при этом «блики» на поверхности объекта по размерам существенно меньше полигона [3]. Пиксельные шейдеры сделали возможным освещение любых поверхностей попиксельно, используя запрограммированные разработчиками материалы. Попиксельное освещение иллюстрирует следующий рисунок:


Но, опять же, только освещением дело не ограничивается: с помощью пиксельных шейдеров можно также автоматически генерировать текстуры, например стилизацию под дерево, или под воду, или блики на дне ручья, отбрасываемые рябью на его поверхности. Причем - текстуры, изменяющиеся во времени и не теряющие детализации даже при приближении к ним. Мультитекстурирование иллюстрирует следующий рисунок:


Или можно создавать оптические эффекты - дрожащий воздух, неровное стекло... вариантов много. Правда, пиксельный шейдер не столько вычисляет, сколько изменяет некий предварительно вычисленный стандартными способами цвет, поэтому даже при отсутствии поддержки пиксельных шейдеров акселератор все-таки сможет что-то изобразить. Пиксельные шейдеры позволяют получить реальное освещение и точные тени. У программистов появилась возвожность работать с микрополигонами, что позволяет создавать реалистичные эффекты взрыва, дождя, пыли, дыма. Помимо арифметических инструкций присутствуют и специализированные «текстурные», осуществляющие выборки цвета и арифметические вычисления сданными текстур. Различия между версиями пиксельных шейдеров заключаются в следующем. Пиксельный шейдер версии 1.0 - не более восьми арифметических инструкций и не более четырех текстурных. Шейдер 1.4 - это те же восемь арифметических, но уже шесть текстурных инструкций. Никаких условных переходов нет. Впрочем, для создания правдоподобных металлических или, скажем, неровных поверхностей этого вполне достаточно. Главная особенность шейдеров второй версии - поддержка чисел с плавающей точкой. В задачах освещения это очень важно: динамического диапазона стандартного 8-битного цвета для передачи всего богатства оттенков может и не хватить. В пиксельные шейдеры третьей версии включена поддержка условных переходов для задания формул освещения - функция почти бесполезная, но позволяющая в некоторых случаях оптимизировать производительность шейдера (например, не проводить вычислений над заведомо бесперспективными пикселями) [3]. Первый рисунок илюстрирует полное отсутствие шейдеров, второй илюстрирует работу шейдеров версии 1.4, а третий илюстрирует работу шейдеров версии 2.0:


Пиксельные шейдеры обеспечивают невероятную степень детализации поверхностей, позволяя вам наслаждаться эффектами, выходящими за уровень треугольников, дают художникам и разработчикам возможность создавать попиксельные эффекты, отражающие их реальное видение. А также гарантируют разработчикам беспрецедентный контроль освещения, закраски и цвета каждого отдельного пикселя, позволяя создавать уникальные эффекты поверхностей.

Шейдерные языки

Впервые использованные в системе RenderMan компании Pixar, шейдеры получали всё большее распространение со снижением цен на компьютеры. Основное преимущество от использования шейдеров — их гибкость, упрощающая и удешевляющая цикл разработки программы, и при том повышающая сложность и реалистичность визуализируемых сцен. Шейдерные языки обычно содержат специальные типы данных, такие как матрицы, семплеры, векторы, а также набор встроенных переменных и констант для удобной интеграции со стандартной функциональностью 3D API Поскольку компьютерная графика имеет множество сфер приложения, для удовлетворения различных потребностей рынка было создано большое количество шейдерных языков. [1]

Данные шейдерные языки ориентированы на достижение максимального качества визуализации. Описание свойств материалов сделано на максимально абстрактном уровне, для работы не требуется особых навыков программирования или знания аппаратной части. Такие шейдеры обычно создаются художниками с целью обеспечить «правильный вид», подобно наложению текстуры, источников света и другим аспектам их работы.
Обработка таких шейдеров обычно представляет собой ресурсоёмкую задачу. Совокупная вычислительная мощность, необходимая для обеспечения их работы, может быть очень велика, так как используется для создания фотореалистичных изображений. Основная часть вычислений при подобной визуализации выполняется большими компьютерными кластерами.

Шейдерный язык RenderMan, описанный в Спецификации интерфейса RenderMan, является фактическим стандартом для профессионального рендеринга. API RenderMan, разработанный Робом Куком, используется во всех работах студии Pixar. Он также является первым из реализованных шейдерных языков. [1]

NVIDIA Gelato представляет собой оригинальную гибридную систему рендеринга изображений и анимации трехмерных сцен и объектов, использующую для расчетов центральные процессоры и аппаратные возможности профессиональных видеокарт серии Quadro FX. Основополагающим принципом, которого неукоснительно придерживаются разработчики, является бескомпромиссное качество финального изображения, не ограниченное ничем, в том числе — современными возможностями видеокарт. Как производственный инструмент, способный создавать конечный продукт высокого качества, Gelato предназначен для профессионального использования в таких областях как кино, телевидение, промышленный дизайн и архитектурные визуализации. [1]

Шейдерный язык OpenGL носит название GLSL (The OpenGL Shading Language). GLSL основан на языке ANSI C. Большинство возможностей языка ANSI C сохранено, к ним добавлены векторные и матричные типы данных, часто применяющиеся при работе с трехмерной графикой. В контексте GLSL шейдером называется независимо компилируемая единица, написанная на этом языке. Программой называется набор откомпилированных шейдеров, связанных вместе. [1]

Cg разработан nVidia совместно с Microsoft (такой же по сути язык от Microsoft называется HLSL, включён в DirectX 9). Cg расшифровывается как C for Graphics. Язык действительно очень похож на C, он использует схожие типы (int, float, а также специальный 16-битный тип с плавающей запятой — half). Поддерживаются функции и структуры. Язык обладает своеобразными оптимизациями в виде упакованных массивов (packed arrays) — объявления типа «float a[4]» и «float4 a» в нём соответствуют разным типам. Второе объявление и есть упакованный массив, операции с упакованным массивом выполняются быстрее, чем с обычными. Несмотря на то, что язык разработан nVidia, он без проблем работает и с видеокартами ATI. Однако следует учесть, что все шейдерные программы обладают своими особенностями, которые следует получить из специализированных источников. [1]

Низкоуровневый шейдерный язык DirectX (DirectX ASM) по синтаксису сходен с Ассемблером. Существует несколько версий, различающихся по набору команд, а также по требуемому оборудованию. Существует разделение на вершинные (vertex) и пиксельные (pixel) шейдеры, которые различаются. Выполняет обработку геометрии, то есть изменяет параметры вершины, такие, как позицию, текстурные координаты, цвет вершин. Также может выполнять вычисления освещения. Допустимое количество команд может достигать одной-двух сотен. Выполняет обработку цветовых данных, полученных при рисовании треугольника. Оперирует с текстурами и цветом. Количество инструкций значительно ограничено, так, к примеру, в версии 1.4 оно не может быть больше 32. [1]

Высокоуровневый шейдерный язык DirectX (HLSL — High Level Shader Language) является надстройкой над DirectX ASM. По синтаксису сходен с C, позволяет использовать структуры, процедуры и функции. [1]

Как видим существуют две основные спецификации для написания шейдеров бесплатная open-source технология OpenGL и как всегда платное удовольствие от Microsoft в виде DirectX и Direct3D. Чем же отличаются эти две спецификации кроме доступности - ничем. Отличие в обработке. Вы никогда не задумывались почему игры использующие Direct3D просят вас установить DirectX. А скажем тот же Quake 3 достаточно просто установить на свой компьютер и вуаля он работает. Все дело в том что технология OpenGL обращается напрюмую к вашей видеокарте, грубо говоря она в нее заложена, и использует поддерживаемые ей шейдеры. В то время как DirectX может обращаться к видеочипу только через дополнительный драйвер.

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

простой шейдер «Головокружение»


шейдер, имитирующий электрический разряд


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


Cкининг (skinning). Matrix pallete skinning для скелетной анимации персонажей с большим количеством «костей». Примеры вы видите практически во всех играх. Но приведу один скриншот из Call of Duty 2, над вершинами каждого из персонажей поработал алгоритм скининга. Причем, с шейдерами версии 3.0 сделать скининг стало заметно проще, для шейдеров версии 1.1 нужно было писать несколько шейдеров для каждого вида скининга (с определенным количеством «костей»).


Toon shading/Cel shading. Используется в некоторых играх для создания специального эффекта «мультяшного» изображения:


Имитация ткани (Cloth Simulation) - для имитации поведения подобных ткани материалов, которой очень не хватает в большинстве игр. Наиболее просто понять, о чем речь, по такой картинке:


Bump mapping. Normal mapping. С недавних пор применяется практически везде.


Постобработка кадра. Все эти эффекты Bloom, Depth of Field и Motion Blur...



Parallax Mapping/Offset Mapping


High Dynamic Range (HDR)


Bloom


Пример direct illumination против global illumination рендеринга, чтобы развеять оставшиеся сомнения о полезности вторичного освещения (источник света один, ambient освещение отсутствует):


Источники

  1. Википедия - Шейдеры.
  2. Хакер, номер #064
  3. Алексей Берилло. Современная терминология 3D графики
  4. Основы шейдеров. Статья на информационном портале porti.ru