Vianoce v ITnetwork sú tu! Dobí si teraz kredity a získaj až 80 % extra kreditov na e-learningové kurzy ZADARMO. Zisti viac.
Hľadáme nové posily do ITnetwork tímu. Pozri sa na voľné pozície a pridaj sa k najagilnejšej firme na trhu - Viac informácií.

7. diel - XNA a HLSL - postprocesorov Sepia, Alfa masking a Noise

Vitajte znovu, v dnešnom diele si pridáme ďalšie postprocesorové efekty. Začneme zľahka shader nazývaným sépia. Pridáme si tiež alfa masking a potom taky výsledný obraz zaneřádíme náhodným šumom. Práca veľa, začnime.

Sépia

Sepia je jednoduchý shader, skrze ktorý je možné obraz pozmeniť do podoby staré fotografie. Taky určite poznáte tento efekt z mobilov, kde sa v rôznych obmenách objavuje. Vzorec vyzerá nasledovne:

R=r*0.393 + g*0.769 + b*0.189
G=r*0.349 + g*0.686 + b*0.168
B=r*0.272 + g*0.534 + b*0.131

Ako sa k tomu prišlo sa ma nepýtajte, neviem to. Shader je potom jednoduchý ako facka:

float4 color = tex2D(tex[0], UV);
float4 ret = color;
ret.r = (color.r * 0.393) + (color.g * 0.769) + (color.b * 0.189);
ret.g = (color.r * 0.349) + (color.g * 0.686) + (color.b * 0.168);
ret.b = (color.r * 0.272) + (color.g * 0.534) + (color.b * 0.131);

return ret;

Iba som prepísal konštanty spomínané vyššie. Výsledný efekt vyzerá nasledovne:

Efekt sépia v XNA - Tvorba shaderov v HLSL

Tak to by sme sa rozohriali po kratšej odmlke a teraz už niečo poriadneho a užitočného.

Alfa masking

Alfa masking je shader, ktorý nám umožní prekryť celý obraz obrázkom iným - maskou. Tento efekt určite poznáte zo strieľačiek, keď zameriavate. Ukážeme si dva prístupy, jeden s maskou čierno-bielu a druhý, ktorý používa alfa kanál. Ale čo budeme potrebovať vždy, tak nahrať textúru s maskou a prepašovať ju do shader. Preto v súbore sa triedou pridáme premenné:

Texture2D Mask;
string texture;

V konstruktoru si nastavíme meno súboru s textúrou:

public AlfaMask(Game g, string texture): base("postprocesory/alfamask",g){
  this.texture = texture;
}

A ako obyčajne v metóde Load textúru z mena nahráme:

public override void Load(){
  base.Load();
  Mask = Game.Content.Load<Texture2D>(texture);
}

Pred vykreslením nesmieme zabudnúť textúru nastaviť ako aktívny. Vykonáme to rovnako ako minule nastavením:

public override void Draw(Texture2D input){
  Game.GraphicsDevice.Textures[1] = Mask;
  base.Draw(input);
}

Poradie úkonov som si nepomýlil, musí byť iba takto, inak sa veľmi pravdepodobne nič nevykreslí. Poďme si pripraviť tiež shader. Predovšetkým zvýšime počet samplerov na dva:

sampler2D tex[2];

A môžeme pristúpiť k samotnému shader. Ako vždy si vyberieme farbu:

float4 color = tex2D(tex[0], UV);

a tiež vyberieme farbu z masky:

float4 mask=tex2D(tex[1],UV);

Výslednú farbu získame vynásobením oboch takto získaných farieb.

return color*mask;

Tento jednoduchý postup bude fungovať trebárs pre túto masku:

Alfa masking v XNA ako zameriavač - Tvorba shaderov v HLSL

Jednoduchá čierno-biela maska. Všetko čo je čierne na maske prekryje obraz. Vyplýva to už z kódu shader. Čierna farba sú vlastne samé nuly a ak čokoľvek násobíme nulou, tak ako iste vieme je výsledok zase nula, teda čierna. Biela farba naopak prenesie všetku farbu pôvodnej. Pokiaľ ale budeme chcieť použiť nasledujúce masku:

Alfa masking v XNA ako zameriavač - Tvorba shaderov v HLSL

Ktorá používa okrem čiernej a bielej tiež ďalšie farbičky, mierne narazíme. Aj keď ako kedy. Záleží, aký efekt potrebujeme. Ostatné farby budú viac či menej priehľadné. Bude záležať na ich jasu. Niekedy je to efekt žiaduce, ale trebárs u zameriavača pušky, ktorý je napevno farbou natlačený, to chcieť nebudeme. A práve na tieto prípady budeme potrebovať textúru s alfa kanálom, ktorý nám určí ako veľmi je daný pixel priehľadný. Alfa kanál do nej dostaneme cez nejaký lepší editor na fotky. Ja som použil Photo Filtre, ktorý je podľa mňa jednoduchý na pochopenie. Nie je to síce taká mašinka ako obchod s fotkami, ale všetko čo som kedy potreboval sa mi s ním nejako podarilo urobiť. Pre predstavu ako alfa maska vyzerá viď nasledujúci obrázok:

