{"id":3824,"url":"\/distributions\/3824\/click?bit=1&hash=a0d33ab5520cacbcd921c07a49fc8ac5b78623b57936b992ce15c804b99210d4","title":"\u041a\u0430\u043a\u0443\u044e \u0440\u0435\u043a\u043b\u0430\u043c\u0443 \u043c\u043e\u0436\u043d\u043e \u0434\u0430\u0442\u044c \u043d\u0430 DTF \u0438 \u043a\u0442\u043e \u0435\u0451 \u0443\u0432\u0438\u0434\u0438\u0442","buttonText":"\u0423\u0437\u043d\u0430\u0442\u044c","imageUuid":"75ec9ef4-cad0-549d-bbed-1482dc44e8ee","isPaidAndBannersEnabled":false}
Gamedev
Yegor Zhumikov

Шейдеры. Что и как

Расскажу, как в общем случае они работают, что умеют и как их используют

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

Слово «шейдер» в контексте разработки игр очень популярно, слышать его могли и те, кто игры не делает. Само слово изначально появилось от англ. shading (затенение) — первые шейдеры использовались, чтобы передавать глубину с помощью работы со светом, блеском, тенями и прочим. Со временем шейдеры стали использоваться для совершенно разного вида постобработки и вообще отрисовки примерно всего.

Говоря общими словами, шейдер — это просто программа для графической карты. То есть то, что пишется школьниками на паскале (хипстерами на пайтоне) — это программы для вашего центрального процессора (CPU), а шейдеры — для графического (GPU). Особенность же этих программ выходит из особенностей GPU — они работают параллельно на сотнях маленьких ядех вместо нескольких больших, преимущественно осуществляя математические операции.

GPU-ядрышки резвятся на фоне серьезного CPU

Теперь разберемся, как это все работает.

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

Кубик до и после графичекой карты

Процесс отрисовки

Процесс отрисовки, если его достаточно упростить (что я и сделаю в рамках этой статьи), можно поделить на несколько шагов:

1. Получение входных данных из памяти.
2. Выполнение шейдера вершин.
3. Растеризация.
4. Выполнение шейдера пикселей (фрагментов).
5. Проведение тестов «глубины».
6. Отрисовка на текстуру для экрана.

В первом шаге видеокарта каким-то образом получает данные (вершины, плоскости, текстуры) в свою видеопамять, для нас это сейчас не так важно. Далее происходит конвертация координат относительно объекта в координаты на экране относительно камеры. После происходит растеризация — высчитывается, в каких пикселях уже на экране находится объект. Такие пиксели называют фрагментами. Отличие от пикселей заключается в том, что фрагмент помимо информации о пикселе, содержит еще и некоторую побочную информацию, полученную после растеризации. Для упрощения будем считать, что это все просто пиксели на экране. Далее для каждого пикселя выполняется шейдер фрагмента. А затем проверяется, что расстояние от камеры до фрагмента соответствует высчитанному заранее в нужном направлении в буфере глубины. Проще говоря, проверяется, нет ли перед объектом чего-либо еще, и нужно ли его отрисовывать на итоговое изображение.

Пример буфера глубины

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

1. Шейдер вершин.
2. Шейдер фрагментов.

Шейдер вершин

Как сказано было ранее, этот шейдер (или группа шейдеров по очереди) занимается переводом координат относительно объекта, в координаты на текстуре.

Трансформации вершин.

На картинке начало координат немного не соответствует реальным, что все так же не влияет на понимание процесса :)

Пройдемся по состояниям. В первом у нас, очевидно, входные координаты без излишков. На втором они были перенесены в координаты относительно начала «мира». Потом они переносятся в координаты относительно точки смотрящего (видно на второй картинке), но заметно, что картинка плоская. Их проекция происходит далее и мы получаем наши итоговые координаты. Все эти операции производятся шейдером. Помимо прочего, он позволяет не только отобразить реальные координаты, но и модифицировать их так, чтобы исказить объект для эффекта. Например, я недавно писал шейдер, который переворачивал спрайт, чтобы отрисовать его тень:

Тень!

Из занимательного — итоговые вершины располагаются на так называемой плоскости Clip Space и находятся в диапазоне от -1.0 до 1.0. Именно с такими координатами потом и работает видеокарта.

Шейдер фрагментов

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

Куб!

Выглядит немного странно, да? Проблема в том, что мы не видим ни теней, ни текстур. Будь на кубе какая-либо текстура, мы бы заметили переход между гранями. Вот возьмем текстуру:

Знакомое лицо

Теперь достаточно в каждом пикселе просто брать цвет из текстуры. Но чтобы это сделать, нужно добавить для каждой точки куба еще информацию: UV канал. Это координат вида (u, v). Отсюда и название, так как x и y были заняты. Она присваивается вершине объекта и обозначает точку на текстуре, которая ей соответствует. Чтобы было понятнее, если мы хотим на каждую грань куба нарисовать знакомое нам лицо, то UV координаты для каждой грани будут выглядеть предельно просто:

Знакомое лицо (20

Модифицировать их никак не надо. Более того, считать координаты для каждой точки — тоже не нужно. Этим занимается GPU, самостоятельно интерполируя точки из вершин. Тогда достаточно просто сказать для каждой точки что-то вроде

color = texture_color(cube_texture, data.uv);

Это очень условный пример, но примерно так в простейшем случае оно и работает:

Как же он хорош

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

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

В общем-то, на этом все :)

Куб свиборга между резвящимися GPU-ядрышками
0
25 комментариев
Написать комментарий...
JustIn
фрагмент может на самом деле чуть-чуть залезать на соседние пиксели

Эта фраза вынесла мозг даже мне.

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

Не смешивайте эти 2 понятия пожалуйста.

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

