Direct 3D - Svetlo
Pri designe hry nesmiete zabudnúť na svetlá. Bez nich vyzerá celá scéna jednotvárne a rozhodne sa nepribližuje realite. Spustite si svoju obľúbenú hru a pozorujte ako sú svetlá zasadené do prostredia. Všímajte si, kde sa nachádzajú, čo osvetľujú a aký typ svetiel autori použili. Cieľom týchto častí je, okrem iného, aby ste sa pri hraní pozerali na hru aj z programátorského pohľadu. Schválne hovorím hru, pretože na ne sa budeme predovšetkým zameriavať. To však neznamená, že svoje poznatky nemôžete uplatniť napr. pri tvorbe interaktívnych 3D prezentácií. Ale vráťme sa k téme. Svetlá vytvárajú správnu atmosféru. Všade naokolo je tma a jediné, čo vidíte na svojom monitory je svetlo pouličnej lampy. Preto ich využitie je také obrovské. Samozrejme, má to aj svoje nevýhody. Svetlá spomaľujú rýchlosť renderovania. Na druhej strane, dnes sa vyrábajú karty s priamou podporou svetiel (Napr. Geforce 2 ich má až 16), takže to už prestáva byť problémom. V súčasnosti sa na trh dostáva nová generácia kariet, ktorá dokáže prepočítavať aj tiene v reálnom čase. Tak sa môže stať, že uvidíme dynamické tiene každého listu na strome. Poďme sa pozrieť, čo nám Direct3D v tomto smere ponúka…
Teória
Každý pixel má určitú farbu. Pri použití svetla sa farba oboch prvkov (svetla + pixelu) zmieša a vypočíta sa farba výsledného pixelu. Svetlá môžeme rozdeliť na 4 typy:
Ambient - svetlo, ktoré osvetľuje všetky objekty rovnako. Dalo by sa prirovnať k slnku. Keď nastavíte tento typ svetla, zabezpečíte, že žiadny vertex nebude tmavší ako je hodnota svetla Ambient.
Point - bodové svetlo. Svieti z jedného bodu do všetkých strán.
Directional - Nemá žiadnu polohu zdroja. Svieti z jedného smeru do druhého. Častokrát sa používa na simulovanie slnka (smer zhora nadol).
Spot
- Zdroj svetla sa nachádza na jednom bode (ako svetlo Point) a svieti na iné miesto v priestore, pričom osvetľuje aj susedné body. Opäť ho môžeme prirovnať napr. k lampe. Ako ukazuje obrázok - spot nemá všade rovnakú intenzitu. Najsilnejšie je v strede a po okrajoch slabne. Intenzita stredného svetla je definovaná ako THETA (tmavo modrá šípka) a okrajového ako PHI (bledomodrá šípka). Obe hodnoty sú v radiánoch.
Na každý typ svetla je potrebný iný procesorový (systémový) čas. Najrýchlejší (najmenej náročný na výkon) je Ambient. Za ním nasleduje Directional, Point a Spot.
Parametre svetla
Farba - farba svetla v RGB alebo v 16-tkovej sústave
Dosah - Svetlo má určitý dosah, t.j. maximálna vzdialenosť od zdroja, ktorú osvetľuje. Je zbytočné nastavovať veľký dosah pokiaľ chcete osvetliť blízke objekty. Čím väčší dosah, tým viac výkonu spotrebuje.
Specular - Zaujímavá funkcia u svetla. Vytvára lesk na objektoch. Dobré pri vytváraní kryštálov a iný lesklých vecí.
Tiene - je dosť zložitá problematika. Na ich výpočet existujú zložité vzorce - nebude tu rozobraté. Jednoducho povedané: keď svetlo narazí na objekt, zastaví sa. Priestor za objektom ostane neosvetlený => vytvorí tieň.
Zoslabenie - určuje ako sa zmení intenzita svetla v závislosti na vzdialenosti od zdroja.
Pozícia - kde sa nachádza zdroj svetla (súradnica X,Y,Z)
Smer svetla - je udávaný vektorom [x,y,z]
Vektory
Na to, aby sme mohli pracovať so svetlami, musíme vedieť, čo je vektor a normálový vektor.
Všetky orientované úsečky rovnakého smeru majú rovnaký vektor.

