Witaj świecie!
Zazwyczaj przykład "Hello world!" stanowi pierwszy krok przy nauce nowego języka. Jest to prosty jednolinijkowy programy, który zwraca pełną entuzjazmu wiadomość powitalną i tym samym zapowiada nadchodzące przygody.
W świecie GPU renderowanie tekstu jest jednak zbyt skomplikowanym zadaniem dla żółtodzioba. Zamiast tego wybierzemy jasny, serdeczny kolor by wykrzyczeć naszą ekscytację!
Jeżeli czytasz tę książkę w przeglądarce: powyższy blok kodu jest interaktywny. Oznacza to, że możesz edytować dowolną linijkę kodu w celach eksploracyjnych. Shader kompiluje się na bieżąco, więc zmiany widoczne będą natychmiast. Spróbuj pozmieniać wartości w linijce 8.
Choć kod jest prosty, to możemy wyciągnąć z niego ważne wnioski:
-
Podobnie jak w C, GLSL ma jedną funkcje
main. Pod koniec zwraca ona kolor. -
Finalny kolor piksela przypisywany jest do zarezerowanej zmiennej globalnej
gl_FragColor. -
Ten C-podobny język ma wbudowane zmienne (jak
gl_FragColor), funkcje i typy. W aktualnym przykładzie występuje jedynie typvec4, oznaczający czterowymiarowy wektor zmiennoprzecinkowy (ang. "float vector"). Później zobaczymy również takie typy jakvec3,vec2oraz znajomefloat,intibool. -
Patrząc na typ
vec4, możemy wywnioskować, że jego cztery argumenty odnoszą się do kanałów CZERWONEGO, ZIELONEGO, NIEBIESKIEGO i ALPHA. Widać też, że jego wartości są znormalizowane, więc znajdują się w zakresie od0.0do1.0. Później zobaczymy, jak normalizowanie wartości pomaga w mapowaniu wartości między zakresami. - Kolejną ważną C-podobną własnością w tym przykładzie jest obecność makr preprocessora. Dzięki nim można definiować zmienne globalne za pomocą
#defineoraz operacje warunkowe za pomocą#ifdef("if defined"),#ifndef("if not defined") i#endif. Wszystkie makra zaczynają się od płotka#i ewaluowane są podczas procesu prekompilacji poprzedzającego kompilację. W naszym powyższym przykładzie linijka 2 kompilowana jest tylko wtedy, gdy zmiennaGL_ESjest zdefiniowana (co występuje na urządzeniach mobilnych i w przeglądarkach).
-
Typy zmiennoprzecinkowe są kluczowe w shaderach, więc ich poziom precyzji (ang. precision) jest kluczowy. Niższa precyzja oznacza szybsze renderowanie, ale kosztem jakości. Możesz być wybredny i określać precyzję każdej zmiennej zmiennoprzecinkowej z osobna. W linijce 2 (
precision mediump float;) ustawiamy średnią precyzję zmiennych zmiennoprzecinkowych ("mediump", bo "medium precision"). Możemy też ustawić ją jako niską (precision lowp float;) lub wysoką (precision highp float;). - Ostatni i chyba najważniejszy szczegół specyfikacji GLSL: nie ma gwaracji, że zmienne będą automatycznie castowane (np. z
intdofloatprzy dzieleniu liczby 5 przez 2). Producenci GPU mogą stosować przeróżne optymalizacje w kartach graficzncyh, ale muszą przy tym przestrzegać pewnych wytycznych. Automatyczne castowanie nie jest jednym z nich. W naszym przykładzievec4ma precyzję zmiennoprzecinkową i dlatego jego argumenty wymagająfloatów. Przezwyczaj się do stawiania kropek (.) wefloatach (1.lub1.0, a nie1), jeżeli nie chcesz spędzić godzin przy debugowaniu. Poniższy kod nie zawsze będzie, zatem, działał:
void main() {
gl_FragColor = vec4(1,0,0,1); // ERROR
}
Czas na ćwiczenia! Pamiętaj, że w wypadku błędu kompilacji pokaże się informacje o błędzie i linijce w której wystąpił, a kanwa zmieni kolor na biały.
-
Spróbuj zamienić
floaty nainty. Jeśli kod się nie kompiluje, to widocznie twoja karta graficzna tego nie toleruje -
Zakomentuj linię 8
- Stwórz osobną funckję, która zwraca dowolny kolor i użyj jej w
main(). Wskazówka: poniższy kod zwraca kolor czerwony:
vec4 red(){
return vec4(1.0,0.0,0.0,1.0);
}
- Jest wiele sposobów tworzenia typu
vec4- spróbuj je znaleźć. Jeden z nich wygląda tak:
vec4 color = vec4(vec3(1.0,0.0,1.0),1.0);
Choć przykład ten nie jest zbyt ekscytujący, ale stanowi ważną podstawę. W następnych rozdziałach zobaczymy, jak zmienić kolor piksela z pomocą inputu przestrzennego (położenie piksela na ekranie) i temporalnego (okres czasu od momentu załadowania się strony).