Zarábaj až 6 000 € mesačne! Akreditované rekvalifikačné kurzy od 0 €. Viac informácií.

6. diel - XNA a HLSL - Negatív, embos, gamma, toonshading a Sobel

Vitajte znova. V tomto dieli pridáme ďalšie ľahké postprocesové efekty. Ale ešte ktoré začneme, tak pre istotu zopakujem kostru shader, do ktorej budeme pridávať všetok obsah, ak nebude povedané inak:

sampler2D tex[1];

float4 PixelShaderFunction(float4 Position : POSITION0, float2 UV : TEXCOORD0) : COLOR0
{
    float4 color = tex2D(tex[0], UV);
    //dalsi kod semhle
}

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

Negatív

Ďalším veľmi ľahkým efektom je vytvorenie negatívu. Vlastne len farby otočíme a to takto:

1-barva

To len aby ste nevypadli zo vzorčekov :-) Vezmeme kostru shader a otrocky vzorček aplikujeme na všetky farebné zložky okrem alfa kanál:

return float4(1-color.r,1-color.g,1-color.b,1);

Shader do programu nasaďte ako minule a výsledok by mohol vyzerať nejako takto.

Shader pre negatív v XNA - Tvorba shaderov v HLSL

Všetko sa dá vyriešiť aj efektívnejšie pomocou takzvaného swizlingu. Je to špecialita jazyka ako takého, kiež by niečo také bolo možného aj inde:

return float4(1-color.rgb, color.a);

Operácia sa vykoná pre všetky komponenty oddelene. Super vlastnosť uľahčujúce zápis. A výsledok je úplne rovnaký. Ešte by som podotkol, že zložky musia byť z rovnakej sady teda buď z xyzw a alebo z rgba. Kombinovať je navzájom nedajú. Pomocou inverzie si vytvoríme ďalší efekt a tým bude embos.

Embos

Efekt embosu dovedie zdôrazniť prechody farieb. Principiálne sa skladá originálna farba s negatívom posunutým v určitom smere o niekoľko pixelov. Vzorček je nasledujúci:

(barva[u,v] + (1- barva[u+1,v+1]))/2

Pre posun o jeden pixel doprava dole. Dvoma delíme, aby bola výsledná farba v korektnom farebnom rozsahu. Posun musí byť pre dosiahnutie daného efektu vždy na diagonále. Implementáciu prevedieme ako vždy do kostry:

float3 ret=(color.rgb+(1-tex2D(tex[0],UV+offset).rgb))/2;
return float4(ret, color.a);

Kde offset je premenná, ktorú som si vystrčil von ako parameter.

float offset=0.001;

Hodnotu možno zistiť buď pokusne a alebo výpočtom. Keďže máme hodnoty pre textúrovanie v rozsahu 0-1, tak budeme musieť trošku kúzliť. Poznáme rozmer okna 800x480 (aspon myslím). Rozmer jedného pixelu zistíme 1/800. Pomerne sa to zhoduje s pokusnú hodnotou.

Shader pre embos v XNA - Tvorba shaderov v HLSL

Gamma korekcia

Ďalšie shader, tentokrát ale už bude používanie. Gama korekcia (anglicky gamma correction) je úprava obrazu, predtým používaná pre zobrazovanie na CRT obrazovkách, ktoré nezobrazovali lineárne. Preto bolo potrebné tento vplyv kompenzovať. Možno ju použiť aj na opravu expozície, kedy je požadovaný objekt schovaný v príliš tmavé alebo príliš svetlé časti obrazu a týmto spôsobom ju možno vytiahnuť von. Opäť ako vždy rovnica:

barva^gamma

Rovnica ako vždy jednoduchá a ak sa zdá niekomu v niečom známa, tak sa ani veľmi nepletie. Rovnakú rovnicu sme mali pre moduláciu jasu bodového svetla. Opäť aj tu môžu nastať dva prípady. Potrebujeme utlmiť príliš jasný obraz a alebo naopak jas zdvihnúť. Exponentmi medzi 0-1 jas zvýši a naopak exponentmi 1-nekonečno znižujú. Na nasledujúcom grafe vidíte výsledný priebeh:

Graf gamma korekcia - Tvorba shaderov v HLSL

Implementácia algoritmu je rovnaká ako u predchádzajúcich shaderov:

float3 ret=pow(color.rgb,gamma);
return float4(ret, color.a);

A premennú gamma dávame ako vstupný parameter:

float gamma=2.2;

Na výsledok s pôvodným obrázkom, obrázkom s gamma na 1 / 2,2 as gamma 2,2 sa môžete pozrieť nižšie:

Obrázok s gamma korekciou v XNA - Tvorba shaderov v HLSL

Nie, nie je to montáž, výstup mi už takto generuje shader. Nie je to nič zložité, stačilo iba zapodmínkovat výpočet asi takto:

float3 ret=color.rgb;
if(UV.x>0.33)ret=pow(color.rgb,1/gamma);
if(UV.x>0.66)ret=pow(color.rgb,gamma);

Obvyklá hodnota pre väčšinu obrazoviek sa pohybuje v rozmedzí 1,8 až 2,4. To len pre kompletnú informáciu.

Toonshading

