Майнкрафт шейдеры что это
Майнкрафт шейдеры что это
Что такое шейдеры в Minecraft?
Шейдеры Minecraft 1.4.2 изменят ваш уютный кубический мир до неузнаваемости и дадут взглянуть на него по-новому. Они полностью изменят графику Minecraft, сделав ее более реалистичной, добавят много интересных эффектов, таких как реалистичные тени, отражение от воды, движение травы и листвы на деревьях, и так далее.
Чем шейдеры Minecraft отличаются от текстур-паков?
В отличие от текстур-паков, шейдеры не изменяют текстуры, а накладывают эффект при обрисовке того или иного игрового объекта. Поэтому шейдеры можно использовать практически с любым набором текстур.
Единственный минус шейдеров Minecraft, так это то, что тянут их без тормозов только действительно мощные компьютеры. Но красота, как говорится, требует жертв.
Какие шейдеры Minecraft ставить?
Несмотря на всю абсурдность вопроса, он с каждым днем становится все более актуальным, ведь постоянно появляются новые шейдеры, новые настройки и т.д. А игроки просто-напросто путаются в этом разнообразии. Мы рассмотрим все виды шейдеров, актуальных на данный момент, а также приведем скриншоты для сравнения.
Итак, расставим все точки над i. Изначально, автором шейдеров для Minecraft являлся daxnitro, который в общем то и придумал методы их подгрузки в в код, отвечающий за прорисовку блоков. Мод получил название GLSL Shaders. Сами же шейдеры могут писаться отдельно, что и дало такой всплеск модификаций изначального кода. Скачать Шейдеры для Minecraft вы сможете скоро, они появяться в новой рубрике «Шейдеры для Minecraft»
Шейдеры. Что и как
Расскажу, как в общем случае они работают, что умеют и как их используют
Сразу оговорюсь, что материал рассчитан на тех, кто никогда не работал с шейдерами или вообще не знаком с разработкой игр, то есть это в некотором смысле научпоп.
Слово «шейдер» в контексте разработки игр очень популярно, слышать его могли и те, кто игры не делает. Само слово изначально появилось от англ. shading (затенение) — первые шейдеры использовались, чтобы передавать глубину с помощью работы со светом, блеском, тенями и прочим. Со временем шейдеры стали использоваться для совершенно разного вида постобработки и вообще отрисовки примерно всего.
Говоря общими словами, шейдер — это просто программа для графической карты. То есть то, что пишется школьниками на паскале (хипстерами на пайтоне) — это программы для вашего центрального процессора (CPU), а шейдеры — для графического (GPU). Особенность же этих программ выходит из особенностей GPU — они работают параллельно на сотнях маленьких ядех вместо нескольких больших, преимущественно осуществляя математические операции.
Теперь разберемся, как это все работает.
В общем случае цель шейдера — отрисовать некоторый объект. Поэтому возьмем куб, распишем процесс его отрисовки и посмотрим, где используются шейдеры и зачем. Сначала опишем сам куб. Для графической карты это 8 точек, между некоторыми из которых есть плоскость. Каждая из точек описывается тремя числами (правильно сказать, что это вершины). Помимо этого у кубика есть цвет и положение внутри мира.
Процесс отрисовки, если его достаточно упростить (что я и сделаю в рамках этой статьи), можно поделить на несколько шагов:
1. Получение входных данных из памяти.
2. Выполнение шейдера вершин.
3. Растеризация.
4. Выполнение шейдера пикселей (фрагментов).
5. Проведение тестов «глубины».
6. Отрисовка на текстуру для экрана.
В первом шаге видеокарта каким-то образом получает данные (вершины, плоскости, текстуры) в свою видеопамять, для нас это сейчас не так важно. Далее происходит конвертация координат относительно объекта в координаты на экране относительно камеры. После происходит растеризация — высчитывается, в каких пикселях уже на экране находится объект. Такие пиксели называют фрагментами. Отличие от пикселей заключается в том, что фрагмент помимо информации о пикселе, содержит еще и некоторую побочную информацию, полученную после растеризации. Для упрощения будем считать, что это все просто пиксели на экране. Далее для каждого пикселя выполняется шейдер фрагмента. А затем проверяется, что расстояние от камеры до фрагмента соответствует высчитанному заранее в нужном направлении в буфере глубины. Проще говоря, проверяется, нет ли перед объектом чего-либо еще, и нужно ли его отрисовывать на итоговое изображение.
Как видите, в процессе отрисовки можно заметить два вида шейдера. На самом деле, сейчас есть чуть больше видов, но они не столь важны для разбора, так как имеют более специфичный характер использования, а мы рассказываем на пальцах. Но вот те два, что нас волнуют:
1. Шейдер вершин.
2. Шейдер фрагментов.
Как сказано было ранее, этот шейдер (или группа шейдеров по очереди) занимается переводом координат относительно объекта, в координаты на текстуре.
На картинке начало координат немного не соответствует реальным, что все так же не влияет на понимание процесса 🙂
Пройдемся по состояниям. В первом у нас, очевидно, входные координаты без излишков. На втором они были перенесены в координаты относительно начала «мира». Потом они переносятся в координаты относительно точки смотрящего (видно на второй картинке), но заметно, что картинка плоская. Их проекция происходит далее и мы получаем наши итоговые координаты. Все эти операции производятся шейдером. Помимо прочего, он позволяет не только отобразить реальные координаты, но и модифицировать их так, чтобы исказить объект для эффекта. Например, я недавно писал шейдер, который переворачивал спрайт, чтобы отрисовать его тень:
После преобразований вершин и растеризации нужно высчитать цвет каждого фрагмента (помним, что для упрощения это пиксели). Для примера возьмём наш куб: мы помним, что он залит одним цветом. Просто сделаем так, чтобы цвет каждого фрагмента стал цвета куба и все:
Выглядит немного странно, да? Проблема в том, что мы не видим ни теней, ни текстур. Будь на кубе какая-либо текстура, мы бы заметили переход между гранями. Вот возьмем текстуру:
Теперь достаточно в каждом пикселе просто брать цвет из текстуры. Но чтобы это сделать, нужно добавить для каждой точки куба еще информацию: UV канал. Это координат вида (u, v). Отсюда и название, так как x и y были заняты. Она присваивается вершине объекта и обозначает точку на текстуре, которая ей соответствует. Чтобы было понятнее, если мы хотим на каждую грань куба нарисовать знакомое нам лицо, то UV координаты для каждой грани будут выглядеть предельно просто:
Модифицировать их никак не надо. Более того, считать координаты для каждой точки — тоже не нужно. Этим занимается GPU, самостоятельно интерполируя точки из вершин. Тогда достаточно просто сказать для каждой точки что-то вроде
Это очень условный пример, но примерно так в простейшем случае оно и работает:
Помимо натягивания текстур в пиксельном шейдере можно, например, получить информацию об освещенности и добавить к цвету черного пропорционально затемнению в этой точке, тогда объект будет менее плоским. Это, конечно, если просчет освещенности где-то написан, потому что сама по себе видеокарта о понятиях освещения и теней мало чего знает. Вообще делать с цветами можно что угодно, например подменять их в зависимости от игрока:
Помимо UV канала в шейдер в зависимости от его вида приходят разные данные. Они зависят от игрового движка и графической библиотеки. Обычно туда входят данные о местоположении точки, нормаль (вектор исходящий от поверхности в точке), UV канал. Но также шейдер вершин может передавать данные в пиксельный шейдер. Если это координата, то она будет проинтеполирована на основе положения фрагмента относительно вершин, между которыми он находится, как, например, UV данные.
Майнкрафт — моды и шейдеры, какой сделать выбор? Как установить?
как установить шейдеры в майнкрафте
Майнкрафт — моды и шейдеры
В Minecraft в базовой версии вполне достаточно играть сотни часов. И все-таки вам предоставляется возможность значительно изменить образ игры, используя моды и шейдеры.
Какая разница между этими двумя явлениями?
Моды для Minecraft — это как правило моды, которые способны в буквальном смысле слова влиять на каждый аспект игрового процесса. Моды дают возможность вам изменять внешний вид мира, персонажей (не только основного героя), создавать программируемых дроидов, вводить новую еду, предметы либо животных для игры … А вообще, каждый мод индивидуален, хотя базируется на базовой игре — геймплей. Возможность изменять игру по собственному усмотрению — один из факторов, делающие эту игру как и прежде приятной, не взирая, что с момента ее премьеры прошло намного более десяти лет. Моды можно скачивать поодиночке либо целыми пакетами, другими словами модпаками.
Шейдеры для Майнкрафт — это специальные пакеты, которые визуально меняют игру — они добавляют тени, сглаживают края объектов и персонажей, меняют освещение, они в том числе могут сделать солнышко и луну полностью круглыми.
Что чрезвычайно важно — моды должны нормально работать с шейдерами.
Майнкрафт — как монтировать моды?
как установить шейдеры на майнкрафт лицензию
После загрузки два раза щелкните по нему левой кнопкой мыши, для того, чтобы установить. Когда закончите, запустите игру и нажмите «Установить». Укажите на еще один вариант. Выберите установленную версию Forge. После выбора нажмите «Создать».
В настоящее время на главном экране программы запуска выберите Forge из раскрывающегося списка слева от кнопки PLAY.
В дополнение к средству для достижении цели модов вам, очевидно, понадобятся сами моды. В сети есть десятки веб-сайтов с их рейтингами, заявлениями и архивами. Каждый из них в большинстве случаев точно описывается и имеет снимок экрана, на котором показано, как игра будет выглядеть, когда вы ее запустите. Загрузите файл модема, после этого
Примечание — в случае если папки с модами нет, вам предоставляется возможность создать ее собственными силами.
Что необходимо подразумевать — некоторое количество модов, брошенных в эту папку, могут вызвать сбои в работе игры, поэтому не переусердствуйте!
Вам предоставляется возможность проверить, какие вы имеете моды Minecraft, на главном экране игры, где располагается опция Mods — их список располагается слева и включает элементы, относящиеся к Forge и самим модам. Вам предоставляется возможность просмотреть более детальную информацию о каждом элементе из списка, щелкнув по нему левой кнопкой мыши.
Как установить шейдеры Minecraft.
майнкрафт как установить шейдеры
А затем что? Выберите Optifine и после этого запустите игру как всегда. На главном экране нажмите: Параметры, Настройки графики, Шейдеры. После этого вы увидите все, что было помещено в папку с игрой. Нажав на абсолютно любой из них, вам предоставляется возможность установить его параметры, такие как качество тени, глубину руки и т. д.
Для того, чтобы шейдеры возникли в нашем списке, вам нужно в первую очередь загрузить их, а после этого сделать это же, что и при помещении модов в каталог игры, но будет гораздо лучше, если вместо того, чтобы в папку модов, вы помещаете их в папку shaderpacks.
Так, например, вот как выглядит игра Майнкрафт без скачивания в отсутствие шейдеров и как она меняется при их включении.
Стоит иметь в виду, что периодически появляются ранее не известные моды и шейдеры, поэтому вам предоставляется возможность менять их в лучшую сторону и интереснее.
Как установить шейдеры Minecraft и значительно улучшить графику.
шредеры для улучшения графики
Minecraft имеет свой фирменный квадратный стиль, завоевавший сердца миллионов игроков, но это уже не значит, что его нельзя немного изменить. Не без помощи шейдеров нам предоставляется возможность добавлять эффектные эффекты, тени, освещение и прочие украшения, благодаря которым игра выглядит феноменально.
Шейдеры несут ответственность за все дополнительные эффекты при деле. Благодаря им мы не только лишь представим новое освещение и тени, но также заставим блоки травы двигаться как на ветру, вода будет отражать свет, а солнышко будет сиять нашими глазами прекрасными лучами. Совместно с квадратным миром Minecraft это может дать ни с чем не сравнимый эффект.
И все-таки, перед тем как мы решим внедрить такие эффекты в нашу игру, помните, что они довольно требовательны и вам нужен вполне достаточно могучий компьютер с эффективной видеокартой. Существует помимо прочего некоторое количество способов установки шейдеров исходя из того, используете ли вы менеджер модов MultiMC и установлен ли у вас Forge.
Для целей руководства мы покажем стандартную установку как для чистой немодифицированной версии игры, так и для Minecraft с установленной Forge. В обоих случаях мы все станем использовать Minecraft версии 1.7.10. Помимо прочего нам потребуется мод Optifine для монтажа шейдеров (он расширяет настройки графики и повышает производительность игры).
Монтаж шейдеров на чистый Майнкрафт 1.7.10 в отсутствие Forge
1. Зайдите на сайт Optifine и скачайте мод в редакции HD Ultra, версия 1.7.10 HD U A4.
3. Перейдите в официальную тему Shaders Mod на официальном форуме Minecraft. Shaders Mod — это исходный мод, который сам собой не имеет графических эффектов, но дает возможность вам активировать те, которые мы загрузим вслед за тем. Он доступен для Minecraft с Forge (Forge Edition) и в отсутствие Forge (Non-Forge Edition).
Скачать мод Shaders для Майнкрафт 1.7.10
Оттого, что мы устанавливаем шейдеры в чистую версию игры в отсутствие Forge, мы загружаем мод GLSL Shaders Mod v2.3.29 Non-Forge Edition для Minecraft 1.7.10.
Монтаж ShadersMod
5. У нас есть OptiFine и Shaders Moda — в настоящее время пора установить т.н. шейдерпака. Их можно отыскать много, и полный перечень можно отыскать в такой же теме, из которой мы загрузили Shaders Moda.
Скачиваем один из доступных шейдерных пакетов (применительно ко мне это будет 1-й из них, другими словами SEUS — Sonic Ether’s Unbelievable Shaders). Мы получим ZIP-файл.
6. Запустите официальный лаунчер Minecraft — в левом нижнем углу вы посчитаете список игровых профилей. Выберите профиль 1.7.10-OptiFine-ShadersMod из списка и включите нужную игру.
7. Перейдите в параметры игры, а после этого на вкладку Шейдеры. Пока список будет пустым. Внизу вы посчитаете кнопку «Открыть папку шейдерпаков» — щелкните по ней.
8. На экране будет замечен пустая директория, в которую нам нужно распаковать шейдеры. Для каждого загруженного пакета шейдеров мы создаем отдельную подпапку. Применительно ко мне я создам подкаталог с именем SEUS, поскольку я загружал такие шейдеры.
Создайте подпапку для выбранного шейдерпака.
Откройте заархивированный файл с загруженными шейдерами и перетащите каталог «Shaders», содержащийся в архиве, в нашу папку SEUS (или другую). После распаковки шейдеры должны оказаться в дороге «/ shaderpacks / SEUS / shaders».
9. Мы вернулись в игру, и в настоящее время SEUS будет отображаться в нашем списке доступных шейдеров. Щелкните по нему в нашем списке, и он готов — графика в Minecraft сразу изменится. Можем закрыть меню и полюбоваться новыми графическими эффектами.
Монтаж очень похож на приведенный выше случай, за исключением некоторых составных частей, связанных с самими файлами OptiFine и Shaders Moda.
1. Перейдите на официальный сайт OptiFine и загрузите версию HD Ultra — OptiFine 1.7.10 Ultra A4.
2. Зайдите в официальную тему Shaders Mod и скачайте версию мода «Forge Edition» для Minecraft 1.7.10 (или любого другого, что у нас есть).
Ручной метод
Менеджер модов MultiMC
4. Из темы Shader Mods на форуме Minecraft загрузите пакет шейдеров — вам предоставляется возможность отыскать их по ссылкам на основной мод. Каждый пакет включает в себя разные шейдеры, которые различаются по эффекту и производительности. Для целей руководства мы загрузим пакет SEUS (необычайные шейдеры Sonic Ether). После скачивания мы получим ZIP-файл — пока не распаковываем его.
5. Запустите Minecraft не без помощи Forge и модов или через программное обеспечение запуска, выбрав профиль «Forge», или через MultiMC. В настоящее время перейдите к параметрам — в этом месте вы посчитаете вкладку «Шейдеры». Все остальное точно такое же, как монтаж шейдеров в немодифицированной версии игры, в следствии этого выполните шаги 7–9 из приведенных выше инструкций.
Создание шейдеров
Освоить создание графических шейдеров — это значит взять под свой контроль всю мощь видепроцессора с его тысячами параллельно работающих ядер. При таком способе программирования требуется другой образ мышления, но раскрытие его потенциала стоит потраченных усилий.
Практически в любой современной графической симуляции используется код, написанный для видеопроцессора: от реалистичных эффектов освещения в высокотехнологичных AAA-играх до двухмерных эффектов постпроцессинга и симуляции жидкостей.
Сцена из Minecraft, до и после добавления нескольких шейдеров.
Задача этого туториала
Иногда программирование шейдеров представляется загадочной чёрной магией и его часто понимают неправильно. Существует множество примеров кода, демонстрирующих создание невероятных эффектов, но в которых практически нет объяснений. В своём руководстве я хочу восполнить этот пробел. Я сосредоточусь в основном на базовых принципах создания и понимания кода шейдеров, чтобы вы смогли с лёгкостью настраивать, сочетать или писать свои собственные шейдеры с нуля!
Это общее руководство, поэтому всё изученное в нём можно применять в любой технологии, использующей шейдеры.
Что же такое шейдер?
Шейдер — это просто программа, выполняемая в графическом конвейере. Она сообщает компьютеру, как рендерить каждый пиксель. Эти программы называются шейдерами («затенителями»), потому что их часто используют для управления эффектами освещения и затенения, но ничего не мешает использовать их и для других спецэффектов.
Шейдеры пишут на специальном языке шейдеров. Не волнуйтесь, вам не придётся изучать совершенно новый язык: мы будем использовать GLSL (OpenGL Shading Language), который похож на C. (Существует несколько языков написания шейдеров для разных платформ, но поскольку все они адаптированы под выполнение в видеопроцессоре, то похожи друг на друга.)
Примечание: эта статья посвящена исключительно фрагментным шейдерам (fragment shader). Если вам любопытно, какие ещё виды шейдеров бывают, то можно почитать о различных этапах графического конвейера в OpenGL Wiki.
Приступаем!
В этом туториале мы будем использовать ShaderToy. Он позволяет нам начать программировать шейдеры прямо в браузере, без возни с установкой и настройкой! (Для рендеринга он использует WebGL, поэтому требуется браузер с поддержкой этой технологии.) Создавать учётную запись не обязательно, но удобно для сохранения кода.
Примечание: на момент написания статьи ShaderToy находился в состоянии беты [прим. пер.: статья написана в 2015 году]. Некоторые детали интерфейса/синтаксиса могут немного отличаться.
Нажав на New Shader, вы увидите что-то вроде этого:
Если вы не зарегистрировались, интерфейс может немного отличаться.
Маленькая чёрная стрелка внизу служит для компиляции кода.
Что происходит?
Я собираюсь описать то, как работают шейдеры, одним предложением. Приготовились? Вот оно!
Именно это, и только это может делать шейдер. Функция выполняется для каждого пикселя на экране. Она возвращает эти четыре цветовых значения, которые становятся цветом пикселя. Это то, что называется пиксельным шейдером (иногда его называют фрагментным шейдером).
Поздравляю! Вот ваш первый готовый шейдер!
Задача: Попробуйте изменить цвет на сплошной серый.
vec4 — это просто тип данных, поэтому мы могли бы объявить цвет как переменную, вот так:
Выглядит не очень вдохновляюще. У нас есть мощь, способная выполнять код для сотен тысяч пикселей одновременно, а мы закрашиваем их одним цветом.
Давайте попробуем отрендерить на экране градиент. Мы не сможем добиться многого, не зная пары вещей о пикселях, на которые мы влияем, например, об их положении на экране…
Входные данные шейдера
Вы заметили проблему в приведённом выше коде? Попробуйте нажать на кнопку go fullscreen в правом нижнем углу окна предварительного просмотра.
Размеры красной части экрана будут разными и зависят от размера экрана. Чтобы закрасить красным ровно половину экрана, нам нужно знать его размер. Размер экрана не является встроенной переменной, как позиция пикселя, потому что обычно он выбирается программистом, создающим приложение. В нашем случае размер экрана устанавливают разработчики ShaderToy.
Если что-то не является встроенной переменной, то можно отправить эту информацию из центрального процессора (из основной программы) в видеопроцессор (в ваш шейдер). ShaderToy берёт эту задачу на себя. Все передаваемые шейдеру переменные указаны во вкладке Shader Inputs. Переменные, передаваемые таким образом из ЦП в видеопроцессор, называются в GLSL uniform (глобальными).
Давайте изменим код, чтобы правильно определять центр экрана. Нам нужно использовать входные данные шейдера iResolution :
Если попробовать теперь изменить размер окна предварительного просмотра, то цвета должны точно разделить экран пополам.
От разделения к градиенту
Задача: Можете превратить эту картинку в вертикальный градиент? А как насчёт диагонального? Как насчёт градиента из нескольких цветов?
Отрисовка изображений
Экспериментировать с цветами интересно, но если мы хотим сделать что-то впечатляющее, то наш шейдер должен научиться получать в качестве входных данных изображение и изменять его. Таким образом можно получить шейдер, воздействующий на весь игровой экран (например, «подводный» эффект или цветокоррекцию), или воздействовать нужным способом только на определённые объекты (например, для создания реалистичной системы освещения).
Если бы мы программировали на обычной платформе, то нужно было бы передавать видеопроцессору изображение (или текстуру) как uniform, точно так же, как мы бы передавали разрешение экрана. ShaderToy позаботится об этом за нас. В нижней части экрана есть четыре входных канала:
Четыре входных канала ShaderToy
Нажмите на iChannel0 и выберите любую текстуру (изображение).
Если мы можем только возвращать цвет, то как можно нарисовать текстуру на экране? Нам нужно каким-то образом привязать текущий пиксель шейдера к соответствующему пикселю текстуры:
В зависимости от расположения (0,0) на экране может потребоваться отразить ось Y для правильной привязки текстуры. На момент написания статьи ShaderToy был обновлён и исходная точка находится в левом верхнем углу, так что ничего отражать не требуется.
Вы можете привязывать координаты к экрану как вам угодно. Можно отрисовать всю текстуру на четверти экрана (пропуская пиксели, то есть уменьшив её масштаб) или просто отрисовать часть текстуры.
Мы хотим только увидеть изображение, поэтому пиксели будут совпадать в масштабе 1:1:
Так мы получили своё первое изображение!
Теперь, научившись правильно получать данные из текстуры, мы можем делать с ними всё, что угодно! Мы можем растягивать или масштабировать их, экспериментировать с цветами.
Давайте попробуем наложить на изображение градиент, похожий на то, что мы делали раньше:
Поздравляю, вы только что написали свой первый эффект постпроцессинга!
Задача: Сможете написать шейдер, делающий изображение чёрно-белым?
Учтите, что хотя мы и выбрали статичное изображение, всё, что вы видите, выполняется в реальном времени. Чтобы убедиться в этом, замените статичное изображение на видео: нажмите на iChannel0 и выберите любое видео.
Добавляем движение
Пока что все наши эффекты были статичными. Мы можем делать гораздо более интересные вещи, воспользовавшись входными данными, предоставляемыми ShaderToy. iGlobalTime — это постоянно увеличивающаяся переменная. Мы можем использовать её как начальное число для создания периодических эффектов. Давайте попробуем немного поиграть с цветами:
Задача: Можете ли вы сделать шейдер, меняющий изображение с чёрно-белого на цветное и обратно?
Примечание об отладке шейдеров
При отладке кода можно выполнять его пошагово или выводить на печать значения, но при написании шейдеров это невозможно. Можете поискать инструменты отладки для своей платформы, но в общем случае лучше всего привязать проверяемое значение к какой-то графической информации, которую можно увидеть.
Подводим итог
Это только самые основы работы с шейдерами, но освоившись с ними, вы сможете добиться гораздо большего. Посмотрите эффекты ShaderToy и проверьте, сможете ли вы понять или воспроизвести их!
В этом туториале я не упоминал вершинные шейдеры (Vertex Shaders). Они пишутся на том же языке, но выполняются для каждой вершины, а не пикселя, и возвращают вместе с цветом ещё и положение. Вершинные шейдеры обычно проецируют 3D-сцену на экран (эта функция встроена в большинство графических конвейеров). Пиксельные шейдеры обеспечивают большинство наблюдаемых на экране сложных эффектов, поэтому я рассматриваю их.
Ещё одна задача: Сможете ли вы написать шейдер, удаляющий зелёный цвет из видео с ShaderToy и добавляющий другое видео в качестве фона первого видео?
ShaderToy, с которым мы работали ранее, отлично подходит для быстрой проверки и экспериментов, но его возможности довольно ограничены. Например, вы не можете контролировать данные, передаваемые шейдеру. Если у нас будет собственная среда для запуска шейдеров, то мы сможем создавать всевозможные интересные эффекты и использовать их в собственных проектах! Для запуска шейдеров в браузере мы будем использовать в качестве фреймворка Three.js. WebGL — это Javascript API, позволяющий рендерить шейдеры, Three.js нужен только для упрощения работы.
Если вам не интересны JavaScript или веб-платформа, не волнуйтесь, мы не будем вдаваться в особенности веб-рендеринга (но если вы хотите узнать о фреймворке больше, то изучите этот туториал). Настройка шейдеров в браузере — самый быстрый способ начать работу, но освоившись с этим процессом, вы сможете с лёгкостью настраивать и использовать шейдеры на любой платформе.
Настройка
В этом разделе мы рассмотрим локальную настройку шейдеров. Вы можете повторять за мной без необходимости скачивать что-то благодаря этому встроенному виджету CodePen:
Hello Three.js!
Three.js — это JavaScript-фреймворк, который берёт на себя большую часть boilerplate-кода для WebGL, необходимого для рендеринга шейдеров. Проще всего начать с использования версии, выложенной на CDN.
Можете скачать файл HTML, который является простой сценой Threejs.
Сохраните файл на диск и откройте его в браузере. Вы должны увидеть чёрный экран. Пока не очень интересно, так что давайте попробуем добавить куб, просто чтобы проверить, что всё работает.
Для создания куба нам нужно определить его геометрию и материал, а затем добавить его в сцену. Добавьте этот фрагмент кода в поле Add your code here :
Мы не будем подробно рассматривать куб, потому что нам больше интересен шейдер. Но если всё сделано верно, то вы увидите в центре экрана зелёный куб:
Задача: Сможете повращать куб по другой оси? А как насчёт двух осей одновременно?
Итак, всё готово, пора добавить шейдеры!
Добавляем шейдеры
На этом этапе мы можем начать думать о процессе реализации шейдеров. Скорее всего, похожая ситуация будет у вас вне зависимости от платформы: всё настроено, на экране отрисовываются объекты, так как теперь получить доступ к видеопроцессору?
Шаг 1: загрузка кода GLSL
Для создания сцены мы используем JavaScript. В других ситуациях может использоваться C++, Lua или любой другой язык. Вне зависимости от этого шейдеры пишутся на специальном языке шейдеров. Язык шейдеров OpenGL называется GLSL (OpenGL Shading Language). Поскольку мы используем WebGL, основанный на OpenGL, то будем писать на GLSL.
В JavaScript это можно сделать простой передачей кода внутри переменной, вот так:
Это возможно, но мы в этом туториале мы будем писать код шейдеров внутри нового тега
Теперь давайте вставим очень простой шейдер, возвращающий белый цвет.
(Компоненты vec4 в этом случае соответствуют значению RGBA, ка объяснялось в начале туториала.)
Теперь нам нужно загрузить этот код. Мы сделаем это простой строкой на JavaScript, находящей HTML-элемент и получающей внутренний текст:
Он должен находится под кодом куба.
Не забывайте: только загруженный как строка код будет парситься как действительный код GLSL (то есть void main() <. >. Остальное — это просто boilerplate HTML.)
Шаг 2: применение шейдера
Способы применения шейдера могут отличаться, они зависят от используемой платформы и её интерфейса с видеопроцессором. С этим никогда не возникает трудностей, и быстрым поиском можно выяснить, как создать объект и применить к нему шейдеры с помощью Three.js.
Нам нужно создать специальный материал и передать его коду шейдера. В качестве объекта шейдера мы создадим плоскость (но можем использовать и куб). Вот что нужно сделать:
На этом этапе вы должны видеть белый экран:
Если заменить цвет в коде шейдера на любой другой и обновить страницу, то вы увидите новый цвет.
Задача: Сможете ли вы сделать одну часть экрана красной, а другую синей? (Если не получится, то на следующем шаге я дам подсказку!)
Шаг 3: передача данных
На этот момент мы уже можем делать с шейдером всё, что угодно, но пока не знаем, что можно сделать. У нас есть только встроенная возможность определения положения пикселя gl_FragCoord и, если вы помните, это положение нормализовано. Нам как минимум нужно знать размеры экрана.
Для передачи данных шейдеру нам нужно отправить то, что мы назвали uniform-переменной. Для этого мы создаём объект uniforms и добавляем к нему наши переменные. Вот синтаксис передачи разрешения экрана:
Строка типа Uniform | Тип GLSL | Тип JavaScript |
---|---|---|
‘i’, ‘1i’ | int | Number |
‘f’, ‘1f’ | float | Number |
‘v2’ | vec2 | THREE.Vector2 |
‘v3’ | vec3 | THREE.Vector3 |
‘c’ | vec3 | THREE.Color |
‘v4’ | vec4 | THREE.Vector4 |
‘m3’ | mat3 | THREE.Matrix3 |
‘m4’ | mat4 | THREE.Matrix4 |
‘t’ | sampler2D | THREE.Texture |
‘t’ | samplerCube | THREE.CubeTexture |
Мы ещё не закончили! Теперь, когда шейдер получает эту переменную, нам нужно с ней что-то сделать. Давайте создадим градиент, как мы делали это раньше: нормализовав координату и используя её для создания значения цвета.
Изменим код шейдера следующим образом:
В результате у нас получится красивый градиент!
Задача: Попробуйте разделить экран на четыре равных части разных цветов. Примерно вот так:
Шаг 4: обновление данных
Хорошо, что мы научились отправлять данные в шейдер, но что если нам нужно их обновить? Например, если открыть предыдущий пример в новой вкладке, а потом изменить размер окна, то градиент не обновится, потому что использует прежние размеры экрана.
Вот как выглядит функция рендеринга после внесения изменений:
Если открыть новый CodePen и изменить размер окна, то вы увидите, как изменяются цвета, несмотря на то, что изначальный размер окна просмотра остался тем же). Проще всего это заметить, посмотрев на цвета в углах и убедившись, что они не меняются.
Примечание: Отправка данных в видеопроцессор обычно является затратной задачей. Отправка нескольких переменных за один кадр вполне нормальна, но при передаче сотен переменных за кадр частота смены кадров значительно снизится. Кажется, что такое маловероятно, но если на экране есть несколько сотен объектов, и ко всем ним применено освещение с разными свойствами, то всё быстро выходит из под контроля. Позже мы узнаем про оптимизацию шейдеров.
Задача: Попробуйте постепенно изменять цвета.
Шаг 5: работа с текстурами
Вне зависимости от способа загрузки и формата текстур на всех платформах они передаются в шейдер как uniform-переменные.
Небольшое примечание о загрузке файлов в JavaScript: можно без проблем загружать изображения с внешнего URL (именно так мы и будем делать). Однако если вы захотите загрузить изображение локально, то возникнут проблемы с разрешениями, потому что JavaScript обычно не может и не должен иметь доступа к файлам в системе. Простейший способ решения — запустить локальный Python-сервер, что на самом деле проще, чем кажется.
В Three.js есть небольшая удобная функция для загрузки изображения как текстуры:
Первая строка задаётся только один раз. В неё можно вставить любой URL изображения.
И, наконец, нам нужно объявить uniform-переменную в коде шейдера, а потом отрисовать её тем же способом, как мы делали ранее — с помощью функции texture2D :
На экране должно появиться растянутое изображение конфет:
(Это изображение является стандартным тестовым изображением в компьютерной графике, оно взято у Института обработки сигналов и изображений (SIPI) (поэтому на нём показана аббревиатура IPI) Университета Южной Калифорнии. Думаю, оно нам подходит, ведь мы как раз изучаем графические шейдеры!)
Задача: Попробуйте постепенно менять цвета текстуры с полного цвета на градации серого.
Дополнительный шаг: применяем шейдеры к другим объектам
В созданной нами плоскости нет ничего особенного. Мы могли применить шейдер и к кубу. На самом деле можно просто заменить строку с геометрией плоскости:
Вуаля, конфеты нарисованы на кубе:
Вы можете возразить: «Постойте, но ведь это не похоже на правильную проекцию текстуры на куб!» Вы правы: если посмотреть на шейдер, то видно, что мы на самом деле говорим ему «размести все пиксели этого изображения на экране». То, что они находятся на кубе, просто означает, что все пиксели за его пределами отбрасываются.
Если бы мы хотели применить текстуру так, чтобы она выглядела физически нарисованной на кубе, то наша работа напоминала бы изобретение заново 3D-движка (что было бы довольно глупо, учитывая, что мы уже используем 3D-движок и мы можем просто попросить его отрисовать текстуру отдельно на каждой стороне). В этом туториале мы используем шейдеры для того, что иначе достичь было бы невозможно, так что мы не будем вдаваться в такие подробности. (Если вы жаждете узнать больше, на Udacity есть отличный курс по основам 3D-графики!)
Следующие шаги
На этом этапе мы уже можем делать всё, что сделали в ShaderToy, однако теперь мы способны использовать любые текстуры и любые объекты, на любой платформе.
Имея всю эту свободу, мы можем сделать что-нибудь вроде системы освещения, с реалистичными тенями и источниками света. Этим мы ниже и займёмся. Кроме того, я расскажу о техниках оптимизации шейдеров.
Освоившись с основами шейдеров мы на практике применим мощь видеопроцессора для создания реалистичного динамического освещения.
Начиная с этого момента мы будем рассматривать общие концепции графических шейдеров без привязки к конкретной платформе. (Для удобства во всех примерах кода по-прежнему будет использоваться JavaScript/WebGL.)
Для начала найдите подходящий вам способ выполнения шейдеров. (JavaScript/WebGL — это простейший способ, но я рекомендую вам поэкспериментировать со своей любимой платформой!)
К концу этого туториала вы не только начнёте хорошо ориентироваться в системах освещения, но и с нуля создадите собственную.
Вот как будет выглядеть конечный результат (нажмите мышью для включения света):
Многие игровые движки имеют уже готовые системы освещения, но понимание того, как они сделаны и как создавать собственные, даёт вам больше возможностей придать игре соответствующий ей уникальный вид. Кроме того, шейдерные эффекты не обязательно должны быть только «косметическими», они открывают двери потрясающим новым игровым механикам!
Отличным примером этого является Chroma. Игрок может бегать по динамическим теням, создаваемым в реальном времени:
Приступаем к работе: наша исходная сцена
Мы многое пропустим в первоначальной настройке, потому что она подробно рассмотрена выше. Начнём с простого фрагментного шейдера, рендерящего нашу текстуру:
Здесь не происходит ничего сложного. Код на JavaScript задаёт сцену и отправляет шейдеру текстуру для рендеринга и размеры экрана.
В коде на GLSL мы объявляем и используем эти uniform-переменные:
Прежде чем использовать координаты пикслей для отрисовки текстуры, мы их нормализуем.
Просто чтобы убедиться, что вы всё понимаете, вот вам небольшое задание на разогрев:
Задача: Отрендерите текстуру, не изменяя соотношения её сторон (Попробуйте сделать это самостоятельно, мы рассмотрим решение ниже.)
Довольно очевидно, почему текстура растянута, но если это непонятно, то вот подсказка: посмотрите на строку, в которой мы нормализуем координаты:
Мы делим x и y на разные числа (на ширину и высоту экрана). Естественно, что изображение будет растянутым.
Ради простоты дальше в туториале мы оставим нормализованный код таким же, то неплохо бы разобраться, что здесь происходит.
Шаг 1: добавление источника света
Прежде чем создать что-то интересное, нам нужен источник света. «Источник света» — это просто точка, передаваемая шейдеру. Для этой точки мы создадим новую uniform:
Мы создали вектор с тремя измерениями, потому что мы хотим использовать x и y в качестве положения источника на экране, а z — в качестве радиуса.
Давайте присвоим в JavaScript значения нашему источнику света:
Мы будем использовать радиус как процент от размеров экрана, поэтому 0.2 будет составлять 20% экрана. (В этом выборе нет ничего особенного. Мы могли бы задать размер в пикселях. Это число ничего не значит, пока мы не начнём с ним делать что-нибудь в коде GLSL.)
Чтобы получить положение мыши, нужно просто добавить получатель события (event listener):
Давайте теперь напишем код шейдера, чтобы воспользоваться этой координатой источника света. Начнём с простой задачи: сделаем так, чтобы каждый пиксель в пределах радиуса источника света был видимым, а остальные были чёрными.
На GLSL это может выглядеть примерно так:
Здесь мы сделали следующее:
Ой-ёй! Источник света следует за мышью как-то странно.
Задача: Сможете это исправить? (Попробуйте снова разобраться самостоятельно, прежде чем мы решим эту задачу ниже.)
Исправление движения источника света
Возможно, вы помните, что ось Y здесь перевёрнута. Вы можете поторопиться просто ввести:
Это математически верно, но если поступить так, то шейдер не скомпилируется! Проблема в том, что uniform-переменные невозмжоно изменять. Чтобы понять, почему, нужно помнить, что этот код выполняется для каждого отдельного пикселя параллельно. Представьте, что все процессорные ядра попытаются изменить единственную переменную одновременно. Плохая ситуация!
Мы можем исправить ошибку, создав новую переменную вместо uniform. Или ещё лучше — мы можем просто сделать этот шаг до передачи данных в шейдер:
Теперь мы успешно определили видимый радиус нашей сцены. Однако он выглядит слишком резким…
Добавление градиента
Вместо простой обрезки до чёрного цвета за пределами радиуса давайте попробуем создать плавный градиент у краёв. Можно сделать это с помощью расстояния, которое мы уже вычисляем.
Вместо присвоения всем пикселям в пределах радиуса цвета текстуры:
мы можем умножать его на коэффициент расстояния:
На этом рисунке dist вычисляется для произвольного пикселя. dist меняется в зависимости от того, в каком пикселе мы находимся, а значение light.z * res.x постоянно.
Если мы посмотрим на пиксель на границе круга, то dist равно длине радиуса, то есть в результате мы умножаем color на 0 и получаем чёрный цвет.
Шаг 2: добавляем глубину
Пока мы не сделали ничего особенного, просто добавили нашей текстуре градиентную маску. Всё по-прежнему выглядит плоским. Чтобы понять, как это исправить, давайте посмотрим, что сейчас делает система освещения, и сравним с тем, что она должна делать.
В представленном выше случае стоит ожидать, что точка A будет освещена сильнее всего, потому что источник света находится прямо над ней, а B и C будут темнее, потому что на боковых сторонах практически нет лучей.
Однако вот что видит наша система освещения сейчас:
Все точки обрабатываются одинаково, потому что единственный фактор, который учитывает система — это расстояние на плоскости xy. Вы можете подумать, что нам всего лишь нужна высота каждой их этих точке, но это не совсем так. Чтобы понять, почему, рассмотрите этот рисунок:
A находится наверху фигуры, а B и C — по бокам. D — это ещё одна точка на земле. Мы видим, что A и D должны быть самыми яркими, причём D немного темнее, потому что свет достигает её под углом. С другой стороны, B и C должны быть очень тёмными, потому что до них почти не доходит свет, ведь они направлены от источника света.
Не так важна высота, как направление, в котором повёрнута поверхность. Оно называется нормалью поверхности.
Но как передать эту информацию шейдеру? Мы ведь наверно не можем передавать огромный массив из тысяч чисел для каждого отдельного пикселя? На самом деле, мы так и делаем! Только мы называем это не массивом, а текстурой.
На рисунке выше показана простая карта нормалей. Если воспользоваться инструментом «пипетка» мы увидим, что направление по умолчанию («плоское») представлено цветом (0.5, 0.5, 1) (синий цвет, занимающий бо́льшую часть изображения). Это направление, указывающее прямо вверх. Значения x, y и z присваиваются значениям r, g и b.
Наклонная сторона справа повёрнута вправо, поэтому её значение x выше. Значение x также является значением красного, именно поэтому сторона выглядит немного красноватой или розоватой. То же самое относится ко всем остальным сторонам.
Карта выглядит забавно, потому что не предназначена для рендеринга, она просто кодирует значения нормалей этих поверхностей.
Давайте загрузим эту простую карту нормалей для теста:
И добавим её как одну из uniform-переменных:
Чтобы проверить, что мы загрузили её правильно, давайте попробуем отрендерить её вместо текстуры, изменив код на GLSL (помните, что мы на этом этапе используем просто фоновую текстуру, а не карту нормалей):
Шаг 3: применение модели освещения
Теперь, когда у нас есть данные нормалей поверхностей, нам нужно реализовать модель освещения. Иными словами, надо сообщить поверхности, как учесть все имеющиеся факторы для вычисления окончательной яркости.
Простейшей для реализации моделью является модель Фонга. Вот как она работает: пусть у нас есть поверхность с данными о нормалях:
Мы просто вычисляем угол между источником света и нормалью поверхности:
Чем меньше угол, тем ярче пиксель.
Это значит, что когда пиксель находится непосредственно под источником света, где разность углов равна 0, он будет самым ярким. Самые тёмные пиксели будут указывать в том же направлении, что и источник света (это будет похоже на заднюю часть объекта).
Давайте реализуем эту модель.
Поскольку для проверки мы используем простую карту нормалей, давайте зальём текстуру сплошным цветом, чтобы чётко понимать, всё ли у нас получается.
Давайте сделаем сплошной белый цвет (или любой другой цвет):
Вот как выглядит алгоритм:
1. Получаем вектор нормали текущего пикселя
Нам нужно знать, в каком направлении «смотрит» поверхность, чтобы можно было вычислить количество света, попадающего на этот пиксель. Это направление хранится в карте нормалей, поэтому получение вектора нормали заключается в получении цвета текущего пикселя в текстуре нормалей:
Поскольку альфа-значение ничего не обозначает на карте нормалей, нам требуются только первые три компонента.
2. Получаем вектор направления света
Теперь нам нужно знать, в каком направлении указывает свет. Можно представить, что поверхность освещения — это фонарик, направленный на экран в точке расположения курсора мыши. Поэтому можно вычислить вектор направления света просто с помощью расстояния между источником света и пикселем:
Он должен иметь и координату Z (чтобы можно было вычислить угол относительно трёхмергого вектора нормали поверхности). С этим значением можно поэкспериментировать. Вы заметите, что чем оно меньше, тем резче контраст между яркими и тёмными областями. Можно представить, что это высота фонарика над сценой: чем он дальше, тем равномернее распространяется свет.
3. Нормализуем векторы
Теперь нам нужно нормализировать:
4. Вычисляем угол между векторами
Давайте сделаем это с помощью встроенной функции dot:
Я назвал переменную diffuse потому что этот термин используется в модели освещения по Фонгу, ведь она определяет количество света, достигающее поверхности сцены.
5. Умножаем конечный цвет на этот коэффициент
И мы получили работающую модель освещения! (Попробуйте увеличить радиус источника света, чтобы эффект был сильнее заметен.)
Хм, кажется, что-то не так. Похоже, что источник как-то наклонён.
Давайте ещё раз посмотрим на наши вычисления. У нас есть вектор света:
Задача: Понимаете ли вы, в чём заключается решение? Сможете реализовать его?
Вот как демо выглядит после вычитания 0.5 из координат x и y вектора нормали:
И у нас получилась работающая модель освещения!
Можно поставить на фон каменную текстуру, а настоящую карту нормалей взять в репозитории этого туториала на GitHub (а именно здесь):
Нам нужно только изменить одну строку на JavaScript, с:
И одну строку на GLSL:
Нам больше не нужен сплошной белый цвет, мы загрузим настоящую текстуру, вот так:
И вот окончательный результат:
Советы по оптимизации
Видеопроцессор выполняет свою работу очень эффективно, но очень важно знать, что может её замедлять. Вот несколько советов:
Ветвление
В шейдерах обычно желательно по возможности избегать ветвления. Хотя большое количество конструкций if редко бывает проблемой в любом коде для ЦП, в шейдерах для видеопроцессора они могут стать узким местом.
Отложенный рендеринг
Это очень полезная концепция при работе с освещением. Представьте, что нам нужны два источника освещения, или три, или десяток. Нам придётся вычислять угол между каждой нормалью поверхности и каждой точкой источника света. В результате шейдер будет выполняться с черепашьей скоростью. Отложенный рендеринг — это способ оптимизации такого процесса разбиением работы шейдера на несколько проходов. Вот статья, в которой подробно объяснено, что это значит. Я процитирую важную для нас часть:
Освещение — это основная причина выбора того или иного пути. В стандартном прямолинейном конвейере рендеринга вычисления освещения должны выполняться для каждой вершины и каждого фрагмента видимой сцены для каждого источника света в сцене.
Например, вместо передачи массива точек источников света, можно отрисовать их на текстур в виде кругов, цвет каждого пикселя которых будет представлять интенсивность освещения. Таким образом мы сможем вычислять комбинированный эффект всех источников освещения в сцене, а потом просто передать его конечной текстуре (или буферу, как её иногда называют) для вычисления освещения.
Способность разделить работу на несколько проходов — это очень полезная техника при создании шейдеров. Например, она используется для ускорения шейдера при вычислении эффекта размывки, а также в шейдерах жидкостей/дыма.
Следующие шаги
Теперь, когда вы получили работающий шейдер освещения, вот с чем ещё можно поэкспериментировать:
Ссылки
Каменная текстура и карта нормалей для этого туториала взяты с OpenGameArt: http://opengameart.org/content/50-free-textures-4-normalmaps.
Существует множество программ, помогающих в создании карт нормалей. Если вам интересно узнать больше о создании собственных карт нормалей, то вам может помочь эта статья.