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:
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