NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!
NOVINKA - Online rekvalifikační kurz Java programátor. Oblíbená a studenty ověřená rekvalifikace - nyní i online.

XNA a HLSL - Světla poprvé

V minulé lekci, XNA a HLSL - Textury, jsme se podívali na to, jak na trojúhelníky pomocí shaderů nanášet textury.

V tomto dílu si přidáme základní osvětlení. Osvětlení objektů bylo jedním z důvodů vzniku shaderů a stále se jedná o jejich nejčastější použití. Možností osvětlování máme spousty, pojďme si je shrnout do seznamu:

  • Diffuse
  • Ambient
  • Directional
  • Point
  • Spot
  • Specular

Záměrně jsem uvedl anglické názvy, je dobré si na ně zvyknout. Netřeba říkat, že se pak lépe googlí různé další postupy implementace. Vezměme si je tedy popořadě.

Diffuse

Není to tak docela světlo, ale já ho do osvětlení zařadím. Už jsme se s ním setkali v bludišti při změně barvy podlah pro start a cíl. Pomocí něj můžeme modifikovat výslednou barvu textury na objektu a tak snadno vytvořit různé barevné variace téhož objektu, aniž by bylo potřeba měnit texturu.

Ambient

Ambientní světlo je světlo, které dopadá na objekt z okolí. Je to jakýsi světelný základ. Toto světlo nemá bod, ze kterého vychází a působí na všechny objekty stejně. Pokud použijeme pouze toto světlo, tak se vše bude jevit velmi ploché. Případná zaoblení objektu vizuálně zmizí. Proto jej vždy budeme kombinovat s dalšími světly.

Directional

Česky směrové světlo. Je to světlo, které má směr, ze kterého vychází. Ale nemá přesné místo. Působí tedy také na všechny objekty stejně. Pro jeho realizaci je potřeba znát u každého modelu takzvané normály. Více o normálách se dozvíte posléze. Světlo zdůrazňuje zakřivení objektů, protože osvětluje pouze ty strany, které jsou ke světlu přivrácené. Ekvivalentem v reálném světě je slunce. Jeho paprsky také svítí na vše rovnoběžně.

Point

Nebo-li bodové světlo. Je to světlo, které má místo odkud vychází a vzdálenost, na kterou působí. Postupně se vzdáleností od centra jeho intenzita klesá. Z popisu je už vidět, že se jedná o složitější výpočet, budeme taktéž potřebovat normály. Představit si jej můžeme jako svíčku.

Spot

Poslední základní světlo je reflektor. Je to světlo vycházející z bodu určitým směrem. Má také kruh, na který působí, vytváří tentokráte kužel. Představit si jej můžeme jako divadelní reflektor. Moc jsem ho nikdy nepoužil, ale přesto se občas hodí.

Specular

Je to speciální světlo pro odlesky. Jeho realizace je možná pro všechny světla nahoře uvedená a nejedná se o samostatné světlo, ale spíš o jednu z dalších složek světel předchozích pro větší věrohodnost. K jeho realizaci potřebujeme znát normálu a také pozici kamery.

Výčet světel není kompletní. Lze v něm pokračovat dalšími a složitějšími světly, které se svou důmyslností stále více a více přibližují k realitě. My si však vystačíme s těmito pár základními světly.

Implementace

Způsobů jak světla realizovat je dost. Já se pokusím popsat dva. První je starší, nicméně pro určité situace stále potřebný - takzvané dopředné (frontal) vykreslování. Při něm kreslíme modely a přímo na ně aplikujeme osvětlení. Tak jak to ostatně dělá BasicEffect. Můžeme v zásadě použít pro každé jednotlivé světlo speciální shader, ovšem musíme počítat s tím, že model pak musíme vykreslit, tolikrát kolik světel na něj chceme aplikovat. Toto však lze obejít tím, že do jednoho shaderu přidáme více světel. Ale i tak čím více světel, tím více vykreslení všech modelů. Druhý a moderní způsob je takzvané zpožděné (deffered) vykreslování. A právě v něm je budoucnost. Modely je nutné vykreslit pouze jednou (podle implementace 2x) a do scény pak přidat velmi levně světla. Hodí se to pro případy, kdy je potřeba mít spoustu světel, ale vše klade vysoké nároky na grafickou kartu. Výhodou je také, že vykreslovaná světla se aplikují jen na ty body na které svítí. Není potřeba kontrolovat všechny body na obrazu, jako je to nutné u dopředného vykreslování. Velké mínus je nemožnost práce s průhlednými objekty a zde musíme opět sáhnout ke starému způsobu. Už jen proto budeme potřebovat mít napsané oba způsoby. Nejraději bych psal rovnou i systém světel pro engin, ale bylo mi řečeno, že není dobré engine zatahovat do všech sekcí. Ale přesto si neodpustím materiály.

Materiály

Ještě než vytvoříme první světlo, musíme se na to připravit. V materiálech budeme skladovat jednotlivé vlastnosti vykreslovaných povrchů. Pro začátek si vystačíme s texturou, diffusní barvou. Ale parametry budou jistě během dalších dílů přibývat. Určitě se dočkáme normálové mapy, mapy pro odlesky, mapy pro glow efekt a dalších. Materiál bude potřeba vytvořit pro všechny části meshe, odkud také získáme texturu a diffusní barvu. Obyčejně se materiály skladují v proměnné Tag, která pro tyto doplňující informace určena, jenomže mě se tento způsob neosvědčil. Vždy se nám načítá pouze jedna instance modelu, která je posléze znovu a znovu používána. Pokud bychom nastavili Tag jednomu modelu, vyskytoval by se pak všude. Proto si vytvoříme pole zvlášť a tento problém nám odpadne. Do projektu si tedy přidáme třídu Material, učiníme jí veřejnou. Přidáme proměnné pro texturu a barvu. Tu ale uložíme už jako 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 kolekce a naplníme je v Load metodě. 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 metodě Draw upravíme vykreslení:

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 ještě přidáme metodu SetEffectParametres, která se postará o nastavení parametrů 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še hotovo a tak si konečně přidáme ono světlo.

Diffuse light

Všechny parametry máme připravené a tak jen přidáme položku do shaderu.

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

Implicitní barva je tedy bílá. A pixel shader upravíme následovně:

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

Diffusní barvu musíme nastavit o alfa kanál. To lze snadno provést následovně:

float4(DiffuseColor,1)

Vynásobením obou barev získáme pak barvu novou. A to je vše. Jak vypadá samotná diffusní složka a jak otexturovaný model s a bez ní si můžete prohlédnout na následujícím obrázku.

Tvorba shaderů v HLSL

To by bylo pro dnešní díl všechno.

Příště, XNA a HLSL - Světla podruhé, si přidáme další světla, konkrétně ambientní a směrové.


 

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 121x (1.59 MB)
Aplikace je včetně zdrojových kódů v jazyce C# .NET

 

Předchozí článek
XNA a HLSL - Textury
Všechny články v sekci
Tvorba shaderů v HLSL
Přeskočit článek
(nedoporučujeme)
XNA a HLSL - Světla podruhé
Článek pro vás napsal vodacek
Avatar
Uživatelské hodnocení:
4 hlasů
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