High Level Shader Language
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 model[1].
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:
Profil | Verze DirectX | Specifikace |
---|---|---|
vs_1_1 | 8.0 | Vertex Shader verze 1.1 |
vs_2_0 | 9.0 | Vertex Shader verze 2.0 |
vs_2_x | 9.0a, 9.0b | rozšířený Vertex shader verze 2.0 |
vs_2_a | 9.0a | optimalizováno pro model shaderu na NVIDIA GeForce FX |
vs_3_0 | 9.0c | Vertex shader verze 3.0 |
vs_4_0 | 10.0 | Vertex shader verze 4.0 |
vs_4_1 | 10.1 | Vertex shader verze 4.1 |
vs_5_0 | 11.0 | Vertex shader verze 5.0 |
ps_1_1 | 8.0 | Pixel Shader verze 1.1 |
ps_1_2 | 8.1 | Pixel Shader verze 1.2 |
ps_1_3 | 8.1 | Pixel Shader verze 1.3 |
ps_1_4 | 8.1 | Pixel Shader verze 1.4 |
ps_2_0 | 9.0 | Pixel Shader verze 2.0 |
ps_2_0a | 9.0a | optimalizováno pro model shaderu na NVIDIA GeForce FX |
ps_2_0b | 9.0b | optimalizováno pro model shaderu na ATI Radeon X700, X800, X850 |
ps_2_x | 9.0 | rozšířený Pixel Shader verze 1.1 |
ps_3_0 | 9.0c | Pixel Shader verze 3.0 |
ps_4_0 | 10.0 | Pixel Shader verze 4.0 |
ps_4_1 | 10.0 | Pixel Shader verze 4.1 |
ps_5_0 | 10.0 | Pixel Shader verze 5.0 |
Operátory
HLSL poskytuje operátory známé z jazyka C, navíc je k dispozici speciální operátor swizzle[2].
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ěnitstatic
— lokální proměnná, jejichž hodnota po skončení průběhu funkce zůstává zachována.
Skalární typy
bool
— pravdivostní datový typ (true/false)int
— 32bit, celé číslo se znaménkemuint
— 32bit, celé číslo bez znaménkahalf
— 16bit, číslo s plovoucí desetinnou čárkoufloat
— 32bit, číslo s plovoucí desetinnou čárkoudouble
— 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[2]
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[0][0]; // cteni slozky 0, 0
temp = fMatrix[0][1]; // cteni slozky 0, 1
float2 temp2;
temp2 = fMatrix[0];
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[2]
- 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[2]. 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í
acos, asin, atan, ceil, cos, cosh, degrees, dot, lerp, log, log10, mul, pow, radians, rsqrt, sin, sincos, sinh, step, sqrt, tan, tanh
.
Seznam geometrických funkcí
distance, length, normalize, reflect, refract
Seznam funkcí pro diferenciální výpočty
ddx, ddy
Funkce pro práci s texturami
Prvním argumentem při zavolání texturovací funkce je vždy objekt typu sampler. Použitá funkce musí vždy souhlasit s dimenzí textury.
Seznam funkcí pro práci s texturami:
Funkce | Popis |
---|---|
tex1D , tex2D , tex3D , texCUBE |
Vzorkuje texturu. |
tex1Dbias , tex2Dbias , tex3Dbias , texCUBEbias |
Vzorkuje texturu s mipmap biasem. Můžeme tak ovlivnit, které úrovně mipmapy se budou vzorkovat. Jako bias se použije 4. souřadnice (a nebo w). |
tex1Dproj , tex2Dproj , tex3Dproj , texCUBEproj |
Texturové souřadnice se před vzorkováním vydělí 4. složkou (a nebo w) tj. provede se projekce souřadnic. |
Jednotlivé texturovací funkce mohou mít více přetížených alternativ.
Direktivy preprocesoru
Pro řízení předzpracování zdrojového kódu, je k dispozici sada preprocesorových direktiv známých z jazyka C.
Komentáře
Stejné jako v jazycích C, C++
/* krátká verze */
// celořádková verze
Proces překladu
Programy napsané v HLSL musejí být nejprve přeloženy do byte kódu překladačem, který je součástí knihovny D3DX (součást DirectX Graphics). Výsledný byte kód je binární reprezentací jazyka podobného jazyku symbolických instrukcí. Takto přeložený program už může být použit jako vstup pro metodu rozhraní Direct3D, která se postará o vytvoření strojové verze shaderu.
HLSL kód[3]:
float4 main(float4 t: TEXCOORD0) : SV_Target
{
if (t.x > t.y)
return t.xyzw;
else
return t.wzyx;
}
Odpovídající DX9 kód[3]:
add r0.x, -v0.x, v0.y
cmp oC0, r0.x, v0.wzyx, v0
Odpovídající DX10 kód (sémantika původního kódu zůstává více zachována)[3]:
float4 main(float4 t: TEXCOORD0) : SV_Target
{
lt r0.x, v0.y, v0.x
if_nz r0.x // <--- POZOR! Opravdu bylo záměrem vytvořit větvení?
mov o0.xyzw, v0.xyzw
ret
else
mov o0.xyzw, v0.wzyx
ret
endif
}
Pro ukládání shaderovacích programů do souboru byl zaveden nový formát s příponou FX.
Ukázka vertex shaderu
Ukázka vertex shaderu pro Direct3D 9[4].
vector vClr;
struct VS_INPUT
{
float4 vPosition : POSITION;
float3 vNormal : NORMAL;
float4 vBlendWeights : BLENDWEIGHT;
};
struct VS_OUTPUT
{
float4 vPosition : POSITION;
float4 vDiffuse : COLOR;
};
float4x4 mWld1;
float4x4 mWld2;
float4x4 mWld3;
float4x4 mWld4;
float Len;
float4 vLight;
float4x4 mTot;
VS_OUTPUT VS_Skinning_Example(const VS_INPUT v, uniform float len=100)
{
VS_OUTPUT out;
// Pozice
float3 vPosition =
mul(v.vPosition, (float4x3) mWld1) * v.vBlendWeights.x +
mul(v.vPosition, (float4x3) mWld2) * v.vBlendWeights.y +
mul(v.vPosition, (float4x3) mWld3) * v.vBlendWeights.z +
mul(v.vPosition, (float4x3) mWld4) * v.vBlendWeights.w;
// Normála
float3 vNormal =
mul(v.vNormal, (float3x3) mWld1) * v.vBlendWeights.x +
mul(v.vNormal, (float3x3) mWld2) * v.vBlendWeights.y +
mul(v.vNormal, (float3x3) mWld3) * v.vBlendWeights.z +
mul(v.vNormal, (float3x3) mWld4) * v.vBlendWeights.w;
// Výstup
out.vPosition = mul(float4(vPosition + vNormal * Len, 1), mTot);
out.vDiffuse = dot(vLight,vNormal);
return out;
}
Ukázka geometry shaderu
Ukázka vertex shaderu pro Direct3D 10[5].
void GSScene(triangleadj GSSceneIn input[6], inout TriangleStream<PSSceneIn> OutputStream)
{
PSSceneIn output = (PSSceneIn)0;
for (uint i=0; i<6; i+=2)
{
output.Pos = input[i].Pos;
output.Norm = input[i].Norm;
output.Tex = input[i].Tex;
OutputStream.Append(output);
}
OutputStream.RestartStrip();
}
Ukázka pixel shaderu
Ukázka jednoduchého pixel shaderu[6].
float4 PS(float vPos : VPOS, float2 tex : TEXCOORD0) : COLOR
{
...
return float4(1.0f, 0.3f, 0.7f, 1.0f);
}
Odkazy
Reference
- Shader Models vs Shader Profiles (anglicky)
- Language Syntax (DirectX HLSL) (anglicky)
- Advanced D3D10 Rendering (anglicky)
- Writing HLSL Shaders in Direct3D 9 (anglicky)
- Getting Started with the Stream-Output Stage (Direct3D 10)
- XNA Meeting Point — Ambient light Archivováno 17. 1. 2012 na Wayback Machine (anglicky)
Související články
- Ostatní jazyky pro psaní shaderů
- OpenGL Shading Language (GLSL) — Vyšší programovací jazyk určený primárně pro použití s OpenGL
- Cg programming language — Jazyk vyvinutý společností NVIDIA
Externí odkazy
- Programming Guide for HLSL, from Microsoft (anglicky)
- Introduction to the DirectX 9 High Level Shading Language, (ATI) AMD developer central (anglicky)
- Riemer's HLSL Introduction & Tutorial (includes sample code) Archivováno 19. 11. 2008 na Wayback Machine (anglicky)
- Simple HLSL Shader Tutorial (anglicky)
- HLSL Introduction (anglicky)
- Hardware pro počítačovou grafiku
- Kurz DirectX Archivováno 2. 3. 2012 na Wayback Machine