Пользовательские фильтры или CSS-шейдеры, как их раньше называли, позволяют вам использовать возможности шейдеров WebGL с вашим контентом DOM. Поскольку в текущей реализации используемые шейдеры практически такие же, как и в WebGL, вам нужно сделать шаг назад и разобраться в некоторой 3D-терминологии и немного о графическом конвейере.
Я включил записанную версию презентации, которую недавно провел в LondonJS. В видео я делаю обзор 3D-терминологии, которая вам нужна, чтобы понять, с какими типами переменных вы столкнетесь и как вы можете начать играть с пользовательскими фильтрами уже сегодня. Вам также следует взять слайды , чтобы вы могли самостоятельно поиграть с демонстрациями.
Введение в шейдеры
Ранее я написал введение в шейдеры , которое даст вам хорошее представление о том, что такое шейдеры и как их можно использовать с точки зрения WebGL. Если вы никогда не имели дела с шейдерами, вам необходимо прочитать эту информацию, прежде чем идти дальше, поскольку многие концепции и язык пользовательских фильтров основаны на существующей терминологии шейдеров WebGL.
Итак, с учетом вышесказанного, давайте включим пользовательские фильтры и приступим к работе!
Включение пользовательских фильтров
Пользовательские фильтры доступны как в Chrome, так и в Canary, а также в Chrome для Android. Просто зайдите в about:flags
и найдите «CSS Shaders», включите их и перезапустите браузер. Теперь вы готовы идти!
Синтаксис
Пользовательские фильтры расширяют набор фильтров, которые вы уже можете применять, например blur
или sepia
, к вашим элементам DOM. Эрик Бидельман написал для них отличный игровой инструмент , который вам стоит попробовать.
Чтобы применить пользовательский фильтр к элементу DOM, вы используете следующий синтаксис:
.customShader {
-webkit-filter:
custom(
url(vertexshader.vert)
mix(url(fragment.frag) normal source-atop),
/* Row, columns - the vertices are made automatically */
4 5,
/* We set uniforms; we can't set attributes */
time 0)
}
Из этого вы увидите, что мы объявляем наши вершинные и фрагментные шейдеры, количество строк и столбцов, на которые мы хотим разбить наш DOM-элемент, а затем любые формы, через которые мы хотим пройти.
И последнее, на что следует обратить внимание: мы используем функцию mix()
во фрагментном шейдере с режимом наложения ( normal
) и составным режимом ( source-atop
). Давайте взглянем на сам фрагментный шейдер, чтобы понять, зачем нам вообще нужна функция mix()
.
Пиксельное нажатие
Если вы знакомы с шейдерами WebGL, вы заметите, что в пользовательских фильтрах все немного по-другому. Во-первых, мы не создаем текстуры, которые наш фрагментный шейдер использует для заполнения пикселей. Скорее, содержимое DOM, к которому применен фильтр, автоматически сопоставляется с текстурой, а это означает две вещи:
- По соображениям безопасности мы не можем запрашивать значения цвета отдельных пикселей текстуры DOM.
- Мы не устанавливаем (по крайней мере, в текущих реализациях) конечный цвет пикселя самостоятельно, т.е.
gl_FragColor
запрещено. Скорее, предполагается, что вы захотите визуализировать содержимое DOM, и вам придется косвенно манипулировать его пикселями черезcss_ColorMatrix
иcss_MixColor
.
Это означает, что наш Hello World фрагментных шейдеров выглядит примерно так:
void main() {
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0);
css_MixColor = vec4(0.0, 0.0, 0.0, 0.0);
// umm, where did gl_FragColor go?
}
Каждый пиксель содержимого DOM умножается на css_ColorMatrix
, который в приведенном выше случае ничего не делает, поскольку является единичной матрицей , и не меняет ни одно из значений RGBA. Если бы мы хотели, скажем, просто сохранить красные значения, мы бы использовали css_ColorMatrix
следующим образом:
// keep only red and alpha
css_ColorMatrix = mat4(1.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 0.0,
0.0, 0.0, 0.0, 1.0);
Надеемся, вы увидите, что, умножая значения пикселей 4D (RGBA) на матрицу, вы получаете манипулируемое значение пикселя с другой стороны, и в данном случае такое, которое обнуляет зеленый и синий компоненты.
css_MixColor
в основном используется в качестве базового цвета, который вы хотите смешать с содержимым DOM. Смешение осуществляется с помощью режимов наложения, с которыми вы знакомы по художественным пакетам: наложение, экран, осветление цвета, жесткий свет и так далее.
Существует множество способов, которыми эти две переменные могут манипулировать пикселями. Вам следует ознакомиться со спецификацией эффектов фильтра , чтобы лучше понять, как взаимодействуют режимы наложения и композиции.
Создание вершин
В WebGL мы берем на себя полную ответственность за создание трехмерных точек нашей сетки, но в пользовательских фильтрах все, что вам нужно сделать, это указать количество строк и столбцов, которые вы хотите, и браузер автоматически разобьет ваш DOM-контент на группу треугольников. :
Каждая из этих вершин затем передается в наш вершинный шейдер для манипуляций, а это означает, что мы можем начать перемещать их в трехмерном пространстве по мере необходимости. Совсем скоро вы сможете создавать потрясающие эффекты!
Анимация с помощью шейдеров
Добавление анимации в шейдеры делает их интересными и увлекательными. Для этого вы просто используете переход (или анимацию) в своем CSS для обновления унифицированных значений:
.shader {
/* transition on the filter property */
-webkit-transition: -webkit-filter 2500ms ease-out;
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 0);
}
.shader:hover {
-webkit-filter: custom(
url(vshader.vert)
mix(url(fshader.frag) normal source-atop),
1 1,
time 1);
}
Итак, в приведенном выше коде следует отметить, что во время перехода время будет уменьшаться с 0
до 1
. Внутри шейдера мы можем объявить единое time
и использовать любое его текущее значение:
uniform float time;
uniform mat4 u_projectionMatrix;
attribute vec4 a_position;
void main() {
// copy a_position to position - attributes are read only!
vec4 position = a_position;
// use our time uniform from the CSS declaration
position.x += time;
gl_Position = u_projectionMatrix * position;
}
Начинайте играть!
С пользовательскими фильтрами очень интересно играть, а без них создать потрясающие эффекты сложно (а в некоторых случаях невозможно). Это еще только начало, и все немного меняется, но их добавление добавит немного шоу-бизнеса в ваши проекты, так почему бы не попробовать?