Nech súradnice bodov A a B úsečky sú: A[5,3,8] B[3,0,9], potom vektor v určíme nasledovne:
v = B - A = [b1 - a1, b2 - a2 , b3 - a3] = [-2,-3,1]. Takto sa matematicky určujeme vektor. Smer svetla je udávaný vektorom [x,z,y]. V Direct3D to vyzerá takto: Bod A je zdroj svetla a bod B je objekt, ktorý chceme osvetliť:
svetlo.pozícia = [5,3,8]
svetlo.smer = vektor(-2,-3,1)
Tým sme nasmerovali svetlo na objekt. Jednoduché, že? U svetla Directional nemáme zdroj, iba smer. Ak cheme, aby svietilo zhora nadol použijeme vektor [0,-1,0], sprava doľava [-1,0,0] atď.
Je úplne jedno, či použijeme vektor [0,-1,0] alebo [0,-6,0], stále je to vektor rovnakého smeru, len má inú veľkosť. Z toho vyplýva, že napr. vektor [8,2,-4] je násobkom vektora [4,1,-2].
Normálový vektor
Je to kolmý vektor na iné dva vektory. Keď chceme použiť svetlo v projekte, musíme ku každému vertexu pridať jeho normálový vektor. Potom deklarácia vertexu môže vyzerať aj takto:
Type MojVertex X As Single Y As Single Z As Single color As Long nx As Single ny As Single nz As Single End Type
Nx,Ny,Nz sú komponenty normálového vektora
Ako určíme normálový vektor ?
Začnem trojuholníkom ABC: Z úsečiek AC a BC vytvoríme vektori u a v (modrá šípka). Nasleduje zistenie normálového vektoru (červená šípka), kolmého na u a v. Na to nám poslúži vektorové násobenie. Pretože trojuholník je útvar rovinný, má každý jeho bod rovnaký normálový vektor. Potom môžeme nami zistený vektor vrcholu C považovať aj za vektor bodov A a B, a tým celého trojuholníka (hnedá šípka)

Teraz konkrétny príklad:
Máme 2 vektory V1 a V2 predstavujúce os X a Y. Už na začiatku je jasné, že normálový vektor bude os Z. Teraz to matematicky dokážeme:
v = [v1 , v2 , v3 ]
u = [u1 , u2 , u3 ]
u x v = [v2 * u3 - u2 * v3 , v3 * u1 - u3 * v1 , v1 * u2 - u1 * v2]
Matematická značka pre vektorové násobenie je x. Rovnica o riadok vyššie vznikla uplatnením krížového pravidla (crossing).
Schéma krížového pravidla :

