3. díl - XNA a HLSL - Světla podruhé

C# .NET XNA game studio HLSL XNA a HLSL - Světla podruhé

Vítejte znova. V tomto dílu si přidáme ambientní a directional (směrové) světlo a také odlesky. Aby ale výsledky byly vidět, potřebujeme jiný testovací model. Nejlépe něco kulatého. Nakonec jsem se pochlapil a stvořil v cinemě novou nádheru. Takže si model a textury do projektu importujte. Druhou maličkostí, kterou je potřeba provést, je aplikovat měřítko, aby byl model větší a lépe se nám s ním pracovalo. Zvolil sem 2,5x a potřebujeme takto zvětšit všechny meshe. Proto tedy modifikujeme World matici. Nezapomeňme na pořadí násobení:

part.Effect.Parameters["World"].SetValue(transforms[mesh.ParentBone.Index]*Matrix.CreateScale(2.5f)*rotace);

Ambientní světlo

První skutečné světlo, které bude přítomno vždycky je ambientní světlo. Tedy jakýsi světelný základ. Já ho používám jako jakési světlo stínu. Tedy pouze ním budou „osvětleny“ neosvícené plochy. Budeme potřebovat pouze jeho barvu. Žádné další informace nejsou potřeba. Přidáme do shaderu nový parametr:

float3 AmbientColor;

Pixel shader bude potřebovat poměrně přeorganizovat, protože budeme sčítat dohromady všechny světelné složky a ty pak aplikujeme na texturu. Proto si vytvoříme proměnnou lights:

float3 lights=AmbientColor;

pro světla a druhou color. A rovnou ji nastavíme na ambientní složku. Pro barvu povrchu uděláme druhou:

float3 color=DiffuseColor;

A tu nastavíme na diffusní barvu. Je-li povolena textura, tak ji s diffusní barvou smícháme vynásobením:

if(TextureEnabled){
  color*=tex2D(TextureSampler,input.UV);
}

Posledním krokem, co nám zbývá, je obě barvy jak světel tak z modelu smíchat dohromady a poslat na výstup:

float3 output = saturate(lights) * color;

return float4(output,1);

Dovolil jsem si použít funkci saturate. Ta nedělá nic jiného, než že nám jakékoliv číslo ořízne do rozsahu 0 – 1. Je tam, protože se může snadno stát, že nasčítáme víc světel a hodnoty by se dostaly mimo tento rozsah a za tu jistotu, že bude vše v pořádku, mi to prostě stojí. Shader je hotov, nyní stačí pouze přidat do hlavního programu proměnnou pro barvu světla a do shaderu ji poslat:

effect.Parameters["AmbientColor"].SetValue(AmbientColor.ToVector3());

Hotovo. Jak scéna vypadá se světlem a bez něj se můžete podívat na následujícím obrázku.

Ambientní světlo v X# .NET XNA

Světlo nezdůrazňuje hrany a proto pod ním vypadají všechny objekty placatě. Proto si přidáme trochu realičnosti dalším světlem.

Directional – Směrové světlo

Směrové světlo nám onu větší realičnost přidá. Jeho výpočet je ovšem složitější. Je zapotřebí znát normálu. Co to je normála? Normála je vektor, který je kolmý k ploše a ještě nejlépe, když má velikost jedna. Jak normály vypadají si můžete prohlédnout na následujícím obrázku. Jsou to ty žluté čáry.

Normálový vektor

Ty potřebujeme dostat do našeho shaderu. Přidáme si do vstupní struktury novou položku, sémantiku použijeme NORMAL0, celá struktura bude vypadat takto:

struct VertexShaderInput{
  float4 Position : POSITION0;
  float2 UV:TEXCOORD0;
  float3 Normal:NORMAL0;
};

Taktéž přidáme do výstupní struktury, ovšem zde použijeme sémantiku TEXCOORD1, tedy pro souřadnice textury. Moc na výběr zde totiž nemáme. Celá struktura bude vypadat takto:

struct VertexShaderOutput{
  float4 Position : POSITION0;
  float2 UV:TEXCOORD0;
  float3 Normal:TEXCOORD1;
};

Ve vertex shaderu musíme normálu transformovat maticí World. Dostane tak rotace a pozici:

output.Normal=mul(input.Normal,World);

Bohužel také měřítko, proto jej budeme muset později normalizovat. Přidáme ještě dva parametry. Směr světla a jeho barvu:

float3 DirectionalLight;
float3 DirectionalLightColor;

Nyní máme vše připraveno pro samotný výpočet. Jak ho ale provést? Jak jinak, než snadno. Směrové světlo se jinak nazývá také Lambertian light, a jeho rovnice je následující:

diff=max(l x n,0)

kde l je směrnice světla, n je normála a x není nic jiného než takzvaný dot product. To je skalární součin dvou vektorů. O ten se naštěstí nemusíme starat, protože na něj máme připravenou funkci. Diff je číslo jakou měrou se má světlo na danou plochu projevit. Funkci max jistě znáte, ta nám zde nepovolí záporná čísla o které nestojíme. Pojďme si to napsat do kódu. Předně si normalizujeme oba vektory:

