IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

XNA a HLSL - Svetlá prvýkrát

V tomto dieli si pridáme základné osvetlenie. Osvetlenie objektov bolo jedným z dôvodov vzniku shaderov a stále sa jedná o ich najčastejšie použitie. Možností osvetľovanie máme hŕbu, poďme si ich zhrnúť do zoznamu:

  • Diffuse
  • Ambient
  • Directional
  • point
  • spot
  • Specular

Zámerne som uviedol anglické názvy, je dobré si na ne zvyknúť. Netreba hovoriť, že sa potom lepšie Google rôzne ďalšie postupy implementácie. Zoberme si ich teda poporiadku.

Diffuse

Nie je to tak celkom svetlo, ale ja ho do osvetlenia zaradím. Už sme sa s ním stretli v bludisku pri zmene farby podláh pre štart a cieľ. Pomocou neho môžeme modifikovať výslednú farbu textúry na objekte a tak ľahko vytvoriť rôzne farebné variácie toho istého objektu, bez toho aby bolo potrebné meniť textúru.

Ambient

Ambientné svetlo je svetlo, ktoré dopadá na objekt z okolia. Je to akýsi svetelný základ. Toto svetlo nemá bod, z ktorého vychádza a pôsobí na všetky objekty rovnako. Ak použijeme iba toto svetlo, tak sa všetko bude javiť veľmi ploché. Prípadná zaoblenie objektu vizuálne zmizne. Preto ho vždy budeme kombinovať s ďalšími svetlami.

Directional

Slovensky smerové svetlo. Je to svetlo, ktoré má smer, z ktorého vychádza. Ale nemá presné miesto. Pôsobí teda tiež na všetky objekty rovnako. Pre jeho realizácii je potrebné poznať pri každom modeli takzvané normály. Viac o normálách sa dozviete neskôr. Svetlo zdôrazňuje zakrivenie objektov, pretože osvetľuje iba tie strany, ktoré sú k svetlu privrátenej. Ekvivalentom v reálnom svete je slnko. Jeho lúče tiež svieti na všetko rovnobežne.

Point

Alebo ak bodové svetlo. Je to svetlo, ktoré má miesto odkiaľ vychádza a vzdialenosť, na ktorú pôsobí. Postupne sa vzdialenosťou od centra jeho intenzita klesá. Z opisu je už vidieť, že sa jedná o zložitejší výpočet, budeme taktiež potrebovať normály. Predstaviť si ho môžeme ako sviečku.

Spot

Posledný základný svetlo je reflektor. Je to svetlo vychádzajúce z bodu určitým smerom. Má tiež kruh, na ktorý pôsobí, vytvára tentokrát kužeľ. Predstaviť si ho môžeme ako divadelný reflektor. Veľmi som ho nikdy nepoužil, ale napriek tomu sa občas hodí.

Specular

Je to špeciálna svetlo pre odlesky. Jeho realizácia je možná pre všetky svetlá hore uvedená a nejedná sa o samostatné svetlo, ale skôr o jednu z ďalších zložiek svetiel predchádzajúcich pre väčšiu vierohodnosť. K jeho realizácii potrebujeme poznať normálu a tiež pozíciu kamery.

Výpočet svetiel nie je kompletný. Možno v ňom pokračovať ďalšími a zložitejšími svetlami, ktoré sa svojou dômyselnosťou stále viac a viac približujú k realite. My si však vystačíme s týmito pár základnými svetlami.

Implementácia

Spôsobov ako svetla realizovať je dosť. Ja sa pokúsim popísať dva. Prvá je starší, avšak pre určité situácie stále potrebný - takzvané doprednej (frontal) vykresľovanie. Pri ňom kreslíme modely a priamo na ne aplikujeme osvetlenia. Tak ako to napokon robí BasicEffect. Môžeme v zásade použiť pre každé jednotlivé svetlo špeciálny shader, avšak musíme počítať s tým, že model potom musíme vykresliť, toľkokrát koľko svetiel na neho chceme aplikovať. Toto však možno obísť tým, že do jedného shader pridáme viac svetiel. Ale aj tak čím viac svetiel, tým viac vykreslenie všetkých modelov. Druhý a moderný spôsob je takzvané oneskorené (deffered) vykresľovanie. A práve v ňom je budúcnosť. Modely je nutné vykresliť iba raz (podľa implementácie 2x) a do scény potom pridať veľmi lacno svetla. Hodí sa to pre prípady, kedy je potrebné mať veľa svetiel, ale všetko kladie vysoké nároky na grafickú kartu. Výhodou je tiež, že vykresľovanie svetla sa aplikujú len na tie body na ktoré svieti. Nie je potreba kontrolovať všetky body na obrazu, ako je to nutné pri dopredného vykresľovanie. Veľké mínus je nemožnosť práca s priehľadnými objektmi a tu musíme opäť siahnuť k starému spôsobu. Už len preto budeme potrebovať mať napísané oba spôsoby. Najradšej by som písal rovno aj systém svetiel pre engin, ale bolo mi povedané, že nie je dobré engine zaťahovať do všetkých sekcií. Ale napriek tomu si neodpustím materiály.

