В двух предыдущих частях мы довольно неплохо освоили владение цветом, а
так же научились влиять на шейдер извне, что позволило нам создать
шейдер с постепенным изменением цветов текстуры. Однако, помимо цвета
пикселя, у него есть еще одна важная для нас информация – координаты.
Получив место расположения пикселя и манипулируя с ним, мы можем менять
не только цвета, но и саму структуру изображения.
В шейдере точка
представлена 2-компонентным вектором. В первой части мы уже сталкивались
с этими координатами, когда разбирали структура стандартного шейдера,
который создает GMS.
На вкладке
Fragment эти координаты представлены строкой:
Код
varying vec2 v_vTexcoord;
Так же, как и у 4-компонентного вектора цвета, мы может обращаться к
каждому из компонентов, мы может обратиться и к координатам. Не сложно
догадаться, что два компонента, который имеет вектор
v_vTexcoord – это координаты x и y. Для примера, давайте обратимся к этим координатам и перепишем их в переменные:
Код
float x1 = v_vTexcoord.x;
float y1 = v_vTexcoord.y;
Теперь
x1 и
y1 содержат в себе координаты текущего пикселя текстуры, что равносильно координатам
v_vTexcoord.x и
v_vTexcoord.y, все просто.
Следует отметить, что подобно цветам, координаты лежат в диапазоне
от 0 до 1, следовательно точки на вершинах имеют координаты 0:0, 0:1, 1:0 и 1:1.
Пришло время посмотреть, как это все работает на практике. Начнем мы с
малого – попробуем вывести на экран лишь часть изображения, обрезав его
слева и справа.
Делается подобное элементарно. На словах это выглядит так: если координата пикселя по
x больше определенного значения или координата пикселя по
x меньше определенного значения, то рисуем этот пиксель. На деле же мы имеем всего две строчки:
Код
if (v_vTexcoord.x > 0.4 && v_vTexcoord.x < 0.6)
gl_FragColor = texture2D(gm_BaseTexture, v_vTexcoord);
Таким образом, будут выводиться только пиксели, попадающие в диапазон от 0.4 до 0.6.
Как видите, в координатах тоже нет ничего особо сложного, главное
придумать, как их применить. И придумал я следующее: как насчет того,
чтобы сделать прозрачной часть текстуры определенного радиуса в месте,
куда указывает курсор!?
Прозрачная область.
Начнем, как обычно, с создания нового шейдера. Перейдем на вкладку
Fragment и приступим.
Получим исходный цвет нашей текстуры.
Код
vec3 Color = texture2D(gm_BaseTexture, v_vTexcoord).rgb;
Как видите, в отличии от прошлых аналогичных действий, мы создаем не
4-компонентный вектор, а 3-компонентный, следовательно, хранить мы будем
всего 3 параметра. Вы, наверное, уже заметили, каким образом три эти
параметра получены. Дело в том, что мы можем обращаться к компонентам
векторов не только поодиночке, как делали это раньше.
Вместо того, чтобы перебирать каждый отдельный компонент через
texture2D(gm_BaseTexture, v_vTexcoord).r,
texture2D(gm_BaseTexture, v_vTexcoord).g и
texture2D(gm_BaseTexture, v_vTexcoord).b, мы просто взяли все их сразу
texture2D(gm_BaseTexture, v_vTexcoord).rgb.
Аналогично, можно поступить и с координатами. Выше, мы брали каждую
координату по отдельности, а теперь давайте просто перепишем обе
координаты в 2-компонетный вектор:
Код
vec2 coords = v_vTexcoord.xy;
В данной ситуации, действие это, как вы понимаете, особого смысла не
имеет. Мы просто переписали один 2-компонентный вектор в другой. Но в
дальнейшем, подобная операция может пригодиться, потому усвоим ее.
Так как положение прозрачной области будет зависеть от координат мыши,
нам необходимо принимать эти координаты извне, чтобы работать с ними в
шейдере. С подобным мы уже сталкивались в прошлой части, тут все делаем
аналогично. Перед функцией
main() задаем две переменные, в которые будем передавать координаты положения курсора:
Код
uniform float xMouse;
uniform float yMouse;
Эти координаты мы так же можем представить, как 2-компонентный вектор.
Код
vec2 mouse_coords = vec2(xMouse,yMouse);
По аналогии с координатами пикселя, к координатам мыши теперь можно обращаться через
mouse_coords.x и
mouse_coords.y.
На самом деле, для наших текущих целей, для этого нет особой
необходимости, ведь мы может с таким же успехом обращаться к координатам
не через вектор, а напрямую, т.к.
mouse_coords.x = xMouse, а
mouse_coords.y = yMouse, но для закрепления материала, поработаем с вектором.
И так, координаты мыши у нас имеются, координаты пикселей тоже,
осталось лишь задать радиус прозрачной области. Для удобства, запишем
его в виде переменной.
Следует учитывать, что раз координаты на текстуре находятся в диапазоне от 0 до 1, то и радиус должен быть в том же диапазоне.
Осталось лишь определить, входит ли текущая точка на текстуре в
заданный радиус вокруг указателя мыши. Проще говоря – принадлежит ли
точка окружности. Если принадлежит, то делаем текущий пиксель
прозрачным, если нет, то выводим его с изначальной альфой.
Открываем учебник по геометрии и смотрим, как проверить принадлежность
точки к окружности. Для этого существует следующее условие:
(x-x0)^2 + (y-y0)^2 <= R^2, где (х0,у0) - центр окружности, R - её радиус, а (х,у) – проверяемая точка.
Все необходимые данные у нас имеются, остается только составить понятное для системы условие.
Код
if ((pow((coords.x - mouse_coords.x),2.0) + pow((coords.y - mouse_coords.y),2.0)) < pow(radius,2.0))
Думаю, объяснять тут ничего не нужно. Единственное, что следует уточнить, это функция
pow(), которая возводит число в указанную степень, в нашем случае 2.0.
Дело за малым, указать, что произойдет, если условие истинно, и что будет, если оно ложно.
Код
if ((pow((coords.x - mouse_coords.x),2.0) + pow((coords.y - mouse_coords.y),2.0)) < pow(radius,2.0))
gl_FragColor = vec4(Color, 0.5);
else
gl_FragColor = texture2D(gm_BaseTexture, coords);
Как видите, если условие выполняется, то мы выводим текущий пиксель с
исходными цветами, то изменяем его прозрачность на 0.5. В противном
случае, мы просто выводим текстуру, как она есть.
Остается
применить наш шейдер на практике. Создадим новый спрайт, часть которого
будем делать прозрачной. При работе с координатами нужно учитывать один
момент: спрайт должен быть представлен текстурой, а текстура должна
иметь размер, кратный двум. Т.е. ширина и высота спрайта должны
соответствовать 32х32, 64х64, 128х128 и т.д. При этом, при создании
спрайта, следует установить галочку
used for 3D (опция эта будет доступна, только если размер спрайта проставлен в соответствии с вышеизложенным правилом).
Создадим новый объект, установим для него наш спрайт и поместим объект в
комнату. Для наглядности, можно установить в комнате какую-нибудь
фоновую картинку.
В событии
Draw нашего объекта прописываем следующее:
Код
var xMouse = shader_get_uniform(shader0,"xMouse");
var yMouse = shader_get_uniform(shader0,"yMouse");
shader_set(shader0);
shader_set_uniform_f(xMouse,mouse_x/sprite_width);
shader_set_uniform_f(yMouse,mouse_y/sprite_height);
draw_sprite(sprite0,0,0,0);
shader_reset();
Тут вам все уже знакомо по прошлым частям. Мы создаем переменную,
которая хранит изменяемое значение в шейдере и задаем этому значению
координаты мыши. Заметьте, что координаты мыши мы делим на размеры
спрайта. Вспоминаем, как у нас представлены координаты текстуры в
шейдере и все становится понятно, координаты мыши должны быть так же в
диапазоне от 0 до 1.
Теперь можно запустить приложение и
насладиться результатом. Вот такую интересную штучку сделал я, благодаря
данному шейдеру, как видите, получился своеобразный "эффект рентгена":
Таким же образом, вы можете реализовать и пройденные в прошлых частях
графические эффекты, например, обесцветив часть изображения.
На этом все. Дальше - больше. Спасибо за внимание.