Z osí X a Y môžeme vytvoriť vektor ľubovolnej veľkosti. Napríklad to môžu byť tieto dva:
V1= [3,0,0]
V2 = [0,2,0]
Vn = V1 x V2 = [0 * 0 - 2 * 0 ,0 * 0 - 0 * 3 , 3 * 2 - 0 * 0] = [ 0 - 0 , 0 - 0 , 6 - 0] = [0 , 0 , 6]
Riešenie: Normálový vektor je [0,0,6]. Ako sme predpokladali Vn má smer ako os Z.
Aplikovanie do Direct3D
Zapamätajte si jednu veľmi dôležitú vec: D3D neosvetlí objekty, ktoré nemajú definovaný normálový vektor. Iba vďaka nemu vie D3D rozoznať, aký uhol je medzi osvetľovanou plochou a smerom svetla. Je to veľmi praktické, keď renderujete kocku. Normálové vektory stien kocky idú smerom od nej. Osvetlená bude iba vonkajšia časť. Vnútro kocky, ktoré aj tak nikto nevidí, bude bez svetla. Pozmenenú deklaráciu vertexu sme si už ukazovali na predchádzajúcich riadkoch. Ešte nesmieme zabudnúť na konštantu FVF:
Const FVF = (D3DFVF_XYZ Or D3DFVF_NORMAL) Dim Lights(0 To 2) As D3DLIGHT8 'pouzijeme 3 svetla D3DDevice.SetRenderState D3DRS_LIGHTING, 1
Svetlo treba deklarovať ako objekt D3DLIGHT8. Potom s ním už môžeme pracovať. Posledným riadkom kódu povolíme osvetlenie v D3D, inak by boli všetky svetlá ignorované. Teraz nastavíme potrebné parametre pre svetlo.
Dim Mtrl As D3DMATERIAL8, Col As D3DCOLORVALUE Col.a = 0.1: Col.r = 1: Col.g = 1: Col.b = 1 Mtrl.Ambient = Col Mtrl.diffuse = Col D3DDevice.SetMaterial Mtrl 'Directional (smerove) svetlo Lights(0).Type = D3DLIGHT_DIRECTIONAL Lights(0).diffuse.r = 1 Lights(0).diffuse.g = 0 Lights(0).diffuse.b = 1 Lights(0).Direction = Vytvor_Vektor(0, -1, 0) D3DDevice.SetLight 0, Lights(0) 'pod index 0 nastavime prve svetlo D3DDevice.LightEnable 0, True
Ako sa bude svetlo prejavovať na objekte záleží od nastavenia materiálu. Pre každý objekt v priestore môžete nastaviť iný materiál. Pre simuláciu lesklých predmetov je potrebné použiť lesklý materiál (specular). V projekte sú použité 3 svetlá s možnosťou prepínania medzi nimi. Tu si popíšeme iba prvé. Ostatné nájdete v zdrojovom kóde. Ako prvé je potrebné uviesť, aký typ svetla sa chystáme použiť, potom akú farbu bude mať svetlo. Samozrejme, že farba je ovplyvnená svetlom ambient (pokiaľ je nastavené) a materiálom. Nečudujte sa, keď objekt bude mať inú farbu ako svetlo. U typu directional je potrebné udať aj smer svetla pomocou vektoru. Keď je štruktúra Lights vytvorená, predáme ju ako parameter funkcii SetLight. V Direct3D sú svetlá rozoznávané podľa indexov. Nakoniec svetlo (už s indexom 0) zapneme nastavením LightEnable na True.
Ešte nám zostáva zistiť normálové vektory pre všetky vertexy. Pri jednoduchých objektoch sa to pri troche predstavivosti dá určiť aj z pamäti. Oveľa praktickejšie je vytvoriť si funkciu, ktorá by všetko prepočítavala za nás:
'vytvorenie 2 vektorov z 3 vertexov D3DXVec3Subtract v01, Vytvor_Vektor(p1.X, p1.Y, p1.Z), Vytvor_Vektor(p0.X, p0.Y, p0.Z) D3DXVec3Subtract v02, Vytvor_Vektor(p2.X, p2.Y, p2.Z), Vytvor_Vektor(p0.X, p0.Y, p0.Z) 'zistenie normaloveho vektoru D3DXVec3Cross vNorm, v02, v01 'Normalizovanie vektora D3DXVec3Normalize vNorm, vNorm ' Vysledna hodnota NajdiNormalovyVektor.X = vNorm.X NajdiNormalovyVektor.Y = vNorm.Y NajdiNormalovyVektor.Z = vNorm.Z
Vráťme sa späť k našemu trojuholníku. Celý postup zistenia normálového vektoru prepíšeme do kódu. D3DXVec3Subtract vytvorí z vertexov AC vektor, ktorý sa uloží do premennej v01. Proces zopakujeme aj pre vertexy BC. Tak máme 2 vektory potrebné na výpočet normálového vektora. Vidíte, že Direct3D ponúka opäť množstvo funkcií. D3DXVec3Normalize vám pomôže vypočítať normálový vektor, tzv. cross product. Posledná operácia je normalizovanie vektora. V D3D nesmú byť komponenty vektora (X,Y,Z) väčšie ako 1 resp. menšie ako -1. Preto výsledný vektor [0,0,6] z predchádzajúceho príkladu je nevyhovujúci a musíme ho “normalizovať” na [0,0,1]. Keď sme si napísali takúto funkciu, zistiť normálový vektor je oveľa jednoduchšie. Stačí ak jej pošlete súradnice vertexov trojuholníka a ona vám vráti normálový vektor.
Na záver: Ak si vspomeniete so svetlom ste sa stretli už v časti Textúrovanie štvorca. Textúry jedného štvorca boli normálne, druhého kolorizované. Akoby osvetlené svetlom rôznej farby. Na tomto princípe funguje celý svetelný engine v Direct3D.Vertexy sa kolorizujú svetlom. Preto svetelný engine je dosť odlišný od 3DStudia. Najlepšie to môžete vidieť na druhom príklade v zdrojových súboroch.
Dodatok
Nedajte sa pomýliť! Pozícia svetla sa je daná bodom v priestore. Napriek tomu sa zápis kódu tvári ako keby to bol vektor. Nie je to tak. Je to len skrátená forma zápisu. Keď sa pozriete na deklaráciu vektoru a súradíc vetrexu zistíte, že sú rovnaké. Preto môžeme použiť túto substitúciu.
svetlo.pozicia = VytvorVektor(0,5,6) - toto je skrátený zápis.
svetlo.pozicia.x = 0
svetlo.pozicia.y = 5 - a toto je úplný zápis
svetlo.pozicia.z = 6