NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.

Lekce 1 - XNA a HLSL - První shader

V tomto seriálu se podíváme na to, jak psát shadery v jazyce HLSL (High Level Shader Language). V XNA to je jediný způsob, jak shadery můžeme psát. Existují i další jazyky (třeba pro OpenGL), ale těmi se tu zabývat nebudeme. Více méně ale vše funguje a vypadá stejně, pokud to není assembler :-)

Co to je shader

Shader je krátký kus programu, který běží na grafické kartě a nějakým způsobem ovlivňuje vykreslování trojúhelníků. Můžeme skrze ně nanést texturu, přidat osvětlení a další jiné věci. Se shaderem jsme vlastně už pracovali BasicEffect není nic jiného než shader. Obsahuje základní vykreslování, budeme se jej snažit nahradit naším vlastním.

První shader

Jako základ bych asi použil pátý díl, kde máme rotující model. Bude se pro naši věc celkem hodit. Neobsahuje žádný komplikovaný kód, který by se nám pletl. Prvně přidáme do projektu nový shader. Dělá se to podobně jako s přidáváním textur. Akorát namísto Add existing item vybereme Add new item a v okně EffectFile. Já jsem ho nazval PrvniShader. Vygeneruje se nám prakticky celý hotový shader. Pojďme se na něj podívat. První, co zkušené oko zaujme, je, že je syntaxe vlastně céčkovská a taky že neexistuje zvýrazňování. Budeme se bez toho muset obejít. Hned nahoře nalezneme naše známe 3 matice:

float4x4 World;
float4x4 View;
float4x4 Projection;

Těch se nezbavíme nikdy. Datový typ float4x4 nám říká, že jedná o matici 4x4. Jsou to takzvané parametry. Jsou pro všechny právě vykreslované vrcholy stejné. Dále následují dvě struktury. Kdo nikdy o strukturách neslyšel, tak je to taková "hloupá" třída:

struct VertexShaderInput{
    float4 Position : POSITION0;
};

První struktura vyjadřuje to, co do shaderu vstupuje. Jistě si vzpomínáte na to, jak jsme tvořili vlastní vertex a právě ta data z vertexu zde reprezentuje tato struktura. Float4 je vektor o 4-řech hodnotách. Za dvojtečkou pak najdeme takzvanou sémantiku. Sémantika jako taková dává oné složce význam. V tomto případě se jedná o pozici vertexu. Máme i další sémantiky, ale o těch se budeme bavit později. Druhá struktura:

struct VertexShaderOutput{
    float4 Position : POSITION0;
};

se příliš od té první neliší. Vlastně vůbec. Říká nám informace o výstupu. Opět chceme, aby vystupovala pozice vertexu, ovšem transformovaná. Ona transformace se děje hned pod:

VertexShaderOutput VertexShaderFunction(VertexShaderInput input){
    VertexShaderOutput output;

    float4 worldPosition = mul(input.Position, World);
    float4 viewPosition = mul(worldPosition, View);
    output.Position = mul(viewPosition, Projection);

    return output;
}

Tomuto úseku se říká vertex shader. Nebo-li část shaderu, která pracuje s vertexy. Toto bude pro nás základní kód. Jen pro představu se v něm odehrává transformace z lokálních souřadnic na souřadnice světa (násobení World maticí), potom jsou souřadnice přepočteny do těch jak je vidí kamera (násobení maticí View) a na závěr transformovány z 3D světa na naši plochou obrazovku (násobení maticí Projection). Funkce mul, jak některým už došlo, slouží pro násobení matic. Další funkce, která následuje, je takzvaný pixel shader:

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0{
    return float4(1, 0, 0, 1);
}

Ten určuje, jakou výslednou barvu bude mít právě vykreslovaný pixel. V našem případě to bude červená. Pořadí barev je následující: červená, zelená, modrá a alfa nebo-li průhlednost. Jednička znamená maximální jas dané barvy. Rozsah je od 0 do 1, ne tedy jak obvykle bývá od 0 do 255-ti. Jistě si pamatujete, jak jsme v bludišti předávali barvu pro podlahy a tu jsme právě museli přepočítat pomocí metody ToVector3. Jedeme dále, už nám zbývá jen kousek:

technique Technique1{
    pass Pass1{
        VertexShader = compile vs_2_0 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}

Ještě než vysvětlím o co se jedná, dovolím si kus kódu, který jsme dříve při používání shaderů používali:

Effect.CurrentTechnique.Passes[0].Apply();

V jednom shaderu totiž může být několik "technik", které lze použít pro vykreslování a každá může mít několik kol. Většinou je to sice jen jedno, ale třeba takové rozmazání obrazu se musí provádět na dvakrát, jednou vertikálně a podruhé horizontálně. Uvnitř každého kola máme nadefinované, které shadery se mají použít a pod jakou verzí se mají zkompilovat. My si vystačíme s verzí 2, ta je podporována v režimu Reach a pro náročnější věci sáhneme pro verzi 3. Tu lze použít pouze pro Hi-def profil. Mnoho rozdílů mezi nimi není. Pouze v množství příkazů, které můžeme zadat a případně používat nějaké pokročilé funkce. Tak by se dal popsat základní shader.

Používáme shader

První, co musíme udělat, je si shader nahrát. Uděláme tak v metodě LoadContent:

Effect effect;
effect = Content.Load<Effect>("PrvniShader");

Máme efekt nahraný a připravený k použití. Aby ho bylo možné použít pro vykreslování modelu, musíme jím nahradit BasicEffect který model nyní používá. Proto projdeme všechny části modelu a nastavíme jim náš shader:

foreach (ModelMesh mesh in model.Meshes){
  foreach (ModelMeshPart part in mesh.MeshParts){
    part.Effect = effect;
  }
}

A už jen stačí efekt použít při vykreslování. Nejprve nastavíme společné parametry, není je potřeba nastavovat pokaždé znovu:

effect.Parameters["View"].SetValue(Matrix.CreateLookAt(new Vector3(100, 150, 0), new Vector3(0, 0, 0), Vector3.Up));
effect.Parameters["Projection"].SetValue(Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, GraphicsDevice.DisplayMode.AspectRatio, 1, 1000));

A pro každou část modelu nastavit matici World:

foreach (ModelMesh mesh in model.Meshes){
  foreach (ModelMeshPart part in mesh.MeshParts){
    part.Effect.Parameters["World"].SetValue(transforms[mesh.ParentBone.Index]*rotace);
  }
  mesh.Draw();
}

Jak jste si všimli, parametry shaderu jsou v kolekci a přistupujeme k nim přes jejich název. Pokud nyní hru pustíte uvidíte něco takovéhoto:

Použití shaderu v HLSL v XNA - Tvorba shaderů v HLSL

Gratuluji. Napsali jste první shader.

Příště, XNA a HLSL - Textury, si do něj přidáme texturu. Čekám na nápady, komentáře, otázky dole pod články ostatně jako vždy skoro marně.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 150x (1.58 MB)
Aplikace je včetně zdrojových kódů v jazyce C# .NET

 

Všechny články v sekci
Tvorba shaderů v HLSL
Přeskočit článek
(nedoporučujeme)
XNA a HLSL - Textury
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
6 hlasů
Vodáček dělá že umí C#, naplno se již pět let angažuje v projektu ŽvB. Nyní studuje na FEI Upa informatiku, ikdyž si připadá spíš na ekonomice. Není mu také cizí PHP a SQL. Naopak cizí mu je Java a Python.
Aktivity