Materiály

Ešte než vytvoríme prvé svetlo, musíme sa na to pripraviť. V materiáloch budeme skladovať jednotlivé vlastnosti vykreslovaných povrchov. Pre začiatok si vystačíme s textúrou, diffusní farbou. Ale parametre budú určite počas ďalších dielov pribúdať. Určite sa dočkáme normálové mapy, mapy pre odlesky, mapy pre glow efekt a ďalších. Materiál bude potrebné vytvoriť pre všetky časti meshe, odkiaľ tiež získame textúru a diffusní farbu. Obyčajne sa materiály skladujú v premennej Tag, ktorá pre tieto doplňujúce informácie určené, lenže mňa sa tento spôsob neosvedčil. Vždy sa nám načítava len jedna inštancia modelu, ktorá je následne znovu a znovu používaná. Ak by sme nastavili Tag jednému modelu, vyskytoval by sa potom všade. Preto si vytvoríme polia zvlášť a tento problém nám odpadne. Do projektu si teda pridáme triedu Material, urobíme jej verejnú. Pridáme premenné pre textúru a farbu. Tú ale uložíme už ako vektor:

public class Material{
  private bool fTextureEnabled;

  public Texture2D Texture{
    get;
    set;
  }
  public bool TextureEnabled{
    get{
      return fTextureEnabled && Texture != null;
    }
    set{
      fTextureEnabled = value;
    }
  }
  public Vector3 DiffuseColor{
    get;
    set;
  }

  public Material(Texture2D texture, Vector3 diffColor):this(texture,diffColor,true){

  }

  public Material(Texture2D texture, Vector3 diffColor, bool textureEnabled){
    Texture = texture;
    DiffuseColor = diffColor;
    TextureEnabled = textureEnabled;
  }
}

Materiály uložíme do kolekcie a naplníme ich v Load metóde. Asi takto:

foreach (ModelMesh mesh in model.Meshes){
  foreach (ModelMeshPart part in mesh.MeshParts){
    BasicEffect ef = part.Effect as BasicEffect;
    Material mat = new Material(ef.Texture, ef.DiffuseColor);
    Materialy.Add(mat);
    part.Effect = effect;
  }
}

V metóde Draw upravíme vykreslenia:

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

A ešte pridáme metódu SetEffectParametres, ktorá sa postará o nastavenie parametrov efektu:

public virtual void SetEffectParametres(Effect ef){
  ef.Parameters["Texture"].SetValue(Texture);
  ef.Parameters["TextureEnabled"].SetValue(TextureEnabled);
  ef.Parameters["DiffuseColor"].SetValue(DiffuseColor);
}

Máme všetko hotové a tak si konečne pridáme ono svetlo.

Diffuse light

Všetky parametre máme pripravené a tak len pridáme položku do shader.

float3 DiffuseColor=float3(1,1,1);

Implicitná farba je teda biela. A pixel shader upravíme nasledovne:

if(TextureEnabled)return tex2D(TextureSampler,input.UV)*float4(DiffuseColor,1);
else return float4(DiffuseColor, 1);

Diffusní farbu musíme nastaviť o alfa kanál. To možno ľahko vykonať nasledovne:

float4(DiffuseColor,1)

Vynásobením oboch farieb získame potom farbu novú. A to je všetko. Ako vyzerá samotná diffusní zložka a ako otexturovaný model sa bez nej si môžete prezrieť na nasledujúcom obrázku.

Tvorba shaderov v HLSL

To by bolo pre dnešné diel všetko. Nabudúce si pridáme ďalšie svetla, konkrétne ambientné a smerové.


 

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 121x (1.59 MB)
Aplikácia je vrátane zdrojových kódov v jazyku C#

 

Predchádzajúci článok
XNA a HLSL - Textúry
Všetky články v sekcii
Tvorba shaderov v HLSL
Preskočiť článok
(neodporúčame)
XNA a HLSL - Svetlá druhýkrát
Č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