Toonshading je mierne zložitejšie technika. Podľa jasu vyberieme farbu z druhej pomocnej textúry a tak farbu zameníme. Už je na nás akú paletu zvolíme. Predovšetkým si vytvoríme textúru s novou paletou farieb. Ja som vzal desať náhodných farbičiek, šírka textúry teda bude 10 a výška 1.

Tooning efekt v XNA - Tvorba shaderov v HLSL

A textúru pridáme do projektu. Poďme na shader. Najprv musíme pridať jednu textúru do poľa samplerov:

sampler2D tex[2];//2 textury

A vypočítame si intenzitu pre daný pixel, napríklad tým zložitejším spôsobom s váhami:

float intensity = 0.3f * color.r + 0.59f * color.g + 0.11f * color.b;

A túto hodnotu použijeme pre určenie novej farby z druhého samplera. Druhá súradnice bude vždy nula, pretože textúra má vlastne len jeden rozmer.

float3 toon=tex2D(tex[1],float2(intensity,0));

A všetko pošleme na výstup:

return float4(toon, color.a);

A to je celý shader. Jeho obsluha bude veľmi jednoduchá. V triede (je rovnaká ako ostatné) si iba konštruktor odovzdáme meno textúry s paletou:

Texture2D Toon;
string toontexture;

public ToonShading(Game g, string toontexture): base("postprocesory/toon",g) {
  this.toontexture = toontexture;
}

Prepíšeme metódu Load a textúru nahráme:

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

A v metóde Draw nastavíme textúru do druhého samplera:

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

Tým sme nastavili textúru ako aktívny. A to je celé. Výsledok bude pre moju textúru vyzerať nasledovne:

Toon efekt v XNA - Tvorba shaderov v HLSL

Detekcia hrán

Občas je potrebné tiež detekovať hrany v obraze. Je to napríklad jedna z metód anti-aliasingu. A určite je dobré tento shader poznať. Metód je veľa, ale ja použijem takzvaný sobelův filter. Je to matica (áno zas matice) 3x3 o nasledujúcom rozložení:

-1 0 1      -1 -2 -1
-2 0 2       0  0  0
 1 0 1       1  2  1

Potrebujeme matice dve, jedna je pre horizontálne detekcie a druhá pre vertikálne. Avšak je možné maticu rotovať po 45% a zisťovať tak i ďalšie prechody. Predovšetkým si nadefinujeme konštanty. Prvý bude hodnota, ktorá nám určí hranicu medzi hranou a nehraný:

float treshold=0.05;

Nasledujú dve premenné pre veľkosti jedného pixelu:

float pixelx=0.001;
float pixely=0.002;

Pre zjednodušenie si napíšeme ľahkú funkciu, ktorá nám vypočíta jas na danom bodu. HLSL rovnako ako ďalšie programovacie jazyky podporuje písanie funkcií. Tá musí však byť deklarovaná pred tým, než ju použijeme, teda rovnako ako v C-čku. Funkcia je to ľahká, takže bez komentára:

float Jas(float2 uv){
  float3 color= tex2D(tex[0], uv);
  return (color.r+color.g+color.b)/3;
}

Použil som jednoduchú variantu, ale môžeme použiť aj tú s váhami. Sobelovy matica aplikujeme následujícně:

float valx=-1*Jas(float2(UV.x-pixelx,UV.y-pixely))+Jas(float2(UV.x+pixelx,UV.y-pixely));
valx+=-2*Jas(float2(UV.x-pixelx,UV.y))+2*Jas(float2(UV.x+pixelx,UV.y));
valx+=-1*Jas(float2(UV.x-pixelx,UV.y+pixely))+Jas(float2(UV.x+pixelx,UV.y+pixely));

A pre vertikálne smer:

float valy=-1*Jas(float2(UV.x-pixelx,UV.y-pixely))-2*Jas(float2(UV.x,UV.y-pixely))-1*Jas(float2(UV.x+pixelx,UV.y-pixely));
valy+=1*Jas(float2(UV.x-pixelx,UV.y+pixely))+2*Jas(float2(UV.x,UV.y+pixely))+1*Jas(float2(UV.x+pixelx,UV.y+pixely));

A na záver len rozhodneme, či je na danom mieste prechod alebo nie. A to tak, že sčítame druhej mocniny oboch vypočítaných hodnôt a porovnáme ich s hranicou:

if((valx*valx+valy*valy)<treshold)return tex2D(tex[0],UV);
return float4(0,0,0, 1);

A to je všetko :-) Výsledok vyzerá nejako takto:

Detekcia hrán pomocou shader v XNA - Tvorba shaderov v HLSL

Tak a to by bolo pre dnešné diel všetko. Postprocesové efekty sú pekné tému, ale veľmi to nevydrží. Nepoznám ich totiž toľko a pretože sú naozaj len na pár riadkov, tak sa ich do jedného dielu vojde naozaj dosť. Snáď vás detekcia hrán neodradila, je už pravda zložitejšie. Budem sa tešiť ako vždy na komentáre, otázky, nápady, sťažnosti a na videnie zase 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é 99x (3.08 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
XNA a HLSL - postprocesorov
Všetky články v sekcii
Tvorba shaderov v HLSL
Preskočiť článok
(neodporúčame)
XNA a HLSL - postprocesorov Sepia, Alfa masking a Noise
Č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