Filtry niestandardowe, czyli tzw. Shadery CSS, pozwalają wykorzystać możliwości cieniowania WebGL w Twojej zawartości DOM. Ponieważ w obecnej implementacji używane są praktycznie takie same narzędzia do cieniowania, jak te stosowane w WebGL, trzeba cofnąć się i nieco lepiej zrozumieć terminologię 3D i trochę procesu graficznego.
W załączeniu przesyłam nagranie prezentacji dostarczonej przeze mnie niedawno do LondonJS. W tym filmie omówię terminologię 3D, co musisz wiedzieć, jakie są różne typy zmiennych i jak już dziś zacząć korzystać z filtrów niestandardowych. Warto też chwycić slajdy, aby samodzielnie przetestować prezentacje.
Wprowadzenie do Shaderów
Już wcześniej wspomnieliśmy wprowadzenie do cieniowania, z którego dokładnie wynika, czym są cieniowanie i jak można ich używać z punktu widzenia WebGL. Jeśli nie masz doświadczenia z cieniowaniem, musisz przeczytać go, zanim pójdziesz dalej, ponieważ wiele pojęć i języków zależy od obecnej terminologii cieniowania WebGL.
Mając to na uwadze, włączmy filtry niestandardowe i zabierajmy się do roboty!
Włączanie filtrów niestandardowych
Filtry niestandardowe są dostępne zarówno w Chrome, jak i w wersji Canary oraz Chrome na Androida. Wejdź na stronę about:flags
i wyszukaj „CSS Shaders”, włącz je i ponownie uruchom przeglądarkę. Wszystko gotowe!
Składnia
Filtry niestandardowe rozszerzają zestaw filtrów, które możesz już stosować do elementów DOM, takich jak blur
lub sepia
. Eric Bidelman napisał świetne narzędzie do zabawy, które warto wypróbować.
Aby zastosować filtr niestandardowy do elementu DOM, użyj następującej składni:
.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)
}
Jak widać, deklarujemy cieniowanie wierzchołków i fragmentów, liczbę wierszy i kolumn, na które ma być podzielony element DOM, a potem wszelkie uniformy, które chcemy przekazać.
Warto jeszcze wspomnieć, że używamy funkcji mix()
wokół cieniowania fragmentów z trybem mieszania (normal
) i trybem złożonym (source-atop
). Spójrzmy na sam program do cieniowania fragmentów, aby dowiedzieć się, dlaczego potrzebujemy funkcji mix()
.
Wysyłanie Pixela
Jeśli znasz narzędzia do cieniowania WebGL, zauważysz, że w filtrach niestandardowych trochę się różni. W przypadku jednego z nich nie tworzymy tekstur, których nasz cieniowanie fragmentów używa do wypełnienia pikseli. Zawartość DOM z zastosowanym filtrem jest automatycznie mapowana na teksturę, co oznacza dwie rzeczy:
- Ze względów bezpieczeństwa nie możemy wysyłać zapytań o wartości kolorów poszczególnych pikseli tekstury DOM.
- Nie określamy (przynajmniej w przypadku obecnych implementacji) ostatecznego koloru piksela, czyli
gl_FragColor
jest niedostępny. Zamiast tego zakłada się, że chcesz renderować zawartość DOM, a musisz manipulować jej pikselami pośrednio za pomocącss_ColorMatrix
icss_MixColor
.
W związku z tym nasz „Hello World” cieniowania fragmentów wygląda mniej więcej tak:
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?
}
Każdy piksel zawartości DOM jest mnożony przez parametr css_ColorMatrix
, który w powyższym przypadku nie działa jako matryca tożsamości ani nie zmienia żadnych wartości RGBA. Gdybyśmy chcieli zachować czerwone wartości, użyjemy css_ColorMatrix
w ten sposób:
// 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);
Po pomnożeniu wartości pikseli 4D (RGBA) przez macierz otrzymasz zmanipulowaną wartość piksela z drugiej strony, w tym przypadku wyzerujesz zielone i niebieskie komponenty.
Kolor css_MixColor
jest używany głównie jako kolor podstawowy, który można połączyć z zawartością DOM. Mieszanie odbywa się za pomocą trybów mieszania znanych z pakietów artystycznych: nakładki, ekranu, rozmycia kolorów, ostrego światła itd.
Te dwie zmienne mogą manipulować pikselami na wiele sposobów. Zapoznaj się ze specyfikacją efektów filtra, aby lepiej zrozumieć współdziałanie trybów mieszania i trybów złożonych.
Tworzenie Vertex
W WebGL ponosimy pełną odpowiedzialność za tworzenie punktów 3D naszej siatki. W przypadku filtrów niestandardowych wystarczy określić liczbę wierszy i kolumn, a przeglądarka automatycznie podzieli zawartość DOM na kilka trójkątów:
Każdy z tych wierzchołków jest przekazywany do cieniowania wierzchołków w celu manipulacji. To oznacza, że w razie potrzeby możemy przenosić je w przestrzeni 3D. Już niedługo będziesz mieć możliwość robienia wspaniałych efektów.
Animowanie za pomocą Shaderów
Dodanie animacji do cieniowania sprawia, że są one zabawne i angażujące. Aby to zrobić, użyj przejścia (lub animacji) w kodzie CSS w celu zaktualizowania jednolitych wartości:
.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);
}
W powyższym kodzie należy pamiętać, że w trakcie przenoszenia czas ten zostanie zmniejszony z 0
do 1
. Wewnątrz programu do cieniowania można zadeklarować jednolity format time
i wykorzystać jego obecną wartość:
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;
}
Zacznij grać!
Filtry niestandardowe to świetna zabawa, a niesamowite efekty, jakie możesz uzyskać, są trudne (a w niektórych przypadkach nawet niemożliwe) bez nich. Wciąż jesteśmy na początku drogi i wciąż się zmieniło, ale dodanie ich może nieco uatrakcyjnić Twoje projekty, więc warto spróbować?