Среда, 18 Сентября 2019, 03:43

Приветствую Вас Гость

Меню сайта
Категории каталога
Создание игр [323]
Статьи об общих понятиях связанных с созданием игр.
Программирование [73]
Гайды по программированию на разных ЯП.
Движки и Гейммейкеры [126]
Статьи о программах для создания игр, уроки и описания.
Софт [28]
Различные программы, в том числе в помощь игроделам.
2D-графика [12]
Уроки по рисованию, растр, пиксель-арт, создание спрайтов и пр.
3D-графика [11]
Уроки по моделированию, ландшафт, модели, текстурирование и пр.
Моддинг игр [5]
Модификация компьютерных игр, создание дополнений, перевод, хакинг.
Игры [123]
Статьи об играх, в том числе и сделанных на гейммейкерах.
Разное [83]
Статьи, которые не вошли в определённые разделы.
Наш опрос
Какую графическую систему API вы используете?
Всего ответов: 10043
Главная » Статьи » Создание игр

Game Maker Studio: Шейдеры (Часть 2)


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

Сепия или что-то в этом роде.


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

Создаем новый шейдер и переходим на вкладку fragment. Здесь в функции main() пишем следующее:

Код
vec4 color = texture2D(gm_BaseTexture, v_vTexcoord);


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

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

Код
float intensity = 0.299 * color.r + 0.587 * color.g + 0.184 * color.b;


Как видно, мы создаем переменную intensity, которая будет хранить интенсивность каждого рассматриваемого пикселя. Если сейчас вывести изображение, используя в качестве каждой составляющей цвета полученную интенсивность, то изображение станет черно-белым.

Код
color.r = intensity + 0.195;
color.g = intensity + 0.176;
color.b = intensity;


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

Остается только передать наши новые цвета на вывод и наслаждаться результатом.

Код
gl_FragColor = vec4(color.r,color.g,color.b,1.0);


Ограничение палитры.


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

Код
vec4 color = texture2D(gm_BaseTexture, v_vTexcoord);
float intensity = 0.299 * color.r + 0.587 * color.g + 0.184 * color.b;


Далее, вновь немного поразмыслим. Чтобы ограничить количество цветов по интенсивности, нам необходимо взять отдельные диапазоны интенсивностей и сопоставить им какой-то цвет.

Решение напрашивается само собой – прописать условие попадания в диапазон и задать конкретные для данного диапазона цвета. Сделать это можно так:

Код
If (intensity > 0.9)
color = vec4(…);
else
If (intensity > 0.7)
color = vec4(…);
else


Но мы пойдем другим путем и придадим данному коду более короткую и легко настраиваемую форму.

Код
float i = 1.0;
   
for (i=1.0; i>0.0; i-=0.5)
{
  if (intensity > i)
  {
  color = vec4(1.0*i, 0.0*i, 0.0*i, texture2D(gm_BaseTexture, v_vTexcoord).a);
  break;
  }
  else
  {
  color = vec4(0.0, 0.0, 0.0, texture2D(gm_BaseTexture, v_vTexcoord).a);
  }
}


Задаем знакомый всем цикл for, в котором перебираем значения переменной i с заданным шагом. Переменная i будет хранить у нас нижнюю границу диапазона, в котором пикселю с заданной интенсивностью присваивается конкретный цвет. Следовательно, чем меньше шаг цикла, тем больше будет диапазонов, а значит и цветов. В цикле проверяем, если полученная интенсивность пикселя больше i, то задаем этому пикселю цвет. Цвет, как видите, так же рассчитывается автоматически и в данном случае весь упор делается только на красную составляющую. Получается, что все пиксели, интенсивность которых больше i, будут окрашены в красный цвет вычисляемого оттенка 1.0*i.

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

Осталось вывести полученные значения и посмотреть на результат. На этот раз вывод мы будем осуществлять в более сокращенной форме:

Код
gl_FragColor = color;


Надеюсь, всем понятно, что данная запись равносильна прошлым вариантам вывода, ведь в нашем случае вектор color – это тот самый 4-компонентный вектор, который мы подаем на выходе. На этом наш шейдер создан и готов к работе. В результате чего получим мы примерно следующее:



Влияние извне.


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

Код
varying vec2 v_vTexcoord;
varying vec4 v_vColour;
void main()
{
vec4 color = texture2D(gm_BaseTexture, v_vTexcoord);
vec4 color2 = texture2D(gm_BaseTexture, v_vTexcoord);
float intensity = 0.299 * color.r + 0.587 * color.g + 0.184 * color.b;
float i = 1.0;
   
for (i=1.0; i>0.0; i-=0.5)
{
  if (intensity > i)
  {
  color = vec4(1.0*i, 0.0*i, 0.0*i, texture2D(gm_BaseTexture, v_vTexcoord).a);
  break;
  }
  else
  {
  color = vec4(0.0, 0.0, 0.0, texture2D(gm_BaseTexture, v_vTexcoord).a);
  }
}
gl_FragColor = color;
}


Чтобы создать значение, на которое мы хотим повлиять из все, достаточно объявить его перед функцией main() следующим образом:

Код
uniform float m;


Вне шейдера мы сделаем это значение динамически изменяемым и, чтобы визуально увидеть, что все действительно работает, давайте заменим какое-нибудь значение на переменную m. Я выбрал шаг цикла i-=0.5, получив в результате i-=m.

