A GLSL programozási nyelv

Típusok, típuskonstrukciók

Alap típusok

Void típus

Ha egy függvénynek nincs visszatérési értéke, akkor a visszatérési típust void-nak kell deklarálni. Ezen kívül még az üres paraméterlistát lehet vele jelölni függvényekben. Más deklarációkban nem szerepelhet.

Logikai típus

A logikai típust a bool kulcsszó jelöli, amely csak a true és false értékeket képes tárolni. A C++-al ellentétben nem feleltethető meg az int típusnak.

Egész típus

Az egész típust az int vagy az uint kulcsszó jelöli. Az előbbi előjeles, az utóbbi előjel nélküli egész, amelyek pontosan 32 biten ábrázolódnak. Túlcsordulás és alulcsordulás esetén az alsó 32 bit marad meg. Egy egész literál alapértelmezetten egy int típusú literál, míg az u betű hozzáfűzésével uint típusú literált kapunk. Az egész literálokban a 0 előtag 8-as, a 0x vagy 0X előtag 16-os számrendszerben megadott számot jelöl. Ha nincs előtag, akkor 10-es számrendszerbeli a literál.

Lebegőpontos típus

Egyszeres pontosságú típus a float, kétszeres pontosságú a double. Az IEEE-754 szabvány szerint kódoltak. A lebegőpontos literálok alapértelemezetten float típusúak lesznek, de az lf vagy LF utótag hozzáfűzésével double literált kapunk. A float literálhoz opcionálisan hozzáfűzhető az f vagy F utótag. Exponens rész is megadható. Például: 10e-2. Az előjel megadása esetén az előjel csak egy unáris operátornak számít, nem képezi részét a literálnak.

Vektor típus

A nyelvben beépített típusok a 2, 3 vagy 4 komponenst tartalmazó vektorok, amelyek egész, lebegőpontos vagy logikai értékeket tárolhatnak. A lebegőpontos vektorok használhatók például színek, normálvektorok, pozíciók, textúrakoordináták tárolására, míg a logikai vektorok a numerikus vektorok koponensenkénti összehasonlítására használhatók. Néhány példa:

vec2 texcoord; vec3 position; vec4 RGBAcolor; ivec2 textureLookup; bvec3 less;

Mátrix típus

A nyelvben beépítetten a 2×2, 2×3, 2×4, 3×2, 3×3, 3×4, 4×2, 4×3 és 4×4 lebegőpontos komponens tárolására alkalmas mátrixok találhatóak. A mat szóval kezdődő típusok az egyszeres pontosságú, míg a dmat-al kezdődőek a dupla pontosságú lebegőpontos értékeket tároló mátrixokat jelölik. A mat vagy dmat után kell megadni NxM alakban a mátrix alakját, ahol az N az oszlopok számát, M pedig a sorok számát jelöli. Ha csak egy szám szerepel, akkor az egy NxN-es mátrix. Néhány példa:

mat2 mat2D; mat3 optMatrix; mat4 view, projection; mat4x4 view; mat3x2 m; dmat4 highPrecisionMVP; dmat2x4 dm;

Átlátszatlan(opaque) típusok

Az átlátszatlan típusú változók nem olvashatók vagy írhatók közvetlen módon, ehelyett beépített függvényekkel férhetünk hozzájuk. Az ilyen típusú változók csak függvényparaméterek lehetnek vagy olyan változók, amelyek a uniform minősítővel vannak ellátva. Kifejezésekben nem szerepelhetnek operandusként, kiéve a zárójelezést, tömb indexelést és a struktúra adattag kiválasztást. Függvény paraméterben nem lehet out vagy inout minősítővel ellátni, viszont in minősítővel igen. Az OpenGL API inicializálja az ilyen változókat, így deklarációnál nem adathatunk meg inicializálást.

Három kategóriába sorolhatók az átlátszatlan típusok:

Összetett típusok

Struktúra típus

Felhasználó által definiált típust struktúrákkal lehet létrehozni, amelyeket a struct kulcsszóval kell megadni. Például:

qualifier struct name { T1 member1; T2 member2; ... } variable1, variable2, ...;

A name az új típus neve, a variable1 és variable2 a name típusúnak deklarált új változók. A struktúrában legalább egy adattagnak lennie kell. A qualifier csak a deklarált variable1 és variable2-re vonatkozik nem magára a típusra. Az adattagok csak pontosságra vonatkozó minősítőt tartalmazhatnak deklarációjukban és típusuknak már definiáltnak kell lennie. Az adattagok deklarációja nem tartalmazhat inicializációt. Ha tömbnek deklarálunk egy adattagot, akkor annak meg kell adni a pontos méretét. A struktúrán belül nem lehetnek névtelen mezők és a struktúrák beágyazása sem támogatott.