Maska s alfa kanálom - Tvorba shaderov v HLSL

Zelené čiary mimo kruh sú pevné, vnútri priehľadnej a červený kruh je tiež mierne priehľadný. Do shader si to prenesieme miernu úpravou. Vzorec pre tento druh alfa blending je nasledujúci:

c=color*(1-mask.afa)+mask

A ten potom iba prevedieme do shader následujícně:

return color*(1-mask.a)+mask;

Ako vzorec ale funguje? Vyložíme si to na príklade. Dajme tomu, že máme pixel, ktorý je úplne nepriehľadný. Má teda alfu jedna. Tú odpočítame od jednotky a získame tak v zátvorke nulu. Takže vo výsledku bude len a len obsiahnutá farba z masky. A to je to čo presne chceme. Podobne tomu je aj u ďalších hodnôt.

Maska s alfa kanálom - Tvorba shaderov v HLSL

A ako vidno aj výsledok tomu zodpovedá.

Noise - šum

Tento shader využijeme na zašumenia celého obrazu. Celý princíp je veľmi jednoduchý, Shader vypočítame akýsi virtuálny a náhodný offset as pomocou neho len zo samplera vyberieme výslednú farbu. Ako však generovať náhodné číslo. Náhodné číslo nikdy nebude veľmi dobre náhodné, ale pre naše veľmi skromné účely ho vytvoríme z funkcie sínus a súradníc daného bodu. Treba podotknúť, že tento shader som našiel tu a budem sa originálu držať.

Pridáme si teda do shader premennú Seed, ktorá nám bude reprezentovať semienko:

float Seed;

V pixel shader si vypočítame náhodné číslo zo súradníc za použitia semienka:

float noiseX=Seed*sin(UV.x*UV.y);

Túto "náhodnou" hodnotu upravíme ešte za pomocou funkcie fmod. Tá rovnako pracuje rovnako ako modulo akurát je aj pre čísla s plávajúcou rádovou čiarkou. Vracia nám teda zvyšok po delení.

noiseX=fmod(noiseX,8)*fmod(noiseX,4);

A potom vypočítame offset pre súradnice. Opäť za pomocou zabudovanej funkcie fmod:

float2 dis=float2(fmod(noiseX,NoiseAmount),fmod(noiseX,NoiseAmount+0.002));

Nesmieme zabudnúť pridať premennú NoiseAmount ako parameter shader. Tá nám bude hovoriť ako veľmi sa má šum prejavovať. Ja som ju nastavil rovno na hodnotu 0.01 ktorá sa ukázala pomerne pekná:

float NoiseAmount=0.01;

A na záver offset aplikujeme v samplera a výslednú farbu vraciame ako finálna:

float4 color = tex2D(tex[0], UV+dis);
return color;

Shader je hotový. V triede sa Shader prepíšeme metódu Draw a nastavíme semienko na nami zvolenú hodnotu. Videl by som ju v rádoch stoviek až tak dvetisíc. Ja som použil 523.

float Seed;
public Noise(Game g) : base("postprocesory/noise",g){
  Seed = 523;
}

public override void Draw(Microsoft.Xna.Framework.Graphics.Texture2D input){
  Effect.Parameters["Seed"].SetValue(Seed);
  base.Draw(input);
}

A máme hotovo. Ak teraz program spustíme, dostaneme nádherne zašmudlaný obraz. A to by bolo pre dnes ... moment. A čo keď si zastavím animáciu. Tak to šumieť prestane. A to my nechceme. Budeme musieť do shader pridať tiež vplyv času. Bohužiaľ na to náš systém nie je stavaný, ale to sa dá rýchlo napraviť. V triede s všeobecným postprocesorov upravíme metódu Update a pridáme jej parameter a herným časom, takže bude vyzerať nasledovne:

public virtual void Update(GameTime time){

}

A do volanie tejto metódy v hlavnej hre tento parameter nezabudnite odovzdať. V našom shader potom už len pridáme premennú pre čas:

float Time;

A do funkcie sínus ju premietneme nasledovne:

float noiseX=Seed*Time*sin(UV.x*UV.y+Time);

Do triedy postprocesoru pridáme metódu Update kde si čas upravíme:

public override void Update(GameTime time){
  base.Update(time);
  Time += time.ElapsedGameTime.Milliseconds/500.0f;
}

A na úplný záver v metóde Draw čas nastavíme a sme hotoví.

Effect.Parameters["Time"].SetValue(Time);

Teraz sa bude šum hýbať aj pri statickej scéne.

Efekt šumu v XNA - Tvorba shaderov v HLSL

To by bolo naozaj pre tento diel všetko. Nabudúce sa pozrieme na možnosti rozmazávaniu obrazu, bez ktorých sa v hrách jednoducho nezaobídete. Teším sa na otázky, názory, nápady a tak ďalej v komentároch. Dovidenia nabudúce.


 

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é 120x (3.1 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
XNA a HLSL - Negatív, embos, gamma, toonshading a Sobel
Všetky články v sekcii
Tvorba shaderov v HLSL
Článok pre vás napísal vodacek
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
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