Czym jest shader GLSL?
Shader to mały program działający równolegle na GPU — raz na piksel (fragment shader) albo raz na wierzchołek (vertex shader). GLSL (OpenGL Shading Language) to język przypominający C, w którym się je pisze. W WebGL i Three.js wymagane są oba typy, ale to fragment shader odpowiada za większość wizualnej magii.
GPU uruchamia ten sam fragment shader dla każdego piksela jednocześnie. Canvas 1920×1080 uruchamia shader ~2 miliony razy w każdej klatce, wszystkie równolegle — dlatego shadery są tak szybkie.
Minimalny fragment shader
Jedynym wymaganym wyjściem jest gl_FragColor —
vec4 zawierający (czerwony, zielony, niebieski,
alfa), każdy w zakresie 0.0–1.0.
Korzystanie ze współrzędnych: wbudowane gl_FragCoord
Uniform gl_FragCoord.xy podaje pozycję piksela w
pikselach okna. Znormalizuj ją do [0,1] i użyj jako gradientu
koloru:
To renderuje gradient czerwono-żółty od lewej do prawej i zielono-cyjanowy od dołu do góry — klasyczny wynik wprowadzający do GLSL.
Podstawowe funkcje wbudowane
| Funkcja | Co robi | Typowe zastosowanie |
|---|---|---|
| sin(x) / cos(x) | Fala trygonometryczna, okres 2π | Fale, oscylacje, rotacje |
| fract(x) | Część ułamkowa x — czyli x mod 1.0 | Powtarzające się kafelki, paski, siatki |
| length(v) | Odległość euklidesowa wektora od początku | Gradienty radialne, okręgi, pola odległości |
| smoothstep(a, b, x) | Krzywa S od 0→1, gdy x zmienia się od a→b | Wygładzone krawędzie, winiety |
| mix(a, b, t) | Interpolacja liniowa: a*(1-t) + b*t | Mieszanie kolorów, przejścia |
| abs(x) | Wartość bezwzględna | Symetria, składanie przestrzeni |
| floor(x) | Zaokrąglenie w dół do liczby całkowitej | Ekstrakcja identyfikatora komórki/siatki |
| clamp(x, lo, hi) | Ograniczenie x do [lo, hi] | Bezpieczne wartości koloru, głębi |
| dot(a, b) | Iloczyn skalarny dwóch wektorów | Oświetlenie, rzutowania, cieniowanie rozproszone |
| normalize(v) | Wektor jednostkowy w tym samym kierunku | Normalne, kierunki |
Rysowanie okręgu za pomocą SDF
Pole odległości znakowanej (SDF) to funkcja zwracająca wartości
ujemne wewnątrz kształtu i dodatnie na zewnątrz. Użyj
length + smoothstep, aby uzyskać ostry,
wygładzony okrąg:
Szum: tajny składnik
GLSL nie ma funkcji losowej, ale wiele shaderów udaje ją funkcją hash, a następnie wygładza w szum w stylu Perlina. Oto zwarta pseudolosowa funkcja hash używana w całej witrynie:
Nakładając kolejne oktawy szumu o rosnącej częstotliwości i malejącej amplitudzie, otrzymujemy fraktalny ruch Browna (fBm) — turbulentną, przypominającą chmury teksturę używaną w symulacjach oceanu i atmosfery.
Używanie uniformów: przekazywanie danych z JavaScript
Uniformy to wartości tylko do odczytu przekazywane z CPU
(JavaScript) do shadera w każdej klatce. W Three.js deklaruje się
je w obiekcie uniforms, a w GLSL słowem kluczowym
uniform:
Gdzie na tej stronie pojawiają się shadery
Debugowanie GLSL
GLSL nie ma console.log. Standardową techniką jest
wizualizacja danych jako koloru:
-
Wizualizuj float:
gl_FragColor = vec4(myValue, 0.0, 0.0, 1.0);— intensywność czerwieni pokazuje wartość. -
Wizualizuj vec2:
gl_FragColor = vec4(myVec, 0.0, 1.0); -
Sprawdź zakres: owiń podejrzane wartości w
fract(), aby uwidocznić przepełnienia jako nieciągłości. - Izoluj kroki: zakomentuj logikę i zwracaj wartości pośrednie jako kolor, aż znajdziesz uszkodzoną linię.
Wskazówka wydajnościowa: Na urządzeniach mobilnych
rozgałęzienia (instrukcje if) w shaderach mogą być
znacznie wolniejsze niż arytmetyka, ponieważ na niektórych GPU obie
gałęzie mogą wykonywać się równolegle. Preferuj
mix(a, b, step(...)) i clamp zamiast
warunków, kiedy tylko to możliwe.
Kolejne kroki
Gdy poczujesz się swobodnie z fragment shaderami, poznaj:
- Vertex shadery — przemieszczanie geometrii na GPU (siatka powierzchni oceanu).
- Render targets — zapis do tekstury i odczyt w kolejnej klatce (ping-pong reakcji-dyfuzji).
- Instanced mesh — jedna geometria, tysiące pozycji ustawionych przez atrybuty wierzchołków.
- Compute shadery (WebGPU) — dowolne programy GPU, nie tylko potok graficzny.