float3 lightDir = normalize(DirectionalLight);
float3 normal = normalize(input.Normal);

a provedeme samotný výpočet světla.

lights += saturate(dot(lightDir, normal)) * DirectionalLightColor;

Funkce dot nám provede onen skalární součin. Vynásobíme, abychom dostali výslednou barvu a je to. V hlavním programu pak nastavíme obě hodnoty kupříkladu na:

effect.Parameters["DirectionalLight"].SetValue(new Vector3(0,0,0)-new Vector3(-1,0,0));
effect.Parameters["DirectionalLightColor"].SetValue(Color.Yellow.ToVector3());

A kocháme se výsledkem:

Směrové světlo v C# .NET XNA

Světlo přidalo výsledku plastičnost, tvary nám pěkně vystoupily. Něco výsledku ale ještě schází. Realičnost podtrhneme odlesky.

Specular light

Nebo-li odlesky. Jsou doplňujícím světlem. Ono to vlastně není tak docela nové světlo, ale jen jedna složka ostatních světel. Odlesky mohou generovat všechna světla. Rovnice je takováto:

kspec=max(r x v,0)^n

Kde r je vektor odražený od zdroje světla (ten vypočteme s pomocí normály, kterou máme z minula), x je opět dot product, v je vektor, kterým se na dané místo dívá kamera. Opět vše ořízneme a záporná čísla zanedbáváme funkcí max. Na závěr je ale ještě toto vše umocněno. Tím lze měnit velikost odlesku. Čím větší mocnina, tím je odlesk menší. Rovnice je velmi podobná té předchozí. Ona vlastně funguje velmi podobně. Pro realizaci tohoto efektu potřebujeme znát pouze jednu doplňující informaci - pozici kamery, na které je vlastně celý postup založen. Potřeba je pak samozřejmě i ona mocnina a barva odlesku:

float3 CameraPosition;
float SpecularPower=32;
float3 SpecularColor=float3(1,1,1);

Pozici, ze které se dívá na ono místo kamera, budeme potřebovat vypočítat ve vertex shaderu. Přidáme si do výstupní struktury novou proměnnou:

float3 ViewDirection : TEXCOORD2;

Již jste si všimli, že do texcoordu jde narvat prakticky cokoliv, ano, je to tak :-) Vypočteme směrnici vektoru. Jistě si ze školy pamatujete, že to je koncový bod mínus počáteční, takže tedy:

output.ViewDirection=worldPosition-CameraPosition;

A pak ještě celé světlo:

lights+=pow(saturate(dot(reflect(lightDir,normal),normalize(input.ViewDirection))),SpecularPower)*SpecularColor;

To je onen vzoreček přepsaný do podoby programové. Funkce reflect nám provede onen zrcadlový odraz paprsku a funkce pow není nic jiného než mocnina. Náš materiál nastavíme o další dvě hodnoty SpecularPower a SpecularColor:

public Vector3 SpecularColor{
  get;
  set;
}

public float SpecularPower{
  get;
  set;
}

A v metodě SetEffectParametrs pak pouze nastavíme hodnoty o shaderu:

ef.Parameters["SpecularColor"].SetValue(SpecularColor);
ef.Parameters["SpecularPower"].SetValue(SpecularPower);

Obě hodnoty lze získat z BasicEffectu. Kam jsou nahrány přímo z modelu. Takže záleží jen na kvalitě grafika jak tyto hodnoty nastaví. Takže do metody LoadContent přidáme hned pod vytvoření nové instance efektu následující přiřazení:

mat.SpecularColor = ef.SpecularColor;
mat.SpecularPower = ef.SpecularPower;

Ve výsledku můžeme spatřit drobná kulatá zesvětlení bílého světla. Model má jednu chybu, že jsou hodnoty odrazivosti nastavené velmi nízko. Ale nevadí. Snad do příště přijdu na to, jak to provést. Celek vypadá tedy následovně:

Hotová světla v C# .NET XNA

Tak a to by bylo pro dnešní díl všechno. Příště se podíváme na zbylá světla. A mě nezbývá nic jiného, než těšit na komentáře a případné připomínky či dotazy.


 

Stáhnout

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

 

  Aktivity (1)

Článek pro vás napsal vodacek
Avatar
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.

Jak se ti líbí článek?
Celkem (2 hlasů) :
55555


 


Miniatura
Předchozí článek
XNA a HLSL - Světla poprvé
Miniatura
Všechny články v sekci
Tvorba shaderů v HLSL
Miniatura
Následující článek
XNA a HLSL - Světla potřetí

 

 

Komentáře

Avatar
David Čápka
Tým ITnetwork
Avatar
David Čápka:

Tenhle díl mi přijde jako jeden z nejzajímavějších, jen na konci je to opravdu moc nasvícené :)

Odpovědět  +1 10.8.2013 20:13
Miluji svou práci a zdejší komunitu, baví mě se rozvíjet, děkuji každému členovi za to, že zde působí.
Avatar
vodacek
Redaktor
Avatar
 
Odpovědět 10.8.2013 20:20
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 2 zpráv z 2.