GLSL (ang. OpenGL_Shading Language lub GLslang), czyli język programów cieniowania, został wprowadzony w wersji 2.0 biblioteki OpenGL. Język ten umożliwia tworzenie zarówno programów cieniowania wierzchołków (ang. vertex shader) jak i programów cieniowania fragmentów (ang. fragment shader). Przed wprowadzeniem GLSL do podstawowej części biblioteki programy cieniowania opisane były łącznie w czterech rozszerzeniach: ARB shader objects, ARB vertex shader, ARB fragment shader i ARB - shading language 100.
Programy te opisują odpowiednio przekształcenia geometryczne wierzchołków oraz operacje na fragmentach i przejmują odpowiedzialność za niektóre elementy klasycznego potoku graficznego w bibliotece OpenGL - patrz rysunki 1 i 2.
Język GLSL jest wspólny składniowo dla obu rodzajów programów, ale ich odrębny charakter powoduje, że część operacji jest specyficzna tylko dla określonego rodzaju programu.
Mówiąc o języku GLSL trzeba jeszcze wspomnieć o niskopoziomowych programach cieniowania, które są dostępne jako rozszerzenia: ARB vertex - program i ARB fragment program. To historycznie pierwsza technika programowania potoku graficznego w bibliotece OpenGL (oczywiście poza rozszerzeniami opracowanymi odrębnie przez różnych producentów procesorów graficznych), którą można porównać do języka asemblera w procesorach. Niskopoziomowe programy cieniowania, choć ciągle popularne, najprawdopodobniej nigdy nie wejdą do specyfikacji biblioteki OpenGL.
Wersja 2.1 biblioteki OpenGL udostępnia język GLSL w wersji 1.20. Wersja 1.10 tego języka była dostępna w wersji 2.0 biblioteki OpenGL, a wersję 1.00 opisywały wymienione na wstępnie rozszerzenia. Wersję języka GLSL obsługiwaną przez daną implementację biblioteki OpenGL zawiera zmienna stanu
GL_SHADING_LANGUAGE_VERSION, której wartość można odczytać korzystając z funkcji glGetString.
Procesor wierzchołków i fragmentów
Za przetwarzanie wierzchołków i zarazem wykonywanie programów cieniowania wierzchołków odpowiedzialny jest tzw. procesor wierzchołków (ang vertex processor). Procesor wierzchołków przejmuje wykonanie następujących elementów potoku OpenGL:
Przetwarzaniem fragmentów za pomocą programów cieniowania fragmentów zajmuje się tzw. procesor fragmentów (ang. fragment processor). Procesor fragmentów wykonuje następujące operacje na fragmentach:
Kilka pierwszych generacji programowalnych procesorów graficznych, przeznaczonych na rynek konsumencki, zawierało w swojej strukturze odrębne jednostki będące odpowiednikami procesora wierzchołków i procesora fragmentów. Wprowadzony w 2006 roku procesor graficzny N80 firmy NVIDIA oraz w 2007 roku procesor R600 firmy AMD (ATI) zawierają już zunifikowane jednostki przetwarzania zwane procesorami strumieni (ang. stream procesors), które w zależności od potrzeb wykonują programy cieniowania wierzchołków, programy cieniowania fragmentów lub nowy rodzaj programów cieniowania - programy cieniowania geometrii (ang. geometry shader). Architektura ta nosi angielską nazwę unified shader.
Podstawy składni
Składnia GLSL jest oparta na językach C i C++, stąd osoba znająca te języki ma znacznie ułatwione zadane. Poniższy opis jest ograniczony do niezbędnego minimum, a Czytelnika zainteresowanego bliższymi szczegółami gramatyki języka GLSL zapraszam do lektury specyfikacji.
Zbiór znaków
Program w języku GLSL jest ciągiem znaków będących podzbiorem znaków ASCII. Podzbiór ten zawiera małe litery a-z, duże litery A-Z, podkreślenie , cyfry 0-9, oraz znaki: ., +, -, /, *, %, <, >, [, ], (, ), {, }, ∧, |, &, ˜, =, !, :, ;, , i ?. Znak # jest zarezerwowany do użycia przez preprocesor.
Ponadto program może zawierać tzw. białe spacje (ang. white space), w skład których wchodzą znaki: spacja, tabulator poziomy i pionowy, wysów strony (FF), powrót karetki (CR) i wysów wiersza (LF).
Program w języku GLSL tworzy tablica ciągów znaków, które mogą być podzielone na wiersze. Podział na wiersze nie ma znaczenia przy kompilacji programu, jest jednak elementem przydatnym np. do diagnostyki błędów. Wiersze numerowane są od 0.
Komentarze
GLSL wykorzystuje takie same komentarze jak język C++, tj. // rozpoczyna komentarz obowiązujący do końca linii, a komentarz dowolnej części programu określają pary znaków /* i */.
Słowa zarezerwowane
Język GLSL w wersji 1.20 rezerwuje następujące słowa: attribute, const, uniform, varying, centroid, break, continue, do, for, while, if, else, in, out, inout, float, int, void, bool, true, false, invariant, discard, return, mat2, mat3, mat4, mat2x2, mat2x3, mat2x4, mat3x2, mat3x3, mat3x4, mat4x2, mat4x3, mat4x4, vec2, vec3, vec4, ivec2, ivec3, ivec4, bvec2, bvec3, bvec4, sampler1D, sampler2D, sampler3D, samplerCube, sampler1DShadow, sampler2DShadow i struct.
Specyfikacja GLSL zawiera także wykaz słów zarezerwowanych do użycia w przyszłych wersjach tego języka: asm, class, union, enum, typedef, template, this, packed, goto, switch, default, inline, noinline, volatile, public, static, extern, external, interface, long, short, double, half, fixed, unsigned, lowp, mediump, highp, precision, input, output, hvec2, hvec3, hvec4, dvec2, dvec3, dvec4, fvec2, fvec3, fvec4, sampler2DRect, sampler3DRect, sampler2DRectShadow, sizeof, cast, namespace i using.
Identyfikatory
Identyfikatory, czyli nazwy zmiennych, funkcji, struktur i selektorów pól mogą składać się z małych i dużych liter a-z, A-Z, podkreślenia oraz cyfr 0-9. Pierwszym znakiem identyfikatora nie może być cyfra, a identyfikatory zaczynające się przedrostkiem gl są zarezerwowane do użycia przez OpenGL.
Preprocesor
Preprocesor w języku GLSL jest zbliżony do proprocesora wbudowanego w języku C++. Występujące różnice związane przede wszystkim są ze specyficznym przeznaczeniem języka GLSL.
Operatory
Generalnie semantyka preprocesora GLSL jest zbliżona do zdefiniowanej w języku C++. W tabeli 1 zestawiono wszystkie operatory preprocesora GLSL w kolejności od najwyższego (1) do najniższego (12) priorytetu.
Tabela 1: Pierwszeństwo operatorów preprocesora GLSL
Operator defined może być używany na dwa sposoby:
Instrukcje
Preprocesor w języku GLSL posiada następujące instrukcje: #, #define, #undef, #if, #ifdef, #ifndef, #else, #elif, #endif, #error, #pragma, #extension, #version i #line.
Pojedynczy znak # w linii jest ignorowany przez preprocesor. Instrukcje #define i #undef służą do definiowania i anulowania definicji makr. Ich funkcjonalność odpowiada analogicznym instrukcjom preprocesora języka C++. Także instrukcje #if, #ifdef, #ifndef, #else, #elif i #endif mają takie same znaczenie jak w języku C++, jednak preprocesor w języku GLSL nie wspiera stałych znakowych. Ponadto wyrażenia występujące w instrukcjach #if i #elif są ograniczone do wyrażeń operujących na stałych całkowitych oraz identyfikatorów obsługiwanych przez operator defined.
Instrukcja #error służy do generowania komunikatów diagnostycznych, które umieszczane są w dzienniku informacyjnym (logu) programu cieniowania.
Instrukcja #version określa wersję języka GLSL, w której napisany jest dany program cieniowania. Jest ona obowiązkowa od wersji 1.20 języka GLSL, a jej brak w programie oznacza, że jest on napisany w wersji 1.10 języka. Instrukcja #version musi wystąpić na samym początku programu cieniowania, za wyjątkiem komentarzy i tzw. białych spacji.
Kolejna instrukcja #pragma umożliwia kontrolę nad ustawieniami kompilatora zależnymi do implementacji. W przypadku, gdy dane wyrażenie użyte w poleceniu #pragma nie jest obsługiwane przez bieżącą implementację, całość polecenia jest ignorowana. Do przyszłych zastosowań zarezerwowane jest wyrażenie:
Programy napisane w języku GLSL mogą używać do optymalizacji następujących poleceń:
#pragma optimize(on)
#pragma optimize(off)
przy czym należy pamiętać, że domyślnie optymalizacja jest włączona dla każdego programu cieniowania. Zadaniem drugiej grupa poleceń:
#pragma debug(on)
#pragma debug(off)
jest zebranie podczas kompilacji programu cieniowania informacji przydatnych przy odpluskiwaniu. Domyślnie zbieranie tych informacji jest wyłączone.
Instrukcja #extension kontroluje współpracę programów cieniowania z rozszerzeniami języka GLSL obsługiwanymi przez implementację biblioteki OpenGL. Instrukcja ta występuje w dwóch postaciach:
#extension extension_name : behavior
#extension all : behavior
Pierwsza postać wykonuje operacje na wybranym rozszerzeniu o nazwie (nie identyfikatorze) extension name. Druga umożliwia globalne operacje na wszystkich dostępnych rozszerzeniach języka GLSL. Kwalifikator behavior określa wybrane zachowanie kompilatora GLSL i może przyjąć jedną z wartości:
Początkowy stan obsługi rozszerzeń kompilatora GLSL określa instrukcja:
Ostatnia nieopisana instrukcja preprocesora GLSL to #line, która jest pewną substytucją makra LINE , posiada dwie postacie:
#line line
#line line source_string_number
gdzie line i source string number są stałymi całkowitymi. Wywołanie instrukcji #line powoduje takie zachowanie kompilatora GLSL, jakby kompilowano wiersz tekstu źródłowego programu cieniowania o numerze line (powiększonego o jeden) w zbiorze wierszy o numerze source string number.
Wbudowane makra
GLSL posiada trzy standardowe makra: LINE , FILE i VERSION . Pierwsze z makr określa numer bieżącej linii zbioru wierzy tekstu źródłowego programu cieniowania. Drugie makro FILE zwraca numer bieżącego zbioru wierszy tekstu źródłowego programu cieniowania. Specyfika powyższych makr związana jest ze sposobem ładowania tekstu źródłowego programów cieniowania do obiektów programów. Ostatnie makro zwraca numer obsługiwanej wersji języka GLSL. W przypadku wersji 1.20 zwraca jest wartość całkowita 120.
Do przyszłego użycia przez preprocesor zarezerwowane są nazwy makr rozpoczynające się dwoma znakami podkreślenia ( ) oraz z przedrostkiem GL .
Podstawowe typy
Wszystkie zmienne w programie GLSL muszą być zadeklarowane przed pierwszym użyciem. W GLSL nie ma typów domyślnych, każda zmienna i funkcja musi mieć zadeklarowany typ oraz opcjonalne kwalifikatory. Trzeba także podkreślić, że GLSL jest językiem o silnej typizacji i niewielkich możliwościach automatycznej konwersji typów.
Podstawowe typy danych w języku GLSL przedstawiono w tabeli 2. Zauważmy, że GLSL nie posiada żadnego odpowiednika typów wskaźnikowych z C/C++. Macierze prostokątne zostały wprowadzone w wersji 1.20 języka GLSL.
Tabela 2: Podstawowe typy GLSL
Niejawne konwersje typów
Jak napisaliśmy na wstępie GLSL ma niewielkie możliwości automatycznej konwersji typów. Sprowadza się to do konwersji z typu int do typu float oraz konwersji wektorów z elementami całkowitymi (ivec2, ivec3 i ivec4) na wektory z liczbami zmiennoprzecinkowymi o analogicznych wymiarach (vec2, vec3 i vec4).
Zakres widoczności zmiennych
Widoczność zmiennej zależy od miejsca jej deklaracji. Zmienne zadeklarowane poza funkcjami mają zasięg globalny rozpoczynający się od miejsca deklaracji.
Konstruktory
Konstruktory służą do zainicjowania wartości zmiennej i wykorzystują składnię identyczną jak przy wywołaniu funkcji, przy czym nazwą konstruktora jest podstawowy typ danych lub nazwa struktury zdefiniowanej przez użytkownika. Konstruktory mogą być także wykorzystane do wymuszenia konwersji typów danych, zbudowania większego typu z mniejszych (np. wektory), bądź zredukowania większego typu do mniejszego.
Oto dostępne konstruktory dla typów skalarnych:
int( bool )
int( float )
float( bool )
float( int )
bool( float )
bool( int )
W przypadku konwersji z typu float do typu int część zmiennoprzecinkowa jest odrzucana. Konwersja z typów int i float do typu bool odbywa się w taki sposób, że wartość 0 jest zamieniana na false, a każda wartość różna od 0 na true. Konwersja w drugą stronę z typu bool do typów int i float działa tak, że wartość false jest zamieniana na 0, a wartość true na 1.
Konstruktory skalarne można także używać z typami nieskalarnymi. Konwersji podlega wówczas pierwszy element typu nieskalarnego.
Typ void
Typ void jest przeznaczony do użycia dla funkcji nie zwracających żadnej wartości. GLSL nie ma domyślnego typu zwracanego przez funkcje.
Typ bool
Zmienne typu logicznego bool przyjmują jedną z dwóch wartości: true lub false i nie muszą być bezpośrednio wspierane przez procesor graficzny. Deklaracje i opcjonalne inicjalizacje zmiennych tego typu są identyczne jak w języku C++. Poniżej przykład:
bool success;
bool done = false;
Typ int
Typ int, to całkowity ze znakiem o minimalnej precyzji co najmniej 16 bitów (bez znaku). Implementacja może stosować liczby o większej precyzji, nie jest to jednak rozwiązanie przenośne. Podobnie jak w przypadku typu bool liczby typu całkowitego nie musza być bezpośrednio wspierane przez procesor graficzny.
Zmienne typu int mogą być inicjalizowane za pomocą literałów o podstawie dziesiętnej, ósemkowej i szesnastkowej. Metody zapisu literałów całkowitych są identyczne jak w języku C. Liczba ósemkowa rozpoczyna się cyfrą 0, a liczba szesnastkowa stałą 0x lub 0X. Oto przykładowe deklaracje i inicjalizacje zmiennych typu int:
int i,
j = - 42;
int o = 07;
int h = 0xA;
Typ float
Typ float to liczby zmiennoprzecinkowe o pojedynczej precyzji. GLSL akceptuje liczby w formacie IEEE (analogicznie jak w języku C), przy czym wewnętrzna reprezentacja nie musi być zgodna z tym formatem. Od wersji
1.20 jeżyka GLSL liczba typu float może zawierać przyrostek f lub F. Poniżej przykłady użycia typu float:
float a,
b = 1.5;
float c = - 5.0f;
Typy wektorowe
Typy wektorowe obejmują wektory dwu, trój i czterowymiarowe z elementami typu bool, int i float. Dostęp do elementów wektora możliwy jest na kilka sposobów. Pierwszy jest taki sam jak w przypadku typów tablicowych w językach C i C++ (elementy wektora numerowane są od 0). Alternatywna metoda jest skorzystanie z selektora . (kropka) i następujących nazw pól: {x, y, z, w}, {r, g, b, a} lub {s, t, p, q}. Nazwy te mogą ułatwić korzystanie ze zmiennych wektorowych reprezentujących różnego rodzaju parametry programu. Pola te można dodatkowo łączyć (wyłącznie w zakresie danej grupy) uzyskując mniejsze wektory:
vec4 v4;
v4.rgba;
v4.rgb;
v4.st;
Możliwych konstruktorów typów wektorowych jest dość dużo, stąd poniżej przedstawiamy jedynie wybrane przykłady:
vec3( float )
vec4( ivec4 )
vec2( vec3 )
vec3( float, vec2 )
vec4( vec3, float )
Oto kilka przykładowych deklaracji zmiennych wektorowych wraz z różnorodnymi konstruktorami:
vec4 pos = vec4( 1.0, 2.0, 3.0, 4.0 );
pos.xw = vec2( 5.0, 6.0 );
vec4 swiz = pos.wzyx;
vec3 rgb = vec3( color );
vec4 rgba = vec4( 1.0 );
Zwróćmy szczególną uwagę na te przykłady konstruktorów, w których różna jest kolejność składników, oraz te, gdzie nazwy pól zostały wykorzystane po lewej stronie wyrażenia, określając zapisywane składowe wektora.
Typy macierzowe
Wszystkie typy macierzowe w GLSL zawierają elementy typu float. Macierze prostokątne zostały wprowadzone w wersji 1.20 języka GLSL (w nazwie matmxn m oznacza ilość kolumn, a n ilość wierszy). Podobnie jak w przypadku wektorów, typy macierzowe także posiadają wiele możliwych konstruktorów. Oto wybrane przykłady:
mat2( float )
mat2( vec2, vec2 )
float, float );
float, float, float,
mat4( float, float, float, float,
mat2x3( vec2, float,
mat3x3( mat4x4 )
mat2x3( mat4x2 )
mat4x4( mat3x3 )
Odwoływanie się do poszczególnych elementów macierzy możliwe jest przy zastosowaniu operatora indeksowania tablicy. Jeżeli macierz traktujemy jako tablicę jednowymiarową, to jej elementami są wektory odpowiadające poszczególnym kolumnom macierzy. Podobnie jak w językach C i C++ indeksy elementów macierzy numerowane są od 0. Poniżej kilka przykładowych operacji na zmiennych macierzowych:
mat4 m;
m[ 1 ] = vec4( 2.0 );
m[ 0 ][ 0 ] = 1.0;
m[ 2 ][ 3 ] = 2.0;
Zauważmy, że dostępność wektorów i macierzy jako typów podstawowych znacznie ułatwia programowanie operacji graficznych, które standardowo korzystają z tego rodzaju danych.
Uchwyty tekstur
Uchwyty tekstur umożliwiają dostęp do danych tekstury jedno, dwu i trójwymiarowych oraz do tekstur sześciennych. Uchwyty stosowane są w funkcjach próbkujących tekstury, które zostaną przedstawione dalej.
Struktury
Struktury definiowane są analogicznie jak w języku C z wykorzystaniem słowa zarezerwowanego struct, przy czym deklaracja zmiennej typu strukturalnego nie wymaga użycia słowa struct. Wszystkie składowe struktury muszą być wcześniej zdefiniowane. GLSL nie dopuszcza struktur anonimowych ani zagnieżdżonych.
Oto definicja przykładowej struktury zawierającej dwa pola i jednocześnie deklaracja zmiennej tego typu:
struct light
{
float intensity;
vec3 position;
} lightVar;
light lightVar2;
Argumenty konstruktorów struktur muszą być tego samego typu i występować w takiej samej kolejności jak definicje pól w strukturze. Oczywiście możliwe są opisane wcześniej konwersje typów. Konstruktor struktury ma taką samą nazwę jak sama struktura:
light lightVar1 = light( 3.0, vec3( 1.0, 2.0, 3.0 ) );
Dostęp do poszczególnych elementów struktury, podobnie jak w językach C i C++ zapewnia operator . (kropka).
Tablice
Tablice definiowane są analogicznie jak w języku C zużyciem nawiasów kwadratowych [ ], przy czym GLSL obsługuje wyłącznie tablice jednowymiarowe. Rozmiar tablicy musi być określony stałym wyrażeniem o wartości większej od 0, przy czym podanie rozmiaru tablicy nie jest obowiązkowe. Jeżeli tablica jest indeksowana dowolną zmienną lub jest przekazywana jako parametr funkcji, jej rozmiar musi być z góry określony. Przekroczenie zakresu tablicy jest zachowaniem niezdefiniowanym.
Oto przykładowe deklaracje tablic:
float frequencies[ 3 ];
light lights[];
oraz przykładowe konstruktory tablic:
const float c[ 3 ] = float[ 3 ]( 5.0, 7.2, 1.1 );
const float d[ 3 ] = float[]( 5.0, 7.2, 1.1 );
...
float a[ 5 ] = float[ 5 ]( g, 1, g, 2.3, g );
float b[ 3 ];
b = float[ 3 ]( g, g + 1.0, g + 2.0 );
Konstruktor tablicy powinien wskazywać rozmiar zgodny z rozmiarem tablicy. W przypadku niewskazania rozmiaru tablicy w konstruktorze, tablica otrzymuje rozmiar równy ilości elementów konstruktora. Pobranie rozmiaru tablicy umożliwia funkcja length:
która, zauważmy, korzysta z operatora . (kropka), czyli takiego samego jak przy dostępie do pól struktur.
Operatory i wyrażenia
Operatory języka GLSL przedstawiono w tabeli 3. Jak widzimy, część operatorów jest zarezerwowana. Operatory języka GLSL zestawiono w kolejności od najwyższego (1) do najniższego (17) priorytetu. Zauważmy, że języka GLSL nie zawiera żadnych operatorów adresowych i wskaźnikowych.
Tabela 3: Pierwszeństwo operatorów języka GLSL
Operacje na strukturach, poza już wspomnianym operatorem . (kropka), mogą wykonywać następujące operatory: ==, ! = (porównanie) oraz = przypisanie. Ten sam zestaw operatorów, poza operatorem dostępu do elementu [], jest dopuszczalny także dla tablic. Oczywiście operatory przypisania i porównania wymagają do poprawnego działania zgodności typów obu operandów.
Poza nielicznymi wyjątkami w przypadku macierzy i wektorów operatory działają na wszystkich składowych zmiennej. Przykładowo:
vec3 v, u;
float f;
v = u + f;
jest równoważne z następującymi wyrażeniami:
v.x = u.x + f; v.y = u.y + f; v.z = u.z + f;
I następny przykład:
jst równoważny zapisom:
w.x = v.x + u.x; w.y = v.y + u.y; w.z = v.z + u.z;
Wyjątkiem są przypadki, gdy mnożymy macierz przez wektor, wektor przez macierz oraz macierz przez macierz. Wykonywane są wówczas odpowiednie operacje algebraiczne:
vec3 v, u;
mat3 m;
u = v * m;
jest równoważne:
I drugi przykład:
jest równoważny:
u.x = m[ 0 ].x * v.x + m[ 1 ].x * v.y + m[ 2 ].x * v.z; u.y = m[ 0 ].y * v.x + m[ 1 ].y * v.y + m[ 2 ].y * v.z; u.z = m[ 0 ].z * v.x + m[ 1 ].z * v.y + m[ 2 ].z * v.z;
Trzeci i ostatni przykład:
odpowiada wyrażeniom:
r[ 0 ].x = m[ 0 ].x * n[ 0 ].x + m[ 1 ].x * n[ 0 ].y + m[ 2 ].x * n[ 0 ].z; r[ 1 ].x = m[ 0 ].x * n[ 1 ].x + m[ 1 ].x * n[ 1 ].y + m[ 2 ].x * n[ 1 ].z; r[ 2 ].x = m[ 0 ].x * n[ 2 ].x + m[ 1 ].x * n[ 2 ].y + m[ 2 ].x * n[ 2 ].z; r[ 0 ].y = m[ 0 ].y * n[ 0 ].x + m[ 1 ].y * n[ 0 ].y + m[ 2 ].y * n[ 0 ].z; r[ 1 ].y = m[ 0 ].y * n[ 1 ].x + m[ 1 ].y * n[ 1 ].y + m[ 2 ].y * n[ 1 ].z; r[ 2 ].y = m[ 0 ].y * n[ 2 ].x + m[ 1 ].y * n[ 2 ].y + m[ 2 ].y * n[ 2 ].z; r[ 0 ].z = m[ 0 ].z * n[ 0 ].x + m[ 1 ].z * n[ 0 ].y + m[ 2 ].z * n[ 0 ].z; r[ 1 ].z = m[ 0 ].z * n[ 1 ].x + m[ 1 ].z * n[ 1 ].y + m[ 2 ].z * n[ 1 ].z; r[ 2 ].z = m[ 0 ].z * n[ 2 ].x + m[ 1 ].z * n[ 2 ].y + m[ 2 ].z * n[ 2 ].z;
Oczywiście warunkiem wykonalności operacji mnożenia macierzy przez wektor, wektora przez macierz oraz macierzy przez macierz jest spełnienie odpowiednich warunków algebraicznych związanych z rozmiarami mnożonych wektorów i macierzy.
Kwalifikatory typów
Zmienne można deklarować z opcjonalnymi kwalifikatorami. Dostępne kwalifikatory omawiamy poniżej. Domyślnie zmienne nie otrzymują żadnego kwalifikatora. Niekwalifikowane zmienne globalne i lokalne umożliwiają jedynie odczyt i zapis przydzielonego im obszarowi pamięci.
Kwalifikatory można podzielić na określające sposób dostępu do pamięci (const, attribute, uniform i varying) oraz określające rodzaj dostępu do parametrów funkcji (in, out i inout). Z niektórymi kwalifikatorami może występować dodatkowy kwalifikator invariant wspierający optymalizację obsługi jednakowych zmiennych w niezależnych programach cieniowania. W przyszłości możliwe jest dodanie kwalifikatora precision i innych kwalifikatorów związanych z dokładnością zmiennych.
Kolejność użycia kwalifikatorów jest następująca: invariant poprzedza kwalifikator dostępu do pamięci; kwalifikator dostępu do pamięci poprzedza kwalifikator dostępu do parametrów funkcji.
const
Kwalifikator const określa wartość stałą inicjalizowaną podczas deklaracji i może być użyty z każdym podstawowym typem danych. Zmienne z kwalifikatorem const przeznaczone są tylko do odczytu. Ponadto kwalifikator const używany jest w deklaracjach tych argumentów funkcji, które nie ulegają zmianie w trakcie jej działania.
Wyrażeniem stałym może być:
attribute
Kwalifikator attribute dostępny jest wyłącznie w programach cieniowania wierzchołków i określa zmienne - atrybuty przekazywane przez OpenGL dla każdego wierzchołka. Zmienne z kwalifikatorem attribute przeznaczone są tylko do odczytu i muszą być zmiennymi globalnymi. GLSL ogranicza typ zmiennych z kwalifikatorem attribute do liczb zmiennoprzecinkowych oraz wektorów i macierzy z liczbami zmiennoprzecinkowymi. Nie są dopuszczalne tablice lub struktury z tym kwalifikatorem.
Standardowe atrybuty wierzchołków, np. podstawowy i drugorzędny kolor, współrzędne, wektor normalny, dostępne są poprzez wbudowane atrybuty. Umożliwia to łatwą integrację pomiędzy klasyczną częścią potoku OpenGL a programem cieniowania wierzchołków. Wbudowane atrybuty programów cieniowania wierzchołków opisujemy dalej.
uniform
Kwalifikator uniform określa globalne zmienne jednorodne, których wartość dostępna jest zarówno dla programu cieniowana wierzchołków jak i programu cieniowania fragmentów. Zmienne jednorodne są stałe w obrębie programów cieniowania a ich wartość definiowana jest przez wywołanie odpowiednich funkcji biblioteki OpenGL.
Kwalifikator uniform może być użyty z dowolnym typem danych w tym także z typami danych zdefiniowanymi przez użytkownika.
varying
Zmienne z kwalifikatorem varying służą do komunikacji pomiędzy programami cieniowania wierzchołków a programami cieniowania fragmentów. Program cieniowania wierzchołków oblicza dla każdego wierzchołka wartość udostępnianej zmiennej, a program cieniowania fragmentów otrzymuje te wartości interpolowane dla każdego fragmentu przy użyciu korekty perspektywy. Zmienna z kwalifikatorem varying jest dla programu cieniowania fragmentów zmienną tylko do odczytu.
Kwalifikator varying może być używany wyłącznie z liczbami zmiennoprzecinkowymi oraz wektorami i macierzami z liczbami zmiennoprzecinkowymi. Dopuszczalne są także tablice zmiennych tego typu. Nie są natomiast dopuszczalne struktury z tym kwalifikatorem.
W przypadku, gdy żaden program cieniowania wierzchołków nie jest aktywny nieprogramowalna część potoku OpenGL obliczy wartości wbudowanych zmiennych udostępnianych, tak aby mogły być użyte w programach cieniowania fragmentów. Analogicznie, jeżeli nie jest aktywny żaden program cieniowania fragmentów, program cieniowania wierzchołków jest odpowiedzialny za obliczenie wartości zmiennych udostępnianych, które są dalej użyte przez nieprogramowalny potok przekształceń fragmentów.
Wspomniana wyżej interpolacja wartości zmiennych z kwalifikatorem varying przy wyłączonym wielopróbkowaniu obliczana jest dla środka piksela. Jednak w przypadku, gdy włączone jest wielopróbkowanie, interpolowana wartość może pochodzić z jednej z próbek, ze środka piksela bądź centroidu (patrz rysunek 3). Może to być przyczyną różnego rodzaju przekłamań (arfetaktów) w renderingu.
Problem ten rozwiązuje dodany w wersji 1.20 języka GLSL kwalifikator centroid, występujący łącznie z varying, który w przypadku włączonego wielopróbkowania wymusza interpolację przy użyciu centroidu. Jeżeli wielopróbkowanie jest wyłączone kwalifikator centroid jest ignorowany.
in
Kwalifikator in określa parametry wejściowe funkcji. Wszelkie zmiany tego parametru wewnątrz funkcji nie mają wpływu na jego wartość poza funkcją. Jest to domyślny kwalifikator argumentów funkcji.
out
Kwalifikator out określa parametr wyjściowy funkcji. Parametry z tym kwalifikatorem nie wymagają przekazywania do funkcji żadnej konkretnej wartości.
inout
Kwalifikator inout określa, że parametr funkcji jest parametrem zarówno wejściowym jak i wyjściowym. Wszelkie zmiany wartości tego parametru będą miały wpływ na jego wartość poza funkcją.
invariant
Ostatni opisywany kwalifikator to dodany w wersji 1.20 GLSL kwalifikator invariant, którego zadaniem jest ułatwienie optymalizacji obsługi jednakowych inwariantnych (niezmienniczych) zmiennych w niezależnych programach cieniowania.
Kwalifikator invariant mogą otrzymać zarówno zmienne wbudowane jak i zmienne zdefiniowane przez użytkownika. Kwalifikator można także użyć w innym miejscu niż deklaracja zmiennej:
lub jednocześnie z jej deklaracją:
invariant varying vec3 Color;
Z uwagi na swoją specyfikę kwalifikator invariant mogą otrzymać jedynie wyjściowe z programu cieniowania wierzchołków. Dotyczy do w praktyce zdefiniowanych przez użytkowniak zmiennych z kwalifikatorem varying (zmienne udostępniane) oraz specjalnych zmiennych programów cieniowania wierzchołków gl_Position i gl_PointSize. Kwalifikator invariant musi zostać wyspecyfikowany przed kwalifikatorem varying.
W przypadku niezmienniczych zmiennych udostępnianych, które deklarowane są zarówno w programie cieniowania wierzchołków jak i w programie cieniowania fragmentów, obie deklaracje powinny zostać poprzedzone kwalifikatorem invariant, lub kwalifikator ten musi zostać dodany przed pierwszym użyciem zmiennej. Zagwarantowanie przyjęcie obsługi zmiennej inwariantnej w obu programach cieniowania wymaga ponadto spełnienia szeregu dodatkowych warunków (wszystkie wymienia specyfikacja języka GLSL). Wymienimy tylko jeden, stanowiący, że cały przepływ danych związany ze zmiennymi inwariantnymi musi zawierać się z jednej jednostce kompilacji (tzw. obiekcie programów cieniowania, które poznamy w następnym odcinku kursu).
Domyślnie wszystkie zmienne wyjściowe są zdefiniowane bez kwalifikatora invariant. Można jednak przed deklaracją zmiennych użyć następującej instrukcji preprocesora:
#pragma STDGL invariant(all)
która wymusza domyślne zastosowanie kwalifikatora invariant dla wszystkich zmiennych wyjściowych. Specyfika tej instrukcji wymaga umieszczenia wyłącznie w programie cieniowania wierzchołków.
Generalnie kwalifikator invariant gwarantuje swobodę optymalizacji dla kompilatora GLSL, ale wydajność może spaść przy jego stosowani. Stąd specyfikacja GLSL zaleca użycie powyższej instrukcji preprocesora do testów wydajności i na podstawie tego podejmowanie indywidualnych decyzji dotyczących stosowania tego kwalifikatora.
Instrukcje i struktura programu
Podstawowymi blokami w języku GLSL są:
Definiowanie funkcji
Program cieniowania w języku GLSL jest zasadniczo sekwencją definicji zmiennych globalnych i funkcji. Deklaracja funkcji (prototyp) wygląda następująco:
typZwracany nazwaFunkcji( typ0 arg0, typ1 arg2,..., typN argN );
natomiast definicja funkcji przyjmuje postać:
typZwracany nazwaFunkcji( typ0 arg0, typ1 arg2,..., typN argN )
{
}
Każda funkcja musi mieć określony typ zwracany. Także każdy z argumentów funkcji musi mieć określony typ oraz opcjonalny kwalifikator: in, out, inout i/lub const. Jago argumentu funkcji można użyć tablicy, jednak tablica nie może być typem zwracanym przez funkcję. Wszystkie funkcje, przed pierwszym użyciem muszą być zadeklarowane lub zdefiniowane. Jeżeli funkcja nie zwracają żadnych wartości musi być zadeklarowana jako void.
Język GLSL dopuszcza przeciążanie funkcji, czyli wykorzystywanie jednej nazwy dla funkcji różniących się listą argumentów. Oczywiście poza zgodnością nazwy funkcji i typów jej argumentów musi występować zgodność typu zwracanego oraz zgodność kwalifikatorów argumentów. Przeciążanie funkcji jest szeroko wykorzystywane przez funkcje wbudowane. Przykładem może być funkcja dot obliczająca iloczyn skalarny:
float dot( float x, float y ); float dot( vec2 x, vec2 y ); float dot( vec3 x, vec3 y ); float dot( vec4 x, vec4 y );
Punktem wejściowym każdego programu cieniowania jest funkcja main. Funkcja ta nie posiada argumentów i nie zwraca żadnej wartości:
Wywoływanie funkcji
Przy wywołaniu funkcji argumenty wejściowe kopiowane są w momencie wywołania, natomiast argumenty wyjściowe kopiowane są przed zakończeniem działania funkcji. Do określenia, który z parametrów jest wejściowy, wyjściowy lub wejściowy i wyjściowy, służą opisywane wcześniej kwalifikatory: in, out oraz inout. Przy braku kwalifikator przyjmowany jest in.
GLSL dopuszcza możliwość zmiany w ciele funkcji wartości argumentu z kwalifikatorem in, gdyż modyfikacji podlega jedynie jego lokalna kopia. Wyjątek stanowią argumenty z dodatkowym kwalifikatorem const, który, co oczywiste, nie może być użyty przy argumentach z kwalifikatorem out oraz inout.
Specyfikacja języka GLSL nie dopuszcza możliwości rekurencyjnego wywoływania funkcji.
Instrukcje sterujące
Instrukcje sterujące if oraz if/else mają taką samą konstrukcję jak w językach C i C++. Wyrażeniem warunkowym w nim występującym może być dowolne wyrażenie typu logicznego bool. Wyjątkiem jest wyłączenie typów wektorowych jako wyrażenia w instrukcji if. Instrukcje sterujące mogą być zagnieżdżone.
Pętle
Pętle for, while oraz do/while mają w języku GLSL identyczną konstrukcję jak w językach C i C++. Wyrażenie warunkowe w nich występujące musi być wyrażeniem typu logicznego bool. Pętle mogą być zagnieżdżone.
GLSL formalnie dopuszcza pętle nieskończone, jednak konsekwencje związane z ich uruchomieniem zależne są od implementacji.
Skoki
GLSL ddefiniuje kilka rodzajów instrukcji skoków: continue, break, return (w tym także ze możliwością zwrócenia wartości) oraz discard. Dwie pierwsze instrukcje mają zastosowanie wyłącznie w pętlach, a ich działanie jest takie samo jak w językach C i C++. Instrukcja discard dostępna jest wyłącznie w programach cieniowania fragmentów, a jej wywołanie spowoduje odrzucenia aktualnie przetwarzanego fragmentu i jednocześnie brak aktualizacji zawartości bufora ramki.
Instrukcja return powoduje natychmiastowe opuszczenie funkcji. Wywołanie return w programie cieniowania fragmentów jest równoważne wykonaniu instrukcji discard.
Zauważmy, że pomowo zarezerwowania słowa goto, język GLSL nie zawiera obsługi tej instrukcji.
Wbudowane zmienne
Programy cieniowania komunikują się z nieprogramowalną częścią potoku OpenGL za pomocą wbudowanych zmiennych. Z uwagi na odmienne charaktery wykonywanych zadań programy cieniowania wierzchołków i programy cieniowania fragmentów mają częściowo odrębne zestawy wbudowanych zmiennych.
Specjalne zmienne programów cieniowania wierzchołków
Programy cieniowania wierzchołków mają dostępne trzy globalne zmienne specjalne:
vec4 gl_Position; float gl_PointSize; vec4 gl_ClipVertex;
Zmienna gl_Position określa pozycję wierzchołka prymitywu po przekształceniach. Jest to zmienna, której wartość musi obliczyć i zapisać każdy program cieniowania wierzchołków. Jeżeli przekształcenie pozycji wierzchołka ma odpowiadać przekształceniom klasycznego potoku OpenGL obliczenie wartości zmiennej gl_Position sprowadza się do przemnożenia macierzy rzutowania i macierzy modelowania przez współrzędne wierzchołka:
void main()
{
gl_Position = gl_ProjectionMatrix * gl_ModelViewMatrix *
gl_Vertex;
}
Ponieważ GLSL posiada wbudowaną specjalną zmienną gl_ModelViewProjectionMatrix zawierającą wynik mnożenia macierzy rzutowania i macierzy modelowania, wartość zmiennej gl_Position można także obliczyć w następujący sposób:
void main()
{
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
Jednak najlepszym (i prawdopodobnie najszybszym sposobem) obliczenia „klasycznej” wartości zmiennej gl_Position jest użycie funkcji ftransform, która wyknuje wszystkie niezbędne operacje:
void main()
{
gl_Position = ftransform();
}
Przedstawione powyżej trzy programy są najprostszymi możliwymi programami cieniowania wierzchołków, oczywiście przy założeniu, że program ten wykonuje przekształcenia wierzchołków zgodnie z klasycznym potokiem OpenGL. Warto jednak zwrócić uwagę, że jedynie użycie funkcji ftransform daje pełną gwarancje wykonania przekształceń wierzchołków w dokładnie taki sam sposób jak w potoku nieprogramowalnym. Z uwagi na stosowaną optymalizację oraz skończoną precyzję liczb zmiennoprzecinkowych dwa pierwsze programy mogą dać nieco odmienne rezultaty.
Zmienna gl_PointSize określa (w pikselach) rozmiar rasteryzowanych punktów, a zmienna gl_ClipVertex zawiera współrzędne używane z płaszczyznami obcinania zdefiniowanymi przez użytkownika. Jeżeli wartości tych zmiennych nie zostaną określone ich wartość pozostaje niezdefiniowana.
Specjalne zmienne programów cieniowania fragmentów
Programy cieniowania dostępne mają dostępnych pięć globalnych zmiennych specjalnych:
vec4 gl_FragCoord; bool gl_FrontFacing; vec4 gl_FragColor;
vec4 gl_FragData[ gl_MaxDrawBuffers ];
float gl_FragDepth;
Dwie pierwsze zmienne są zmiennymi tylko do odczytu. Zmienna gl_FragCoord zawiera współrzędne ,,x, y, z, 1 położenia fragmentu w oknie renderingu. Wartość głębi (współrzędna z) program cieniowania fragmentów może wykorzystać do własnych obliczeń tej wartości. Druga zmienna do odczytu gl_FrontFacing zawiera informację, czy dany fragment należy do prymitywu zwróconego przodem do obserwatora (wartość true) lub tyłem (wartość false).
Program cieniowania fragmentów musi w celu komunikacji z pozostałą częścią potoku OpenGL zapisać wartości składowych koloru fragmentu (zmienna gl_FragColor) oraz wartość głębi fragmentu (zmienna gl_FragDepth). Zapis wartości głębi fragmentu jest opcjonalny, ale jeżeli jest wykonywany, to obowiązkowo dla każdego fragmentu. Jeżeli program cieniowania fragmentów nie zapisuje wartości głębi, jest ona wyznaczana automatycznie przez OpenGL.
Zapis koloru fragmentu nie jest opcjonalny, stąd chyba najprostszym przykładem programu cieniowania fragmentów jest program kolorujący wierzchołki prymitywów na określony kolor:
void main()
{
gl_FragColor = vec4( 1.0, 0.0, 0.0, 1.0 );
}
Jeżeli program korzysta z techniki wielokrotnych docelowych buforów koloru (rozszerzenie ARB draw buffers) zapis koloru fragmentu dokonywany jest do odpowiednich elementów tablicy gl_FragData. Program cieniowania fragmentów nie może jednocześnie zapisywać wartości zmiennej gl_FragColor i elementów tablicy gl_FragData. To samo ograniczenie dotyczy również wszystkich programów cieniowania fragmentów, które są razem skonsolidowane.
Program cieniowania fragmentów może także odrzucić wybrany fragment z dalszego potoku renderingu. Służy do tego instrukcja discard. Wartości wszystkich zmiennych wyjściowych pozostają wówczas niezdefiniowane.
Wbudowane atrybuty programów cieniowania wierzchołków
Programy cieniowania wierzchołków mają dostęp do wszystkich atrybutów wierzchołków definiowanych w nieprogramowalnej części potoku OpenGL. Są to kolejno: składowe podstawowego koloru wierzchołka, składowe drugorzędnego koloru wierzchołka, współrzędne wektora normalnego, współrzędne wierzchołka, współrzędne tekstur dla kolejnych jednostek teksturujących oraz współrzędne mgły. Nazwy i typy wymienionych atrybutów przedstawione są poniżej:
attribute vec4 gl_Color;
attribute vec4 gl_SecondaryColor; attribute vec3 gl_Normal; attribute vec4 gl_Vertex; attribute vec4 gl_MultiTexCoord0; attribute vec4 gl_MultiTexCoord1; attribute vec4 gl_MultiTexCoord2; attribute vec4 gl_MultiTexCoord3; attribute vec4 gl_MultiTexCoord4; attribute vec4 gl_MultiTexCoord5; attribute vec4 gl_MultiTexCoord6; attribute vec4 gl_MultiTexCoord7; attribute float gl_FogCoord;
Wbudowane stałe
Przedstawione poniżej wbudowane stałe dostępne są zarówno dla programów cieniowania wierzchołków jak i programów cieniowania fragmentów. Podane wartości stałych odpowiadają minimalnym wymaganiom określonym przez specyfikację biblioteki OpenGL. W komentarzach oprócz opisu stałej dodano także numer wersji OpenGL, w której wprowadzono jej klasyczną (objętą nieprogramowalnym potokiem) wersję. Ponadto komentarze zawierają nazwy zmiennych stanu odpowiadających opisywanym stałym.
const int gl_MaxLights = 8;
const int gl_MaxClipPlanes = 6;
const int gl_MaxTextureUnits = 2;
const int gl_MaxTextureCoords = 2;
const int gl_MaxVertexAttribs = 16;
const int gl_MaxVertexUniformComponents = 512;
const int gl_MaxVaryingFloats = 32;
const int gl_MaxVertexTextureImageUnits = 0;
const int gl_MaxCombinedTextureImageUnits = 2;
const int gl_MaxTextureImageUnits = 2;
const int gl_MaxFragmentUniformComponents = 64;
const int gl_MaxDrawBuffers = 1;
Wbudowane zmienne jednorodne
Wbudowane zmienne jednorodne opisują bieżącą konfigurację potoku OpenGL i są dostępne zarówno dla programów cieniowania wierzchołków jak i programów cieniowania fragmentów. Można je podzielić na wiele grup. Opis poszczególnych zmiennych zawarto w poniższych komentarzach.
uniform mat4 gl_ModelViewMatrix;
uniform mat4 gl_ProjectionMatrix;
uniform mat4 gl_TextureMatrix[ gl_MaxTextureCoords ];
uniform mat4 gl_ProjectionMatrixInverse;
uniform mat4 gl_TextureMatrixInverse[ gl_MaxTextureCoords ];
uniform mat4 gl_ProjectionMatrixTranspose;
uniform mat4 gl_ModelViewProjectionMatrixTranspose;
uniform mat4 gl_TextureMatrixTranspose[ gl_MaxTextureCoords ];
uniform mat4 gl_ModelViewProjectionMatrixInverseTranspose;
uniform mat4 gl_TextureMatrixInverseTranspose[ gl_MaxTextureCoords ];
uniform float gl_NormalScale;
struct gl_DepthRangeParameters
{
float near;
};
uniform gl_DepthRangeParameters gl_DepthRange;
uniform vec4 gl_ClipPlane[ gl_MaxClipPlanes ];
struct gl_PointParameters
{
float size;
float sizeMax;
float fadeThresholdSize;
};
uniform gl_PointParameters gl_Point;
struct gl_MaterialParameters
{
vec4 emission;
vec4 ambient;
vec4 diffuse;
float shininess;
};
uniform gl_MaterialParameters gl_FrontMaterial;
uniform gl_MaterialParameters gl_BackMaterial;
struct gl_LightSourceParameters
{
vec4 ambient;
vec4 position;
vec3 spotDirection;
float spotExponent;
float spotCutoff;
float spotCosCutoff;
float quadraticAttenuation;
};
uniform gl_LightSourceParameters gl_LightSource[ gl_MaxLights ];
struct gl_LightModelParameters
{
vec4 ambient;
};
uniform gl_LightModelParameters gl_LightModel;
{
vec4 sceneColor;
};
uniform gl_LightModelProducts gl_FrontLightModelProduct;
uniform gl_LightModelProducts gl_BackLightModelProduct;
struct gl_LightProducts
{
vec4 ambient;
};
uniform gl_LightProducts gl_FrontLightProduct[ gl_MaxLights ];
uniform gl_LightProducts gl_BackLightProduct[ gl_MaxLights ];
uniform vec4 gl_TextureEnvColor[ gl_MaxTextureUnits ];
uniform vec4 gl_EyePlaneS[ gl_MaxTextureCoords ]; uniform vec4 gl_EyePlaneT[ gl_MaxTextureCoords ]; uniform vec4 gl_EyePlaneR[ gl_MaxTextureCoords ]; uniform vec4 gl_EyePlaneQ[ gl_MaxTextureCoords ];
uniform vec4 gl_ObjectPlaneS[ gl_MaxTextureCoords ]; uniform vec4 gl_ObjectPlaneT[ gl_MaxTextureCoords ]; uniform vec4 gl_ObjectPlaneR[ gl_MaxTextureCoords ]; uniform vec4 gl_ObjectPlaneQ[ gl_MaxTextureCoords ];
{
vec4 color;
float start;
float scale;
};
uniform gl_FogParameters gl_Fog;
Wbudowane zmienne udostępniane
W przeciwieństwie do zmiennych udostępnianych definiowanych przez użytkownika, zmienne wbudowane nie mają pomiędzy programami cieniowania wierzchołków i programami cieniowania fragmentów korelacji „ jeden do jednego”. Stąd każdy z rodzajów programów cieniowania ma swój odrębny zestaw zmiennych udostępnianych, które posiadają specyficzne zależności między sobą.
Programy cieniowania wierzchołków mają możliwość zapisu następujących zmiennych udostępnianych:
varying float gl_FogFragCoord;
Natomiast programy cieniowania fragmentów mogą odczytywać następujące zmienne udostępniane:
varying float gl_FogFragCoord;
Zauważmy, że nazwy zmiennych gl_Color i gl_SecondaryColor są identyczne jak nazwy wbudowanych atrybutów programów cieniowania wierzchołków. Na szczęście konflikt nazw nie występuje bowiem powyższe atrybuty widoczne są wyłącznie w programach cieniowania wierzchołków, a zmienne udostępniane dostępne są tylko w programach cieniowania fragmentów.
Wartości zmiennych gl_Color oraz gl_SecondaryColor są automatycznie obliczane ze zmiennych gl_FrontColor, gl_BackColor, gl_FrontSecondaryColor i gl_BackSecondaryColor w oparciu o to, która strona wielokąta jest widoczna.
Zmienna gl_PointCoord jest niezdefiniowan jeżeli bieżący prymityw nie jest punktem, lub sprajty punktowe nie są aktywne.
Wbudowane funkcje
GLSL zawiera szereg funkcji działających na zmiennych skalarnych oraz wektorowych. Większość z nich można wykorzystać zarówno w programach cieniowania wierzchołków jak i w programach cieniowania fragmentów. Wbudowane w GLSL funkcje można podzielić na trzy podstawowe grupy:
Wiele funkcji wbudowanych w języku GLSL jest wzorowana na funkcjach bibliotecznych języka C. Różnica polega zazwyczaj na możliwości operowania także na zmiennych wektorowych. W programach cieniowania należy preferować stosowanie funkcji wbudowanych nad odpowiadające im własne funkcje, bowiem z założenia są one optymalne i zawsze, gdy jest taka możliwość, wykonywane sprzętowo.
W programie cieniowania można zastąpić wbudowaną funkcję deklarując i definiując funkcję o takiej samej nazwie i liście parametrów jak funkcja wbudowana. W znajdujących się poniżej opisach typ genType oznacza dowolny skalarny lub wektorowy typ zmiennoprzecinkowy: float, vec2, vec3 i vec4. Analogicznie typ mat oznacza dowolny typ macierzowy, vec wektor z liczbami zmiennoprzecinkowymi a bvec wektor z liczbami typu bool.
Funkcje trygonometryczne
W tej grupie funkcji przedstawionych w tabeli 4, poza funkcjami trygonometrycznymi, znajdują się także funkcje kątowe i cylkometryczne (odwrotne do funkcji trygonometrycznych). Parametry funkcji trygonometrycznych podawane są w radianach. W przypadku wystąpienia dzielenia przez zero wynik funkcji jest nieokreślony. Operacje wykonywane są na każdej składowej parametru (lub parametrów) funkcji.
Tabela 4: Zestawienie funkcji trygonometrycznych
Funkcje wykładnicze
Opis funkcji wykładniczych i innych przedstawionych w tabeli 5 dotyczy operacji na każdej składowej parametru (lub parametrów) funkcji.
Tabela 5: Zestawienie funkcji wykładniczych
Funkcje ogólne
Funkcje ogólne zestawione w tabeli 6 operują na różnego rodzaju argumentach, przy czym prezentowany opis dotyczy działania na każdej składowej argumentu.
Tabela 6: Zestawienie funkcji ogólnych
Funkcje geometryczne
Spośród funkcji geometrycznych przedstawionych w tabeli 7 szczególną uwagę warto zwrócić na ftransform, która umożliwia bezpośrednie wyliczenie wartości zmiennej gl_Position.
Tabela 7: Zestawienie funkcji geometrycznych
Funkcje macierzowe
Zestawienie funkcji działających na macierzach przedstawia tabela 8. Funkcje operujące na macierzach prostokątnych zostały wprowadzone w wersji 1.20 języka GLSL.
Tabela 8: Zestawienie funkcji macierzowych
Funkcje porównujące wektory
Przedstawione w tabeli 9 funkcje porównujące wektory działają na elementach składowych wektorów w wyniku zwracając wektor z liczbami typu bool. Oczywiście wymiary wektorów wejściowych muszą być równe. Jedynie trzy ostatnie funkcje wykonują operacje logiczne na wektorach z liczbami typu bool.
Użyte w opisach funkcji typy bvec oznaczają odpowiednio: bvec2, bvec3 lub bvec4. Analogiczna zasada dotyczy vex - są to typy: vec2, vec3 lub vec4 oraz ivec, co odpowiada ivec2, ivec3 lub ivec4. W każdym przypadku wymiary porównywanych wektorów muszą być zgodne.
Tabela 9: Zestawienie funkcji porównujących wektory
Funkcje próbkujące tekstury
Funkcje próbkujące tekstury dostępne są zarówno dla programów cieniowania fragmentów jak i programów cieniowania wierzchołków. Dostęp do tekstury uzyskiwany jest za pośrednictwem uchwytu - parametry sampler. Występujące w części funkcji opcjonalne parametry bias dostępne są wyłącznie w programach cieniowania fragmentów. Wartość tego parametru jest dodawana do poziomu szczegółowości mipmap (LOD) przed pobraniem próbki tekstury. Występujący w części funkcji parametr lod, to oczywiście wspomniany przed chwilą poziom szczegółowości mipmap.
Tabela 10: Zestawienie funkcji próbkujących tekstury
Funkcje różniczkowe
Funkcje różniczkowe (tabela numer 11) są dostępne tylko dla programów cieniowania fragmentów. Ponieważ obliczanie wartości pochodnych jest kosztowne i może być niestabilne numerycznie, implementacja może stosować szybkie przybliżone metody ich obliczania. Ponadto implementacja może udostępniać regulację dokładności obliczania funkcji różniczkowych przy pomocy wskazówki renderingu określonej stałą
GL_FRAGMENT_SHADER_DERIVATIVE_HINT.
Tabela 11: Zestawienie funkcji różniczkowych
Funkcje stochastyczne
Wymienione w tabeli 12 funkcje stochastyczne są dostępne dla obu rodzajów programów cieniowania. Funkcje zwracają wartości pseudolosowe o następujących właściwościach:
Tabela 12: Zestawienie funkcji stochastycznych
Na koniec opisu języka GLSL jeszcze jedna uwaga. Zawsze należy zapoznać się z dokumentacją udostępnianą przez producentów procesorów kart graficznych. Przykładowo w wydaniu 60 sterowników do kart z procesorami NVIDIA, wszystkie funkcje stochastyczne zwracały wyłącznie wartości 0.