4. díl - XNA a HLSL - Světla potřetí

C# .NET XNA game studio HLSL XNA a HLSL - Světla potřetí

Vítejte v dalším dílu. Dnes se podíváme na bodové (point) světlo a reflektor (spot). Výpočty spojené s realizací obou světel jsou už trochu náročnější, ale doufám, že nebudou dělat problém.

Point light

Bodové světlo se nejvíce přibližuje skutečnému zdroji světla. Má svoje centrum, kde je jas nejvyšší, ten se pak snižuje postupně do ztracena. Má tedy tvar koule. Na následujícím diagramu je zanesena závislost jasu na vzdálenosti.

Graf závislosti jasu světla na vzdálenosti

Intenzita jasu nebude klesat lineárně. Jak jste si už zvykli, můžeme to také vyjádřit rovnicí:

katt=1 – (d/r)^f

Kde katt je onen jas, d je vzdálenost středu světla od bodu, který vykreslujeme. r je poloměr kružnice, kterou světlo vytváří. Jedničku odečítáme kvůli převrácení, takto by bylo nejvíce jasno na okrajích. Poslední, co zbývá osvětlit, je f, což je koeficient, který udává jak bude převodní křivka z obrázku nahoře zakřivená. Jak je zakřivení ovlivněno koeficientem se můžete podívat na graf níže:

Zakřivení a koeficient

Dost ale teorie, pojďme si toto světlo napsat. Začneme jako vždy novými parametry:

float3 PointLightPosition;
float3 PointLightColor;
float PointLightAttenuation;
float PointLightFalloff=2;

První je pozice světla, jeho barva, poloměr dosahu a onen koeficient. Pro výpočet vzdálenosti vykreslovaného bodu od středu světla budeme potřebovat znát pozici tohoto bodu. Bude proto potřeba tuto hodnotu předat až do pixel shaderu. Upravíme si výstupní strukturu:

float4 WorldPosition:TEXCOORD3;

Sémantika opět tradiční, do které se vleze prostě všechno. Ve vertex shaderu tuto hodnotu doplníme na již vypočtenou hodnotu:

output.WorldPosition=worldPosition;

A to bude všechno. Vertex shadery, jak jste si už povšimli, jsou velmi snadné. Veškerá magie se odehrává až při výpočtech jednotlivých pixelů. Nebylo to tak vždycky, ale to už zas odbočuji od tématu. Pixel shader nám musí pro každý pixel provést výpočet osvětlení podle rovnice zmíněné nahoře. V kódu to bude vypadat následovně. Nejprve si vypočteme vzdálenost světla od bodu, který vykreslujeme:

float d=distance(PointLightPosition,input.WorldPosition);

Používáme funkci distance, která se o výpočet postará, zbytek rovnice pak vypadá následovně:

lights+=(1-pow(saturate(d/PointLightAttenuation),PointLightFalloff))*PointLightColor;

Shader je tím hotov. Shadery jsou dobré v tom, že jejich kód je opravdu krátký. Ještě musíme v našem programu nastavit hodnoty pro světlo. Pokusně jsem zvolil tyto:

effect.Parameters["PointLightPosition"].SetValue(new Vector3(50,40,0));
effect.Parameters["PointLightColor"].SetValue(Color.White.ToVector3());
effect.Parameters["PointLightAttenuation"].SetValue(100);

Světlo bude mít tedy bílou barvu, jeho střed je na (50,40,0) a poloměr je 100. Mělo by být vše hotovo. Zkušený čtenář už ale moc dobře ví, že skoro pokaždé, když se vytasím s touto větou, tak vše hotovo rozhodně není. Je tomu i nyní. Pojďme se ale nejdříve podívat na výsledek. Nutno podotknout že jsem se zbavil všech ostatních světel aby vyniklo jen světlo nové:

Point light v C# XNA

Žlutě je vyznačena koule, na kterou světlo působí. Ovšem scéna je nasvětlena špatně. Krásně je to vidět na krychli. Ta má nasvícený i vršek, i když je zdroj světla nalézá níže. Výpočet není kompletní, bude potřeba do něj započítat i normály povrchů, stejně jakoby se jednalo o směrové světlo. Vrátíme se tedy opět do pixel shaderu. Zrecykloval bych proměnnou lightDir, protože její význam bude stejný:

lightDir=normalize(PointLightPosition-input.WorldPosition);

Pro připomenutí směrový vektor znormalizujeme. To jak moc na dané místo získáme pak stejně jako u směrového světla:

float diffuse=saturate(dot(normal,lightDir));

Normála je stále stejná a tak ji vesele recyklujeme. Tímto pak stačí vynásobit to, co již máme. Celek bude tedy vypadat následovně:

lights=(1-pow(saturate(d/PointLightAttenuation),PointLightFalloff))*PointLightColor*diffuse;

Jak se změnil výsledek můžete sledovat na následujícím obrázku:

Bodové světlo v C# XNA

Zmizely pouze vršky, které osvětlené být nemají. Bodové světlo je hotovo.

Spot light

Reflektorové světlo se výpočtem od bodového příliš neliší. Má bod, ze kterého vychází, směrnici kterou svítí a také úhel. Úhel určuje jak široký bude výsledný kotouč. Viz následující obrázek:

