OpenGL Shading Language

OpenGL Shading Language (zkráceně GLSL nebo také GLslang) je vyšší programovací jazyk pro psaní shaderů, který vychází ze syntaxe jazyka C pro všeobecné použití. Jazyk GLSL byl vytvořen konsorciem OpenGL ARB.

Popis

Jazyk vznikl v rámci procesu postupné transformace fixního vykreslovacího řetězce na řetězec programovatelný a stal se jednou z vyspělejších alternativ k tehdy používaným nižším programovacím jazykům pro psaní shaderů.

Nová funkcionalita (nebo funkcionalita specifická pro určitého výrobce či hardware) je do GLSL často doplňována nejprve v podobě tzv. rozšíření (např. podpora geometry shaderu), které je třeba v kódu explicitně aktivovat a v pozdějších verzích se významná rozšíření stávají součástí funkcionality základní.

Verze

Tabulka shrnující jednotlivé verze OpenGL a příslušné verze GLSL, které byly v rámci dané specifikace představeny:[1]

Verze GLSLVerze OpenGL
1.10.592.0
1.20.82.1
1.30.103.0
1.40.083.1
1.50.113.2
3.30.63.3
4.00.94.0
4.10.64.1
4.20.64.2

Operátory

Jazyk GLSL poskytuje operátory známé z jazyka C. Výjimku tvoří operátory související s ukazateli, které nejsou v jazyce GLSL podporovány. V prvních verzích rovněž nebylo možné používat bitové operátory — jejich podpora byla zavedena ve verzi 1.30.[2] Oproti jazyku C je navíc k dispozici speciální operátor swizzle.

Datové typy

GLSL podporuje skalární datové typy jazyka C. Lze použít i datový typ void ovšem pouze jako typ návratové hodnoty funkcí. Podpora struktur a polí umožňuje tvorbu uživatelských datových typů.

Skalární typy

  • bool — pravdivostní datový typ (true/false)
  • int — 32bit, celé číslo se znaménkem
  • uint — 32bit, celé číslo bez znaménka
  • float — 32bit, číslo s plovoucí desetinnou čárkou
  • double — 64bit, číslo s plovoucí desetinnou čárkou a dvojnásobnou přesností

Vektorové typy

Vektor je homogenní datový typ. GLSL má vestavěnou podporu pro jedno- až čtyř-složkové vektory. Název vektorového datového typu (např. dvec4) se skládá z prefixu části "vec" a sufixu. Prefix určuje datový typ složek vektoru[p 1] a sufix určuje jejich počet. Příklad použití (včetně inicializace):

vec4 color0 = vec4(1.0, 0.0, 0.0, 0.0);

K jednotlivým složkám vektoru lze pak přistupovat pomocí definovaných složek x, y, z a w (např. color0.x = 0.5;).[3]

Matice

GLSL poskytuje podporu pro práci s maticemi o rozměrech 2x2 až 4x4, přičemž matice nemusí být pouze čtvercová (k dispozici jsou všechny kombinace uvedených rozměrů). Názvy maticových datových typů jsou tvořeny obdobně jako u vektorů — název začíná prefixem následuje část "mat" a je ukončen sufixem. Prefix určuje datový typ prvků matice[p 1] a sufix určuje rozměr matice (např. mat3x4; u čtvercových matic je použit zkrácený zápis např. mat3 pro matici o rozměrech 3x3). Příklad použití (včetně inicializace):

K jednotlivým prvkům matice přistupujeme obdobně jako k prvkům pole v jazyce C:[3]

mat3 matrix;
matrix[1] = vec3(3.0, 3.0, 3.0);
matrix[2][0] = 16.0;

Další datové typy

GLSL nabízí i tzv. opaque (česky někdy též "transparentní") datové typy. Opaque datový typ je ve skutečnosti pouze handler (handle) na nějaký jiný objekt. Přístup k takovému objektu probíhá přes sadu vestavěných funkcí (přímé čtení či zápis hodnoty proměnné takového typu není možné). Při deklaraci jednoho opaque datového typu jsou ve skutečnosti deklarovány objekty dva, handler samotný i vnitřní objekt ke kterému handler přistupuje. Deklarace těchto typů je možná pouze na některých místech kódu — typicky bývají deklarovány např. jako parametry funkcí. [4]

Do kategorie opaque datových typů spadají v GLSL tzv. Samplery (např. sampler2D) a jedná se o handler k jedno-, dvou- či tří-rozměrné textuře, hloubkové textuře, cube-mapě apod.

Image je další třídou opaque datových typů (např. image1D, image2D, image2DMS a další). jedná se o handler k jedno-, dvou- či tří-rozměrnému obrázku (image), které se používají pro načítání či ukládání souborů obsahujících statický obraz (případně pro další atomické operace).

Řídící struktury a funkce

Jazyk GLSL využívá stejnou konstrukci řídících struktur (větvení, cyklů, skoků) a funkcí jako jazyk C. Kromě podpory uživatelských funkcí disponuje GLSL i sadou funkcí vestavěných. Některé z nich jsou podobné funkcím jazyka C (zejména matematickým) — např. funkce exp() nebo abs(), zatímco jiné jsou určeny speciálně pro práci s grafikou — např. smoothstep() nebo texture2D().

Direktivy preprocesoru

Pro řízení předzpracování zdrojového kódu, je k dispozici sada preprocesorových direktiv známých z jazyka C. Navíc jsou k dispozici speciální direktivy #version a #extension.[4]

Direktiva version

Direktiva #version se musí nacházet na začátku každého zdrojového souboru a určuje verzi GLSL, která bude při překladu použita.

