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.
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