A HLSL programozási nyelv

Típusok, típuskonstrukciók

Beépített típusok

Skaláris típusok

A típusok az azonos nevű ANSI C típusokhoz hasonlóak.

  • bool – true vagy false lehet az értéke
  • int – 32 bites előjeles integer
  • uint – 32 bites előjel nélküli integer
  • half – 16 bites lebegőpontos érték, csak a nyelvek kompatibilitása miatt hozták létre a Direct3D 10 miatt
  • float – 32 bites lebegőpontos érték
  • double – 64 bites lebegőpontos érték
  • string – ASCII string
  • DirectX 10-ben még további típusok:

  • snorm float – IEEE 32 bites előjeles normalizált lebegőpontos a [-1, 1]-es intervallumban
  • unorm float - IEEE 32 bites előjel nélküli normalizált lebegőpontos a [0, 1]-es intervallumban
  • Tömb típusok

  • Buffer:
  • Egy, vagy több skalár lehet benne.

    Deklarálása:

    Buffer<Típus> Név;

    Például:

    Buffer<float4> g_Buffer;

    Adatok lekérése:

    float4 bufferData = g_Buffer.Load(1); //ez a Buffer-ből a második elemet veszi ki

  • Vektor:
  • Deklarálása:

    Típuskomponens Név

    Típuskomponens – Ennek az első fele egy skalár típus, a második fele pedig egy szám, ami 1 és 4 közé esik.

    Másik megadási lehetőség:

    vector<Típus, Vektorméret> Név

    Az elemek elérése a .xyzw, vagy .rgba –val is történhet.

    Például:

    float4 fVector = { 0.1f, 0.5f, 0.4f, 0.2f }; vector<float, 4> fVector = { 0.1f, 0.5f, 0.4f, 0.2f }; fVector.x = 0.1f;

  • Mátrix:
  • Vektorhoz hasonlóan működik.

    Deklarálása:

    Típuskomponens Név

    Típuskomponens – itt az első fele egy skalár típus, a második fele a sorok száma, a harmadik pedig az oszlopok száma. A sorok és oszlopok száma pozitív integer 1 és 4 között.

    Másik megadási lehetőség:

    matrix<típus, sorszám, oszlopszám> Név

    A mátrix elemeit így is el lehet érni:

    ._m<row><col>[_m<row><col>][...]

    Például:

    int1x1 iMatrix; // integer értékeket tartalmazó mátrix 1 oszloppal, és 1 sorral float3x4 fMatrix; // float mátrix 3 sorral, és 4 oszloppal matrix<int, 3, 3> iMatrix = { 1, 0, 0, 0, 1, 0, 0, 0, 1 }; //3x3-as egységmátrix iMatrix._m00_m11 = { 1, 0 };

    Egyéb típusok

  • Shader típus:
  • Ez a shader változó, a lefordított shader-ünket fogja reprezentálni, tehát igazából ebbe kerül bele az általunk megírt függvény, melyben visszaadjuk azt, amit majd a következő shader megkap a sorban.

    DirectX 9-ben

    Deklarálása:

    XShader = compile ShaderTarget ShaderFunction(…);

    X – VertexShader/PixelShader lehet, attól függ mit akarunk fordítani

    ShaderTarget – Ebben megtudjuk adni, hogy milyen verziójú shader modellt használjon a fordításhoz. Tehát például az újabb GPU-knak extra funkcióik vannak.

    ShaderFunction – Ez egy ASCII string, melyben megadjuk a függvény nevét, ami a shader programunknak igazából a „main” függvénye. Tehát ez az a függvény, ahol elkezdődik a végrehajtás, amikor a shader meghívódik. (…)-ban az argumentumok szerepelnek. Ezek az argumentumok ugyanazok, mint amiket az API-ból adunk át, amikor a shader-t létrehozzuk a SetVertexShader, vagy SetPixelShader paranccsal.

    Példák:

    Ez egy egyszerű példa a shader típus használatára.

    string ParamID = "0x0"; float4x4 wvp : WORLDVIEWPROJ; struct VS_OUTPUT
    { float4 Pos : POSITION; float4 Col : COLOR0; }; VS_OUTPUT VS( float3 Pos : POSITION ) { VS_OUTPUT Out = (VS_OUTPUT)0; float4 hPos = float4( Pos, 1); Out.Pos = mul( hPos, wvp); //csak beállítjuk a vertexek pozícióját
    a wvp (world*view*projection) mátrixal Out.Col = float4( 1, 1, 1, 1); //fehér szín mindegyik vertexnek return Out; } technique Default { pass P0 { // shaders CullMode = None; VertexShader = compile vs_2_0 VS(); } }

    A technique részben hozzuk létre a VertexShader típusunkat. A shader programunknak ez a része nagyon fontos, mivel itt adjuk meg a lefordított shader-jeinket, illetve beállíthatunk bizonyos dolgokat, mint például a CullMode, tehát a háromszögeknél a hátlapeldobást. Ebbe részletesebben nem megyek bele itt.

    A következő példa a különböző shader modellek használatát mutatja be. Ilyenkor újabb GPU esetén, az újabb shader modellt használja, és az ahhoz megírt függvényt (VS40(), VS30(), …). Tehát ilyenkor négyféleképpen meg kell írnunk a dolgokat, az újabb GPU esetén az újabb dolgokat kihasználva, de ha nincs meg a támogatás, akkor a régebbi verzióra megírt függvényt kell használni.

    // Shader model 4 technique DefaultTechniqueSM4 { pass P0 { // shaders CullMode = None; VertexShader = compile vs_4_0 VS40(); } }
    // Shader model 3 technique DefaultTechniqueSM3 { pass P0 { // shaders CullMode = None; VertexShader = compile vs_3_0 VS30(); } }
    // Shader model 2 technique DefaultTechniqueSM2 { pass P0 { // shaders CullMode = None; VertexShader = compile vs_2_0 VS20(); } }

    DirectX 10-ben

    Hasonló a DirectX 9-hez.

    Különbség a megadáskor:

    SetX Compile( ShaderTarget, ShaderFunction );

    X – bővült a geometry shader-rel

    ShaderTarget – bővült a shader model 4-el (vs_4_0, ps_4_0, gs_4_0)

    Példa:

    // Direct3D 10 technique10 Render { pass P0 { SetVertexShader( CompileShader( vs_4_0, VS() ) ); SetGeometryShader( NULL ); SetPixelShader( CompileShader( ps_4_0, PS() ) ); } }

    A felhasználó által definiálható típusok

  • Lehet a nyelvben általunk definiált típusokat is létrehozni.
  • Deklarálása:

    typedef [const] Type Name[Index];

    const – Opcionális, ezzel megjelölhetjük a típust egy konstansnak.

    Type – Az adattípus, ennek egy a HLSL beépített típusnak kell lennie.

    Name – ASCII string, ami azonosítja a típust.

    Index – Opcionális megadható. Ez a tömb méretét adja meg, 1 és 4 között előjel nélküli integernek kell lennie.

    Példák:

    typedef vector <bool, #> bool#; typedef vector <int, #> int#; typedef vector <uint, #> uint#; typedef matrix <float, #, #> float#x#; typedef matrix <int, #, #> int#x#; typedef matrix <double, #, #> double#x#;

    # jel, 1 és 4 közötti előjel nélküli integert jelent.

    A DirectX 8-al való kompatibilitás miatt a következő típusok automatikusan definiáltak:

    typedef int DWORD;
    typedef float FLOAT;
    typedef vector <float, 4> VECTOR;
    typedef matrix <float, 4, 4> MATRIX;
    typedef string STRING;
    typedef texture TEXTURE;
    typedef pixelshader PIXELSHADER;
    typedef vertexshader VERTEXSHADER;

  • Struct típus
  • Ugyanúgy létrehozható, és használható, mint C++-ban.

    Például:

    struct VS_OUTPUT { float4 Position : SV_POSITION; float4 Diffuse : COLOR0; float2 TextureUV : TEXCOORD0; };

    struct-ok segítségével adjuk át az egyes shader-ek között az adatokat is. Tehát az előző példánkban VS_OUTPUT lesz a pixel shader bemeneti változója.

    Műveletek

    A HLSL-ben sok művelet előre meg van írva, tehát ha például összeszorzunk két vektort, akkor azoknak a skaláris szorzatát adja vissza, két mátrix szorzata esetén elvégzi a mátrixszorzást. Tehát a művelet, illetve a művelet komponensei alapján kiválasztja a megfelelőt, és azt végzi el.

    Például:

    float4 v = v1*v2; //ilyenkor az eredmény: float4 v.x = v1.x*v2.x; float4 v.y = v1.y*v2.y; float4 v.z = v1.z*v2.z; float4 v.w = v1.w*v2.w;

    ez teljesen más mintha egyetlen skalárt akartunk volna eredménynek, ilyenkor a dot függvényt kell használnunk

    v = v1.x*v2.x + v1.y*v2.y + v1.z*v2.z + v1.w*v2.w;

    Ugyanígy a mátrixszorzás is működik:

    float3x3 mat1, mat2, mat3; … //mat1, mat2 feltöltése mat3 = mat1*mat2;

    Ilyenkor az eredmény komponensenkénti szorzása a két mátrixnak:

    mat3._m00 = mat1._m00*mat2._m00; …

    Itt szintén más a megszokott mátrixszorzás, dot függvénnyel érhető el ez az eredmény:

    mat.m00 = mat1._m00 * mat2._m00 + mat1._m01 * mat2._m10 + mat1._m02 * mat2._m20 + mat1._m03 * mat2._m30;

    a szorzás függvénynek léteznek beépített túlterhelései különböző típusokra:

    Pl.: vector*vector, vector*matrix, matrix*vector, matrix*matrix

    float4x3 World; float4 main(float4 pos : SV_POSITION) : SV_POSITION { float4 val; val.xyz = mul(pos,World); val.w = 0; return val; }

    Ez ugyanaz, mintha az írtam volna, hogy:

    float4x3 World; float4 main(float4 pos : SV_POSITION) : SV_POSITION { float4 val; val.xyz = (float3) mul((float1x4)pos,World); val.w = 0; return val; }

    Ebben az esetben használtuk a cast-olást.