На этом создание шейдера мы закончили, перейдем к его выводу. Как и в первом уроке, создадим произвольный спрайт, желательно с детализированным изображением, для более четкого визуально представления эффекта. Зададим этот спрайт объекту, в событии draw у которого напишем следующее:

Код
shader_set(shader0);
draw_sprite(sprite_index,0,x,y);
shader_reset();


Если мы таким образом запустим вариант нашего шейдера без m, то увидим, что изображение приобрело красный оттенок и имеет заметно ограниченное количество цветов. Но что же делать с переменной m?!

Для начала, перейдем в событие create и привычным нам образом объявим произвольную переменную, присвоив ей значение 0.0.

Код
n = 0.0;


Вернемся в событие draw и перед запуском шейдера, пропишем незамысловатый принцип, по которому переменная n будет менять свое значение.

Код
if n<=0.5
n+=0.005;


Теперь нам нужно получить из шейдера то значение, которое мы хотим изменять. Сделать это можно по средствам функции shader_get_uniform.

Код
mn = shader_get_uniform(shader0, "m");


Как понятно, из shader0 мы взяли значение "m", которое будем менять. Остается только задать этому значению нашу переменную. Делается это после запуска шейдера следующим образом:

Код
shader_set_uniform_f(mn,n);


Теперь значение m из шейдера, которое мы записали в mn, будет равно созданной нами переменной n. Общий код в событии draw выглядит так:

Код
if n<=0.5
n+=0.005;

mn = shader_get_uniform(shader0, "m");

shader_set(shader0);
shader_set_uniform_f(mn,n);
draw_sprite(sprite_index,0,x,y);
shader_reset();


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

На этом все, дальше больше и интереснее. Спасибо за внимание.
Категория: Создание игр | Добавил: LunarPixel (04 Сентября 2013)
Просмотров: 5952 | Комментарии: 9 | Рейтинг: 4.3/6 |
Теги: Game Maker Studio, GameMaker Studio, шейдеры, уроки, GM, графика, Статьи, GMS, Shader, gamemaker
Дополнительные опции:
Также если вы считаете, что данный материал мог быть интересен и полезен кому-то из ваших друзей, то вы бы могли посоветовать его, отправив сообщение на e-mail друга:

Игровые объявления и предложения:
Если вас заинтересовал материал «Game Maker Studio: Шейдеры (Часть 2)», и вы бы хотели прочесть что-то на эту же тему, то вы можете воспользоваться списком схожих материалов ниже. Данный список сформирован автоматически по тематическим меткам раздела. Предлагаются такие схожие материалы: Если вы ведёте свой блог, микроблог, либо участвуете в какой-то популярной социальной сети, то вы можете быстро поделиться данной заметкой со своими друзьями и посетителями.

Всего комментариев: 9
+1-
8 Tim   (05 Сентября 2013 10:25)
TimПростите мою неосведомленность - я в GM уже много лет не работал - но язык шейдеров напоминает GLSL. Из чего можно сделать вывод, что GM нынче использует OpenGL. Это так?

+1-
9 LunarPixel   (05 Сентября 2013 13:05)
LunarPixelДа, это GLSL, так же поддерживается HLSL.

+1-
5 LunarPixel   (05 Сентября 2013 00:31)
LunarPixelСпасибо ) Завтра постараюсь выложить третью часть, в которой будет кое что уже более интересное )

+1-
6 FadeBaker   (05 Сентября 2013 07:21)
Ждем! Также, если не займет много времени, прошу в статье уделить внимание вершинным шейдерам. Или ты просто до них еще не дошел?

+1-
7 LunarPixel   (05 Сентября 2013 07:53)
LunarPixelДо них не дошел еще, думаю в 4 или 5 части начну их затрагивать.

+1-
1 Eshford   (04 Сентября 2013 21:39)
EshfordСпасибо за статью. Пробую написать шейдер инверсии цвета, но спрайт почему-то тупо полностью окрашивается в белый. Что делаю не так?
Код
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

void main()
{
  vec4 color = texture2D(gm_BaseTexture, v_vTexcoord);
  float invcolr = color.r;
  float invcolg = color.g;
  float invcolb = color.b;
  
  color.r = 255.0-invcolr;
  color.g = 255.0-invcolg;
  color.b = 255.0-invcolb;
  gl_FragColor = vec4(color.r,color.g,color.b,1.0);
}

+2-
2 LunarPixel   (04 Сентября 2013 21:49)
LunarPixelДиапазон оттенков цвета не от 0 до 255, а от 0 до 1. smile
т.е. 1.0-invcolor;

+1-
3 Eshford   (04 Сентября 2013 21:57)
EshfordДа, точно. Вот только что понял это, сделал, и тут увидел твой ответ, но спасибо всё равно!

Буду дальше пробовать! :3

+1-
4 FadeBaker   (05 Сентября 2013 00:08)
Хорошо смотрится! Лунару, как всегда, большое спасибо, ведь это можно использовать не только в GM: Studio.

Добавлять комментарии могут только зарегистрированные пользователи.
[ Регистрация | Вход ]
Поиск по сайту
10 случ. движков
  • Risen3D
  • PrBoom-plus
  • S2 Engine
  • Corona
  • Alice
  • ADRIFT
  • DX Studio
  • PixelLight
  • Nokia Qt
  • Zelda Classic
  • Друзья сайта
    Игровой форум GFAQ.ru Перевод консольных игр
    Все права сохранены. GcUp.ru © 2008-2019 Рейтинг