náhled|Malá fotografie zobrazuje několik různých prvků scény vytvořených pomocí jazyka HLSL. Jednotlivé prvky jsou záměrně různě deformovány podle různých algoritmů. High Level Shader Language (zkráceně HLSL) je vyšší programovací jazyk pro psaní shaderů vyvinutý společností Microsoft s podporou DirectX (verze 8 a vyšší) XNA, Xbox a Xbox 360. HLSL je velmi podobný jazyku Cg od společnosti NVIDIA, přičemž syntaxe obou jazyků vychází z jazyka C.
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ů.
Shader Model V rámci vývoje grafických karet a vykreslovacího řetězce se postupně měnily i vlastnosti a schopnosti jednotlivých shaderů. Aby bylo možné rozlišit jaké technologie z hlediska shaderů konkrétní hardware či software podporuje, jsou potupně zaváděny tzv. verze Shader Modelu (samostatně pro vertex shader i pixel shader). Bohužel tento údaj je zcela porovnatelný pouze v rámci produktů jedné společnosti, protože neexistuje žádná pevná specifikace, která by jasně definovala požadavky pro jednotlivé verze Shader modelů. Vývoj shaderů je řízen výrobci GPU — společnostmi nVidia a ATI (nyní AMD) — a společností Microsoft, která vyvíjí grafické rozhraní Direct3D. Společnost Microsoft uveřejnila svou vlastní specifikaci pro Shader modelShader Models vs Shader Profiles (anglicky).
Shader Model 1.0 Fixní vykreslovací řetězec omezoval realizaci vlastního formátu pro uložení vrcholu. Toto omezení odstranilo zavedení Vertex Shaderu 1.0 a 1.1.
Jeho instrukční sada je velice omezená. Většina instrukcí, které známe z běžných procesorů zde schází. Naopak jsou zde speciální instrukce, které se hodí pro výpočet osvětlení apod. Délka programu vertex shaderu je zde omezena podle použité grafické karty nejčastěji na 128 instrukcí.
Struktura Pixel Shaderu verzí 1.1 až 1.3, se od sebe příliš neliší. Instrukční sada této verze se spíše podobá CISC procesoru, tzn. že obsahuje poměrně velké množství specializovaných instrukcí převážně na nabírání texelu z textury. Podívejme se na schematický obrázek shaderu:
Další verze Pixel Shader 1.4 už přinesla změny převážně v instrukční sadě a způsobu výběru texelu. Přesto je tato verze plně zpětně kompatibilní se starší řadou. Přibyly dva texturové a tři odkládací registry.
Shader Model 2.0 Ve vertex shaderu verze 2.0 se objevilo hned několik vylepšení. Bylo přidáno několik nových aritmetických instrukcí a délka programu byla prodloužena na 256 instrukcí. Umožňuje skoky, podmíněné příkazy nebo smyčky, takže program ve vertex shaderu se může libovolně větvit.
Kromě několika nových texturových instrukcí, Pixel Shader 2.0 přináší aritmetické instrukce podobné těm ve vertex shaderu. Maximální délka programu byla zvětšena na 32 texturovacích a 64 aritmetických instrukcí. Byly přidány instrukce pro vektorové výpočty (normalizace vektoru, násobení maticí, vektorový součin).
Shader Model 3.0 Vertex Shader 3.0 přinesla prodloužení programu na 512 instrukcí. Specifikace jednotky vertex shaderu byla rozšířena o čtení dat z textury. Počet pracovních registrů byl zvýšen na 32 a ke statickému řízení toku programu přibylo dynamické.
Jednotka Pixel Shaderu 3.0 se v programátorském modelu velice přiblížila jednotce vertex shaderu. Maximální počet instrukcí byl navýšen na 512, počet pracovních registrů vzrostl na 32 a počet registrů konstant na 224. Pixel shader verze 3.0 umí statické i dynamické řízení toku programu.
Shader Model 4.0 Ve čtvrté verzi Shader Modelu nabízejí všechny shadery stejný základní programátorský model. Každý ze tří shaderů (vertex, geometry, pixel) pak přidává funkčnost, která je specifická pro danou fázi grafického řetězce. Délka programu shaderu není omezena.
HLSL profily překladu Profilem překladu lze specifikovat cílovou verzi vertex/pixel shaderu. Dostupné profily shrnuje následující tabulka:
ProfilVerze DirectXSpecifikacevs_1_18.0Vertex Shader verze 1.1vs_2_0 9.0Vertex Shader verze 2.0vs_2_x 9.0a, 9.0b rozšířený Vertex shader verze 2.0vs_2_a 9.0aoptimalizováno pro model shaderu na NVIDIA GeForce FXvs_3_0 9.0cVertex shader verze 3.0vs_4_0 10.0Vertex shader verze 4.0vs_4_1 10.1Vertex shader verze 4.1vs_5_0 11.0Vertex shader verze 5.0ps_1_18.0Pixel Shader verze 1.1ps_1_28.1Pixel Shader verze 1.2ps_1_38.1Pixel Shader verze 1.3ps_1_48.1Pixel Shader verze 1.4ps_2_09.0Pixel Shader verze 2.0ps_2_0a9.0aoptimalizováno pro model shaderu na NVIDIA GeForce FXps_2_0b9.0boptimalizováno pro model shaderu na ATI Radeon X700, X800, X850ps_2_x9.0rozšířený Pixel Shader verze 1.1ps_3_09.0cPixel Shader verze 3.0ps_4_010.0Pixel Shader verze 4.0ps_4_110.0Pixel Shader verze 4.1ps_5_010.0Pixel Shader verze 5.0
Operátory HLSL poskytuje operátory známé z jazyka C, navíc je k dispozici speciální operátor swizzleLanguage Syntax (DirectX HLSL) (anglicky).
Kvalifikátory varying — proměnný datový typ. Hodnota se mění každým vykonání shaderu (typicky pozice, normála aj.) uniform — globální proměnné v shaderu, které jsou inicializovány externě aplikací nebo běhovým prostředím. Hodnota zůstává konstantní pro každé vykonání shaderu. (typicky barva materiálu, transformační matice) const — proměnnou nelze v rámci shaderu změnit static — lokální proměnná, jejichž hodnota po skončení průběhu funkce zůstává zachována.
Datové typy
Skalární typy bool — pravdivostní datový typ (true/false) int — 32bit, celé číslo se znaménkem uint — 32bit, celé číslo bez znaménka half — 16bit, číslo s plovoucí desetinnou čárkou float — 32bit, číslo s plovoucí desetinnou čárkou double — 64bit, číslo s plovoucí desetinnou čárkou
Vektorové typy Vektor je homogenní datový typ. HLSL podporuje až čtyř složkové vektory. Vektorový typ můžete definovat dvěma ekvivalentními způsoby. V následující ukázce je definován třísložkový vektor, kde každá složka je datového typu float: float3 prikladVektor; vector <float, 3> prikladVektor; Vektor můžeme při deklaraci přímo inicializovat: float3 fVector = { 0.2f, 0.3f, 0.4f }; vector <float, 3> fVector = { 0.2f, 0.3f, 0.4f }; K jednotlivým složkám vektoru smíme přistupovat pomocí definovaných složek x, y, z, w nebo r, g, b, a. Oba způsoby jsou ekvivalentní, ovšem nesmějí se vzájemně kombinovat. float4 vektor;
vektor.x = 9; /* správně */ vektor.yz = 0; /* správně */
vektor.a = 5; /* správně */ vektor.rg = 0; /* správně */
vektor.ay = 0; /* nelze */
Matice Postup při deklaraci i inicializaci matice je podobný jako u vektoru. Obecný formát deklarace matice je následující:
datový_typ počet_řádkůxpočet_sloupců identifikátor
Ukázka deklarace matice 4x4 typu float: float4x4 prikladMatice; vector <float, 3> prikladVektor;
Inicializace matice:
float2x2 fMatrix = { 0.0f, 0.1, 2.1f, 2.2f };
Přistoupit k jednotlivým složkám matice můžeme dvěma způsoby: číslování od nuly _m00, _m01, _m02, _m03 _m10, _m11, _m12, _m13 _m20, _m21, _m22, _m23 _m30, _m31, _m32, _m33 číslování od jedničky _11, _12, _13, _14 _21, _22, _23, _24 _31, _32, _33, _34 _41, _42, _43, _44
V obou případech první číslo značí číslo řádku a druhé číslo sloupce.
Příklad float4x4 matice;
matice._11 = 1; matice._m00_m11_m22_m33 = 1;
float2x2 fMatrix = { 1.0f, 1.1f, // row 1 2.0f, 2.1f }; // row 2
float temp;
temp = fMatrix; // cteni slozky 0, 0 temp = fMatrix; // cteni slozky 0, 1
float2 temp2; temp2 = fMatrix;
Sampler, Texture a Shader Type Sampler — handler na texturový objekt Texture — strukturovaná kolekce dat pro ukládání texelů. Texel je nejmenší možnou jednotkou textury, z které lze číst nebo do ní zapisovat (lze asociovat s pixelem). Každý texel obsahuje tři nebo čtyři složky uspořádané podle daného formátu. Na rozdíl od bufferu lze texturu filtrovat pomocí sampleru čtením shaderovací jednotkou. Typ textury určuje jak bude textura filtrována. Textury mohou být: Texture1D, Texture1DArray, Texture2D, Texture2DArray, Texture3D, TextureCube Shader Type — objekt realizující shader
Buffer Generické datové úložiště. Data v bufferu jsou přístupná jako pole prvků. Ukázka: Buffer<float4> g_Buffer;
Vstupní a výstupní proměnné Parametry procházející skrz grafický vykreslovací řetězec je třeba označit jako vstup nebo výstup.
Syntaxe pro označení identifikátoru: Shader má definovány množiny vstupních a výstupních sémantických atributů (POSITION, BLENDWEIGHT, BLENDINDICES, NORMAL, PSIZE, TEXCOORD, TANGENT, BINORMAL, TESSFACTOR, POSITIONT, COLOR, FOG, DEPTH, SAMPLE). Pro vstupní proměnné se kterými programátor chce dále pracovat je třeba vytvořit speciální strukturu. Identifikátory jednotlivých složek této struktury jsou odděleny symbolem : od sémantického atributu. Obdobně se postupuje i při výběru parametrů, které chceme předat na výstup vertex shaderu.
struct VertexShaderInput { float4 vPosition : POSITION; float3 vNormal : NORMAL; float4 vBlendWeights : BLENDWEIGHT;
struct VertexShaderOutput { float4 vPosition : POSITION; float4 vDiffuse : COLOR;
Ve výše uvedené ukázce je atribut vPosition je označen jako POSITION, což znamená že ve výsledném programu bude proměnná namapována na výstupní registr.
Řídící struktury a funkce HLSL nabízí klasické řídící struktury jejichž konstrukce je stejná jako v jazyce C, navíc jsou rozšířeny o tzv. atributy. Jazyk podporuje tvorbu uživatelských funkcí a současně disponuje souborem funkcí vestavěných, které jsou zaměřeny zejména na grafické operace. Tyto funkce dělíme na matematické funkce a specializované funkce pracující s texturami.
if else Příkaz if slouží k větvení programu. Vyhodnocením podmínkového výrazu rozhodne zdali se daný blok příkazů provede (podmínka je pravdivá) nebo ne (podmínka je nepravdivá). K příkazu if lze přidat příkaz else. Příkaz else určuje blok příkazů, které se provedou pouze pokud je podmínka nepravdivá. Formát konstrukce if, else je následující: [Atribut] if ( výraz ) { blok příkazů 1; } else { blok příkazů 2; }
Zadání položky Atribut je volitelné a určuje způsob kompilace příkazu if. Může nabývat hodnot (flatten, branch):
Příklad [branch] if(x) { x = sqrt(x); }
switch Příkaz switch slouží k výběru jedné z několika větví programu, která se má provést v závislosti na nějaké celočíselné hodnotě. Jeho formát je následující: [Atribut] switch( celočíselný výraz) { case hodnota1 : { blok příkazů; } break; case hodnota2 : { blok příkazů;} break; ... case hodnotaN : { blok příkazů; } break; default : { blok příkazů; } break; }
Zadání položky Atribut je volitelné a určuje způsob kompilace příkazu switch. Může nabývat těchto hodnot (flatten, branch, forcecase, call).
Příklad [branch] switch( a ) { case 0: return 0; case 1: return 1; case 2: return 3; default: return 6; }
for Příkaz cyklu for umožňuje opakovat jeden, nebo více příkazů. Konstrukce cyklu for: [Atribut] for ( inicializace; test podmínky; inkrementace ) { blok příkazů; }
Zadání položky Atribut je volitelné a určuje způsob kompilace příkazu for. Může nabývat těchto hodnot (unroll, loop, fastopt, allow_uav_condition).
Příklad float value = 0; [loop] for (uint i = 0; i < 4; i++) { value++; }
while Cyklus while je cyklus s podmínkou na začátku. Napřed se testuje podmínka, je-li platná, pak se provede tělo cyklu a znovu se testuje podmínka. Není-li platná, program pokračuje za cyklem. Není-li tedy podmínka platná při prvním příchodu na cyklus, neprovede se cyklus ani jednou. Jeho formát vypadá takto: [Atribut] while ( test podmínky ) { blok příkazů; }
Zadání položky Atribut je volitelné a určuje způsob kompilace příkazu for. Může nabývat těchto hodnot (unroll, loop, fastopt).
Jazyk HLSL umožňuje použít i do while variantu cyklu.
break, continue Příkazy pro řízení cyklu.
Příkaz discard Příkaz discard ruší zpracování fragmentu nebo vrcholu. Alternativou k tomuto příkazu je funkce clip().
Seznam aritmetických funkcí