Начал было возмущенно писать, но вы чуть ниже исправились и упомянули что все же Clip Space ;)

Этим занимается графическая библиотека и движок, самостоятельно интерполируя точки из вершин.

Стоило вас похвалить - и на тебе. Какая библиотека? Какой движок?  Этим занимается texture sampler прямо на GPU.

пиксельном шейдере можно получить информацию о тенях

Лучше бы вы это не писали, а то большая часть народу подумает что есть некая встроенная функция просчета теней на GPU :)

А вообще инфа ну просто капец как поверхностно подана - те кто не в теме все равно ничего не поймут, а те кто в теме будут бугуртить (как это делаю я, ага )) )

Ответить
Развернуть ветку
Yegor Zhumikov
Автор

Спасибо за содержательную критику. Я постарался исправить статью, чтобы чуть она была более технически точной. Про интерполяцию UV — я хотел объяснить, что это делается «за шторой», но немного перестарался :)

Те, кто не в теме, надеюсь все-таки что-то поймут 

Ответить
Развернуть ветку
Желающий мангал
А вообще инфа ну просто капец как поверхностно подана - те кто не в теме все равно ничего не поймут, а те кто в теме будут бугуртить (как это делаю я, ага )) )

В целом похожие ощущения, но там есть волшебное слово "научпоп", и я уже вроде как не первый раз вижу, что это слово специально используется как щит от технических претензий по терминологии/специфике)

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

Ответить
Развернуть ветку
JustIn

Ну ок, научпоп, но написать что "UV интерполируются движком" - это было уже за гранью добра и зла, и я не мог не бомбануть, извините.

Ответить
Развернуть ветку
Yegor Zhumikov
Автор

Да в общем-то не за что извиняться. Все вполне по делу, я  только рад поправить материал, чтобы от него меньше бомбили  и он был полезнее

Ответить
Развернуть ветку
Cassini D

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

Ответить
Развернуть ветку
Boris Kolobov

Милейший, а где Вы изучали вышеизложенную теорию?

Ответить
Развернуть ветку
Желающий мангал
Слово «шейдер» в контексте разработки игр очень популярно, слышать его могли и те, кто игры не делает.

1) Покупаешь диск "Принц Персии: Пески времени" в 2003 году в ларьке.
2) Читаешь сзади в требованиях, что нужна видеокарта с поддержкой пиксельных шейдеров.
3) Молишься всем богам.

Ответить
Развернуть ветку
Игродел

Именно для песков времени пришлось купить Geforce FX с shader model 2.0.

Ответить
Развернуть ветку
Вчерашний вентилятор

Комментарий недоступен

Ответить
Развернуть ветку
Yegor Zhumikov
Автор

Блин, совсем забыл о нем. Надо было куда-то пририсовать

Ответить
Развернуть ветку
Kirill Sagorov

не забудь ивана - космического байкера.

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку

Комментарий удален модератором

Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
NopeHope

Наслаждайтесь:

Ответить
Развернуть ветку
Eugene S

То есть это не классические
INPUT a, b, c
IF a>b THEN…

?
Я просто далек от программирования, а для общего развития не помешало бы

Ответить
Развернуть ветку
NopeHope

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

В шейдерах могут использоваться for/if/else. Можете зайти на ShaderToy и посмотреть код сложных шейдеров. Но использование слишком длинных ветвлений (if/else) в шейдерах не рекомендуется. Шейдеры обрабатываются на GPU, поэтому их конек это математические вычисления в большом кол-ве. То есть, если нужно a + b тысячу раз вычислить то это по адресу. А вот сравнения и проверки лучше оставить CPU считать.

В шейдерах обычно гораздо больше линейного кода. Код шейдера это в основном куча математических операций и функций, которые перебрасываются из одной переменной/функции в другую. Чаще всего перебрасывают либо цветовую информацию (RGBA каналы), либо координаты текстур (UV). Отдельным делом идут вертексы с матрицами и их системами координат (object/world/view/tangent/clip space).  

Ответить
Развернуть ветку
Eugene S

Понятно, спасибо.

Ответить
Развернуть ветку
Yegor Zhumikov
Автор

Это никак не поможет пониманию, как мне кажется. На каком языке? Может шейдерграф? Объяснять каждую строчку/ноду? А смысл? Пошагово все и так расписано. Для более точных изысканий нужен пост с более глубоким объяснением для конкретной вариации

Ответить
Развернуть ветку
Желающий мангал

Можно подробнее раскрыть про "общий случай", что описанные два шейдера – минимально необходимые в современном графическом конвеере, но на самом деле их больше и ещё они могут это и это (тесселяция, генерация вершин, и т.д.)

Ответить
Развернуть ветку
Yegor Zhumikov
Автор

Я честно говоря не был уверен, насколько глубоко копать, решил остановиться на том, что есть по итогу. Не очень понятно насколько это нужно ЦА поста и насколько интересно

Ответить
Развернуть ветку
JustIn

Я бы рекомендовал перед тем как что-то разъяснять другим, сначала разобраться самому.

Ведь нет ничего хуже, чем неправильно донесенная инфа (а у вас получился какой-то сломанный телефон) уж простите за прямоту.

Ответить
Развернуть ветку
Аккаунт удален

Комментарий недоступен

Ответить
Развернуть ветку
wolik

У тебя всё равно просто не получилось.

Ответить
Развернуть ветку

Комментарий удален модератором

Развернуть ветку
habbahen

не хватает ссылки на пример кода программы на гитхабе.

Ответить
Развернуть ветку
xxx xxx

Смысл такой статьи в разделе Gamedev? Если б на форуме для домохозяек, то прокатило., а тут и так все знают, что они есть и где-то в GPU крутятся.  

Ответить
Развернуть ветку
Читать все 25 комментариев
null