1. diel - XNA a HLSL - Prvý shader
V tomto seriáli sa pozrieme na to, ako písať shadery v jazyku
HLSL
(High Level Shader Language). V XNA to je jediný spôsob, ako
shadery môžeme písať. Existujú aj ďalšie jazyky (napríklad pre OpenGL),
ale tými sa tu zaoberať nebudeme. Viac menej ale všetko funguje a vyzerá
rovnako, pokiaľ to nie je assembler
Čo to je shader
Shader je krátky kus programu, ktorý beží na grafickej karte a nejakým
spôsobom ovplyvňuje vykresľovanie trojuholníkov. Môžeme skrze ne naniesť
textúru, pridať osvetlenie a ďalšie iné veci. So shader sme vlastne už
pracovali BasicEffect
nie je nič iné ako shader. Obsahuje
základné vykresľovanie, budeme sa ho snažiť nahradiť naším
vlastným.
Prvý shader
Ako základ by som asi použil piaty
diel, kde máme rotujúce model. Bude sa pre našu vec celkom hodiť.
Neobsahuje žiadny komplikovaný kód, ktorý by sa nám plietol. Najprv
pridáme do projektu nový shader. Robí sa to podobne ako s pridávaním
textúr. Akurát namiesto Add existing
item vyberieme
Add new item
a v okne EffectFile
. Ja som ho nazval
PrvniShader
. Vygeneruje sa nám prakticky celý hotový shader.
Poďme sa na neho pozrieť. Prvé, čo skúsené oko zaujme, je, že je syntax
vlastne céčkovská a taky že neexistuje zvýrazňovania. Budeme sa bez toho
musieť zaobísť. Hneď hore nájdeme naše poznáme 3 matica:
float4x4 World; float4x4 View; float4x4 Projection;
Tých sa nezbavíme nikdy. Dátový typ float4x4
nám hovorí,
že ide o maticu 4x4. Sú to takzvané parametre. Sú pre všetkých práve
vykresľovanie vrcholy rovnaké. Ďalej nasledujú dve štruktúry. Kto nikdy o
štruktúrach nepočul, tak je to taká "hlúpa" trieda:
struct VertexShaderInput{
float4 Position : POSITION0;
};
Prvá štruktúra vyjadruje to, čo do shader vstupuje. Iste si spomínate na to, ako sme tvorili vlastné vertex a práve tá dáta z vertexu tu reprezentuje táto štruktúra. Float4 je vektor o 4-roch hodnotách. Za dvojbodkou potom nájdeme takzvanou sémantiku. Sémantika ako taká dáva onej zložke význam. V tomto prípade sa jedná o pozíciu vertexu. Máme aj ďalšie sémantiky, ale o tých sa budeme baviť neskôr. Druhá štruktúra:
struct VertexShaderOutput{
float4 Position : POSITION0;
};
sa príliš od tej prvej nelíši. Vlastne vôbec. Hovorí nám informácie o výstupe. Opäť chceme, aby vystupovala pozície vertexu, avšak transformovaná. Ona transformácie sa deje hneď 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 sa hovorí vertex shader. Alebo ak časť shader, ktorá
pracuje s vertexy. Toto bude pre nás základné kód. Len pre predstavu sa v
ňom odohráva transformácie z lokálnych súradníc na súradnice sveta
(násobenie World
maticou), potom sú súradnice prepočítané do
tých ako ich vidí kamera (násobenie maticou View
) a na záver
transformované z 3D sveta na našu plochú obrazovku (násobenie maticou
Projection
). Funkcia mul, ako niektorým už došlo, slúži pre
násobenie matíc. Ďalšie funkcie, ktorá nasleduje, je takzvaný pixel
shader:
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0{ return float4(1, 0, 0, 1); }
Ten určuje, akú výslednú farbu bude mať práve opísaná pixel. V našom
prípade to bude červená. Poradie farieb je nasledovné: červená, zelená,
modrá a alfa alebo ak transparentnosť. Jednička znamená maximálny jas danej
farby. Rozsah je od 0 do 1, nie teda ako zvyčajne býva od 0 do 255-tich. Iste
si pamätáte, ako sme v bludisku odovzdávali farbu pre podlahy a tú sme
práve museli prepočítať pomocou metódy ToVector3
. Ideme
ďalej, už nám zostáva len kúsok:
technique Technique1{ pass Pass1{ VertexShader = compile vs_2_0 VertexShaderFunction(); PixelShader = compile ps_2_0 PixelShaderFunction(); } }
Ešte než vysvetlím o čo sa jedná, dovolím si kus kódu, ktorý sme predtým pri používaní shaderov používali:
Effect.CurrentTechnique.Passes[0].Apply();
V jednom shader totiž môže byť niekoľko "techník", ktoré možno
použiť pre vykresľovanie a každá môže mať niekoľko kôl. Väčšinou je
to síce len jedno, ale napríklad také rozmazania obrazu sa musia vykonávať
na dvakrát, raz vertikálne a druhýkrát horizontálne. Vo vnútri každého
kola máme nadefinované, ktoré shadery sa majú použiť a pod akou verzií sa
majú skompilovať. My si vystačíme s verziou 2, tá je podporovaná v režime
Reach
a pre náročnejších veci siahneme pre verziu 3. Tú možno
použiť len pre Hi-def
profil. Veľa rozdielov medzi nimi nie je.
Iba v množstve príkazov, ktoré môžeme zadať a prípadne používať
nejaké pokročilé funkcie. Tak by sa dal popísať základné shader.
Používame shader
Prvé, čo musíme urobiť, je si shader nahrať. Urobíme tak v metóde
LoadContent
:
Effect effect;
effect = Content.Load<Effect>("PrvniShader");
Máme efekt nahraný a pripravený na použitie. Aby ho bolo možné použiť
pre vykresľovanie modelu, musíme ním nahradiť BasicEffect
ktorý model teraz používa. Preto prejdeme všetky časti modelu a nastavíme
im náš shader:
foreach (ModelMesh mesh in model.Meshes){ foreach (ModelMeshPart part in mesh.MeshParts){ part.Effect = effect; } }
A už len stačí efekt použiť pri vykresľovaní. Najprv nastavíme spoločné parametre, nie je potreba nastavovať zakaždým znova:
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 pre každú časť modelu nastaviť maticu 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(); }
Ako ste si všimli, parametre shader sú v kolekcii a pristupujeme k nim cez ich názov. Ak teraz hru pustíte uvidíte niečo takéhoto:
Gratulujem. Napísali ste prvý shader. Nabudúce si doň pridáme textúru. Čakám na nápady, komentáre, otázky dole pod články ostatne ako vždy skoro márne.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 150x (1.58 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#