Tömb típus

Tömbökben egyforma típusú elemek tárolhatók, amelyek lehetnek alaptípusú vagy struktúra típusú elemek. A tömb mérete megadható egy egész típusú konstanssal, de ez nem kötelező. Ha nem adunk meg méretet deklarációnál, később ugyanazzal az azonosítóval újradeklarálhatunk egy ugyanilyen típusú tömböt egy megadott mérettel. Konstanssal való indexelésnél nem lehet negatív vagy a méretnél nagyobb-egyenlő indexet megadni, míg nem konsatans esetén ilyen esetben nem definiált működést eredményez a végrehajtás. Csak egydimenziós tömbök hozhatók létre és egymásba sem lehet őket ágyazni. Példák:

float a[3]; light lights[];

Tömb lehet függvény visszatérési értéke is és függvényparaméter is. A tömböket lehet inicializálni tömb konstruktorok segítségével:

float a[5] = float[5](3.4, 4.2, 5.0, 5.2, 1.1); float a[5] = float[](3.4, 4.2, 5.0, 5.2, 1.1);

Egy tömbnek értékül adhatunk egy olyan tömböt, amelynek a mérete adott:

float a[5]; float b[] = a; // b mérete 5 float b[5] = a; // b mérete 5 float b[] = float[](1,2,3,4,5); // b mérete 5

A length() függvénnyel kérdezhető le egy tömb mérete, de csak akkor, ha a tömb konkrét elemszámmal lett deklarálva:

a.length();

A length függvény egy int típusú értéket ad vissza.

Minősítők

A minősítőknek tetszőleges sorrendben a típus előtt kell szerepelniük. A layout minősítő az egyetlen, amely többször is szerepelhet. A memory minősítők közül többet felhasználhatunk a deklarációkban, de a többi minősítő típusból legfeljebb egy szerepelhet.

Storage minősítők

Változó deklarációnál legfeljebb egy tárolásra vonatkozó minősítő adható meg a típus neve előtt. A következő lehetőségek vannak:

Az in vagy out minősítővel ellátott változóknak megadható egy kiegészítő, tárolásra vonatkozó minősítő is: centroid, sample vagy patch.

Az attribute és varying minősítők kompatibilitási okokból még a nyelvben vannak. Az előbbi a vertex shaderben az in-nek, az utóbbi a vertex shaderben az out-nak és a fragment shaderben az in-nek felel meg.

Lokális változók a fentiek közül csak a const minősítőt használhatják. A függvény paraméterek az in, out és const-ot használhatják paraméter minősítőként a fentiek közül. Függvény visszatérési érték és struktúra adattagok nem használhatják egyik előbb felsorolt minősítőt sem.

Minősítők csoportosítása

Az in, out és uniform változók deklarációi interface blokkokba csoportosíthatók. Az interface deklarációk formája:

Layout-minősítő Interface-minősítő Blokknév { // tagok deklarációja } Példánynév;

A layout minősítő és a példánynév opcionális. A példány nevével lehet a tagokra hivatkozni. Az interface minősítő in, out vagy uniform lehet. Az out minősítővel ellátott interface blokkok a csővezeték következő programozható szakaszának megfelelő in blokkjába másolódnak. Az uniform minősítővel ellátott interface blokkok a fő alkalmazás buffer objektumába másolódnak. Vertex shader-ben nem lehet in interface blokk, fragment shader-ben nem lehet out interface blokk. A blokkban lévő deklarációkban inicializációs lista, átlátszatlan típusok, struktúra definíciók nem szerepelhetnek. A tagoknak lehetnek további minősítői is: interpolation minősítő, kiegészítő storage minősítő és storage minősítő is. A tagok storage minősítője csak in lehet in minősítővel ellátott interface blokkban és ugyanígy out vagy uniform is lehet, ha out vagy uniform minősítőjű interface blokkban helyezkednek el.

A blokk neve arra való, hogy a szomszédos shader-ek interface blokkjainak illeszkedését megvalósítsuk, kivéve a uniform blokkot, ahol az alkalmazás a blokk nevével tud a blokkra hivatkozni. Egy shader out blokkjának és a következő shader in blokkjának deklarációjának meg kell egyeznie.

A példánynév megadása esetén a tagok a blokk hatókörébe kerülnek és a rájuk való hivatkozás a Példánynév.tag formában lehetséges, különben globális hatókörben deklaráltak és a tag nevével hivatkozhatunk rájuk. Az OpenGL API-ból való hivatkozás is hasonló, azzal a különbséggel, hogy ha megadunk egy példány nevet, akkor az API-ban nem a Példánynév.tag formában, hanem a Blokknév.tag formában hivatkozhatunk rájuk. Néhány példa:

out Vertex { vec4 Position; // az OpenGL API "Vertex.Position"-al hivatkozik rá vec2 Texture; } Coords; // a shader "Coords.Position"-nal hivatkozik rá out Vertex2 { vec4 Color; // az OpenGL API és a shader is "Color"-al hivatkozik rá };

Layout minősítők

Kidolgozásra vár.

Interpolation minősítők

Az input és output változók tovább minősíthetők legfeljebb egy interpolációs minősítővel. A flat minősítő esetén a változót nem interpolálja. A kiegészítő centroid és sample használható a flat mellett, de ez ugyanaz mintha csak a flat minősítőt adtuk volna meg. A smooth minősítő esetén perspektivikusan helyesen fogja interpolálni a kirajzolandó primitívet. A noperspective minősítő esetén lineárisan fogja interpolálni a változót a képernyő koordinátarendszerben.

Paraméter minősítők

A függvény paraméterek lehetséges minősítői. A részletes leírást a Függvények című fejezet tartalmazza.

Precision minősítők

A precision minősítők csak az OpenGL ES-el való kompatibilitás miatt vannak a nyelvben, de funkcionalitásuk és szemantikus jelentésük nincs, így a változók tárolásának pontosságára és a változókra alkalmazott műveletek pontosságára sincs hatással.

A precise minősítő

A fordítók gyakran optimalizálhatják a forráskódot lebegőpontos számításoknál, amelyek nagyobb teljesítményt érnek el, de csak hasonló eredményt adnak. Vannak olyan algoritmusok, amelyeknek a lebegőpontos számításokat abban a sorrendben kell elvégezniük, ahogyan a forráskódban meg lett adva és minden műveletet konzisztensen kell kezelniük. Például a result = (a*b) + (c*d) kifejezés optimalizálás esetén két művelet végrehajtásával számítódik ki, míg eredetileg 3-mal. Ezen kívül optimalizálásnál a két műveletet különböző pontossággal hajthatja végre.

A precise minősítőt egy változó neve előtt használva megtilthatjuk a fordítónak az optimalizálás lehetőségét azokban a műveletekben, amelyekben az adott változó értéke szerepel. Például:

precise out vec4 position;

Invariant minősítők

Előfordulhat, hogy két külön shader programban pontosan ugyanaz a kifejezés különböző értékeket vehet fel. Például, ha van két vertex shader, amelyek két különböző programban vannak és vesszük a gl_Position változót, amelynek ugyanaz a kifejezés az értéke mindkét shader-ben, akkor előfordulhat, hogy nem pontosan ugyanazt az értéket tárolják. Ez problémát okozhat egyes algoritmusokban.

Alapértelmezésben minden változóra fenn áll az előbbi tulajdonság. Az invariant minősítő megadásával deklarált kimenő változók viszont mindig pontosan ugyanazt az értéket fogják tartalmazni, ha ugyanazt a kifejezést adjuk nekik értékül két különböző program shader-jében. Invariant csak out minősítővel ellátott globális hatókörben elhelyezkedő változó lehet. Például:

invariant out vec3 Color; invariant gl_Position;

A következő preprocesszor utasítással (a fragment shader-eket leszámítva) a shader minden változóját invariant-tá lehet tenni, de ez csak debug-olási célokat szolgál, mivel az invariant használata teljesítmény csökkenést okozhat.

#pragma STDGL invariant(all)

Konstans kifejezésekre mindig garantált az invariáns tulajdonság.

Memory minősítők

Csak az image típusú változókat lehet memory minősítővel ellátni.

Operatárorok és kifejezések

Kidolgozásra vár.

Típuskonverziók

Lehetőség van explicit típuskonverzióra skaláris típusok között, de nem típus konverziós operátorral, hanem konstruktorok használatával.

skaláris-típus1(skaláris-típus2); // a paraméterben lévő kifejezést saláris-típus1 típusú kifejezésre konvertálja

Nem skaláris típusú paraméter megadása esetén a paraméter első elemét veszi és azt konvertálja a megfelelő típusra.

Néhány esetben szükség lehet kifejezések implicit típuskonverziójára. A lehetséges implicit konverziókat tartalmazza a következő táblázat:

Ez a típus konvertálhatóErre a típusra
intuint
int, uintfloat
int, uint, floatdouble
ivec2uvec2
ivec3uvec3
ivec4uvec4
ivec2, uvec2vec2
ivec3, uvec3vec3
ivec4, uvec4vec4
ivec2, uvec2, vec2dvec2
ivec3, uvec3, vec3dvec3
ivec4, uvec4, vec4dvec4
mat2dmat2
mat3dmat3
mat4dmat4
mat2x3dmat2x3
mat2x4dmat2x4
mat3x2dmat3x2
mat3x4dmat3x4
mat4x2dmat4x2
mat4x3dmat4x3