Úhel reflektorového světla

A jako vždy nemůže chybět rovnice:

katt=1-(dot(p-lp,ld)/cos(a))^f

Kde lp je pozice světla, p je pozice vykreslovaného pixelu, ld je směrnice světla, a je úhel reflektoru a f je stejné jako u rovnice nahoře. Systém je stejný jako u všech předcházejících světel. Předně horda parametrů:

float3 SpotLightPosition;
float3 SpotLightColor;
float3 SpotLightDirection;
float SpotLightAngle;
float SpotLightFalloff=20;

Nějak se nám množí. Ve vertex shaderu máme již vše, další informace nepotřebujeme. Zaobírat se tedy budeme pouze druhou částí. Jako u předešlého světla si vypočteme jak moc světlo ovlivňuje vykreslovaný povrch:

lightDir=normalize(SpotLightPosition-input.WorldPosition);
diffuse=saturate(dot(normal,lightDir));

Proměnné můžeme z recyklovat, jejich význam je stejný. Dále si vypočteme první část ze vzorce:

d=dot(-lightDir, normalize(SpotLightDirection));
float a=cos(SpotLightAngle);

Povšimněte si mínusu před proměnnou lightDir, pro správný výpočet potřebujeme opačný vektor k již získanému vektoru. Jeho otočení provedeme právě tím mínusem. A pokud je pixel uvnitř kuželu, tak jej osvětlíme:

if(a<d) lights+=1-pow(saturate(a/d),SpotLightFalloff)*diffuse*SpotLightColor;

Pokud není, tak je intenzita světla nula a není potřeba se zbylým výpočtem zaobírat. A to je celý shader. Do programu si pak už jen přidáme nastavení parametrů, třebas takovéto:

effect.Parameters["SpotLightPosition"].SetValue(new Vector3(200,150,0));
effect.Parameters["SpotLightDirection"].SetValue(new Vector3(-200,-150,0));
effect.Parameters["SpotLightAngle"].SetValue(MathHelper.ToRadians(30/2));
effect.Parameters["SpotLightColor"].SetValue(Color.White.ToVector3());

Úhel nezapomeneme vydělit dvěma. Samotné světlo bude vypadat následovně:

Reflektorové světlo v C# XNA

Tak a to bychom měli. Máme úplný soubor světel. Příště se podíváme na něco trochu jiného, budou to post procesové efekty. Ty jsou ještě snazší než osvětlení, takže pokud jste HLSL zatím na chuť nepřišli a dělá vám problém pochopit co se to tam děje, tak zde bude vaše poslední šance. Čekám také jako vždycky na komentáře, dotazy a spol. Takže na viděnou příště.


 

Stáhnout

Staženo 150x (3.06 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 (4 hlasů) :
55555


 


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

 

 

Komentáře

Avatar
magic44
Redaktor
Avatar
magic44:

Dobrej tutorial :), ale jak mám udělat, abych měl zase přeba jen bodové světlo? Teď tam mám vidět jenom Spot light a bodové. A když dám Spot v Game pryč, tak jsou objekty zrnité a není tam žádné světlo (jako bez shaderu). A přitom mi to odstranění ostatních světel u zkoušení bodového fungovalo.

Editováno 12.9.2013 17:37
Odpovědět 12.9.2013 17:33
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

bez toho cos tam vyváděl těžko můžu vědět co se děje

 
Odpovědět 13.9.2013 9:27
Avatar
magic44
Redaktor
Avatar
Odpovídá na vodacek
magic44:

Mám to stejné, jako ty (na konci tohoto dílu). Jen potřebuju vědět, jak mám udělat, abych tam měl jenom jedno světlo (specular, point, nebo spot). Teď tam totiž mám point a spot dohromady.

Odpovědět 14.9.2013 10:15
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

dát tomu co nechceš barvu na černou

 
Odpovědět 14.9.2013 16:55
Avatar
magic44
Redaktor
Avatar
Odpovídá na vodacek
magic44:

Hmm.. to mi nefunguje. Jestli tě dobře chápu, tak to je ve třídě Game1 v Draw. Ale pořád tam mám ten Spot. :/

Odpovědět 16.9.2013 17:10
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

tak to budeš muset někam svůj kod poslat

 
Odpovědět 16.9.2013 17:17
Avatar
magic44
Redaktor
Avatar
Odpovídá na vodacek
magic44:

OK posílám přes PM odkaz na leteckou postu.

Odpovědět 16.9.2013 17:39
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
magic44
Redaktor
Avatar
magic44:

Jako by tam bylo navíc nějaké jiné světlo, ale specular a spot jsou nastaveny na black.

Odpovědět 16.9.2013 21:32
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
Avatar
vodacek
Redaktor
Avatar
Odpovídá na magic44
vodacek:

a není to to směrové

 
Odpovědět 16.9.2013 21:40
Avatar
magic44
Redaktor
Avatar
Odpovídá na vodacek
magic44:

Ne, určitě to je point. Směrový a spot mám nastavený na černou.

Editováno 17.9.2013 17:38
Odpovědět 17.9.2013 17:37
Moudrý člověk nechce být lepší než ostatní, ale lepší, než byl sám včera.
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 10 zpráv z 10.