#version 150

Direktiva extension

Chování GLSL lze dále ovlivnit pomocí tzv. rozšíření OpenGL (OpenGL extensions). Tato rozšíření lze řídit právě pomocí direktivy #extension:

#extension jmeno_rozsireni: chovani

Kde položka "jmeno_rozsireni" definuje název konkrétního rozšíření nebo nabývá hodnoty "all" chceme-li pracovat se všemi rozšířeními. Dané rozšíření lze v části "chovani" povolit nebo zakázat a dále pak definovat chování v případě, kdy rozšíření není dostupné.

Komentáře

Komentáře lze zapisovat ve stejném formátu jako v jazycích C, C++.

/* krátká verze */
// celořádková verze

Proces překladu

Kód shaderu může být umístěn v samostatném souboru nebo jako textový řetězec v rámci hlavního programu. Vlastní překlad probíhá až za běhu aplikace. Nejprve je třeba vytvořit shader objekt a definovat jeho typ (např. GL_VERTEX_SHADER) voláním OpenGL funkce glCreateShader(). Vlastní překlad je vyvolán funkcí glCompileShader() (jako parametr je předán řetězec s kódem shaderu). Dalším krokem je sestavení přeložených shaderů ve výsledný program. Opět je nejprve nutné vytvořit speciální objekt program funkcí glCreateProgram(). Následuje připojení přeložených shader objektů — glAttachShader() a potom již vlastní proces linkování — glLinkProgram(), v rámci kterého dochází rovněž k mapování proměnných hlavního programu a proměnných GLSL shaderů.[5]

Ukázka vertex shaderu

Uvedený vertex shader realizuje stejnou transformaci vstupu jako fixní vykreslovací řetězec.

#version 120

void main(void)
{
    gl_Position = ftransform();
}

Použitá funkce ftransform() není od verze GLSL 1.40 dostupná. Při použití novější specifikace GLSL musí programátor místo použití této funkce explicitně vykonat násobení vertexu projekční a modelview maticí.

#version 140

uniform Transformation
{
    mat4 projection_matrix;
    mat4 modelview_matrix;
};

in vec3 vertex;

void main()
{
    gl_Position = projection_matrix * modelview_matrix * vec4(vertex, 1.0);
}

Ukázka geometry shaderu

Následující kód realizuje jednoduchý průchozí geometry shader pro barvu a pozici vertexu.

#version 120
#extension GL_EXT_geometry_shader4 : enable

void main()
{
    for (int i = 0; i < gl_VerticesIn; ++i) 
    {
        gl_FrontColor = gl_FrontColorIn[i];
        gl_Position = gl_PositionIn[i];
        EmitVertex();
    }
}

Od verze GLSL 1.50 nejsou již geometry shadery rozšířením, ale staly se součástí základní funkcionality GLSL. V souvislosti s tím došlo i k mírné změně syntaxe.

#version 150

layout(triangles) in;
layout(triangle_strip, max_vertices = 3) out;

void main()
{
    for (int i = 0; i < gl_in.length(); i++)
    {
        gl_Position = gl_in[i].gl_Position;
        EmitVertex();
    }
    EndPrimitive();
}

Ukázka fragment shaderu

Jednoduchý shader, jehož výstupem bude fragment červené barvy.

#version 120

void main(void)
{
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

Při použití specifikace GLSL 1.30 nebo pozdější je třeba shader upravit.

#version 150

out vec4 MyFragColor;
void main(void)
{
    MyFragColor = vec4(1.0, 0.0, 0.0, 1.0);
}

V hlavním programu je potřeba provést navázání proměnné:

glBindFragDataLocation(Program, 0, "MyFragColor");

kde:

  • Program — handler použitého shader programu
  • 0 — index barvového bufferu
  • "MyFragColor" — uživatelsky definovaný název barvového bufferu

Ukázka Tessellation control shaderu

Příklad tessellation control shaderu pracujícího s trojúhelníky.[6]

#version 400

layout(vertices = 3) out;
in vec3 vPosition[];
out vec3 tcPosition[];
uniform float TessLevelInner;
uniform float TessLevelOuter;

void main()
{
    tcPosition[gl_InvocationID] = vPosition[gl_InvocationID];
    if (gl_InvocationID == 0)
    {
        gl_TessLevelInner[0] = TessLevelInner;
        gl_TessLevelOuter[0] = TessLevelOuter;
        gl_TessLevelOuter[1] = TessLevelOuter;
        gl_TessLevelOuter[2] = TessLevelOuter;
    }
}

Ukázka Tessellation evaluation shaderu

Příklad tessellation evaluation shaderu pracujícího s trojúhelníky.[6]

#version 400

layout(triangles, equal_spacing, cw) in;
in vec3 tcPosition[];
out vec3 tePosition;
out vec3 tePatchDistance;
uniform mat4 Projection;
uniform mat4 Modelview;

void main()
{
    vec3 p0 = gl_TessCoord.x * tcPosition[0];
    vec3 p1 = gl_TessCoord.y * tcPosition[1];
    vec3 p2 = gl_TessCoord.z * tcPosition[2];
    tePatchDistance = gl_TessCoord;
    tePosition = normalize(p0 + p1 + p2);
    gl_Position = Projection * Modelview * vec4(tePosition, 1);
}

Odkazy

Poznámky

  1. a b U názvů datových typů (vektorů a matic) jejichž složky jsou typu float, se prefix v neuvádí.

Reference

V tomto článku byl použit překlad textu z článku GLSL na anglické Wikipedii.

Související články

Ostatní jazyky pro psaní shaderů

Externí odkazy

IDE
Příklady a ukázky