Dnes budeme upravovat dßle nßÜ engine, p°idßme nap°φklad t°φdu kamery. Ukß₧eme si, co se vlastn∞ na kame°e dß programovat. Kameru pak budeme pot°ebovat v p°φÜtφch lekcφch, kdy se budeme zab²vat optimalizaΦnφmi metodami nad terΘnem.
Do dneÜnφ lekce jsme m∞li pouze statickou kameru tj. dφvali jsme se stßle na stejnΘ mφsto v prostoru, jen jsme pohybovali objekty. Dnes p°idßme do projektu Display novou t°φdu, kterß se bude starat o kameru scΘny (zatφm budeme podporovat pouze jednu kameru). S touto kamerou p∙jdou provßd∞t vÜechny mo₧nΘ kousky: pohyb (translace) do vÜech sm∞r∙, rotace nebo t°eba naklßp∞nφ. To nßm bude prozatφm staΦit. V naÜich p°φkladech jsme zatφm pohybovat kamerou nepot°ebovali, tak proΦ novou t°φdu? Cφl p°φÜtφch lekcφ bude vysv∞tlit princip optimalizace metodou quadtree, kterou si ukß₧eme na terΘnu. Tento terΘn bude podobn² znßmΘmu terΘnu nap°φklad ze hry Transport Tycoon (i kdy₧ tam je ve skuteΦnosti 2D:). O tomto terΘnu si povφme vφce v dalÜφ lekci. Nynφ zp∞t ke kame°e.
Ji₧ vφme, ₧e kamera je v Direct3D urΦena n∞kolika vektory. Za prvΘ je t°eba urΦit jakß osa bude v 3D sv∞t∞ vertikßlnφ. ╚asto se volφ osa Y, ale jß rad∞ji volφm osu Z (p°ijde mi to logiΦt∞jÜφ). Dßle se definujφ dva body, pomocφ nich₧ je urΦen sm∞rov² vektor. Jeho sm∞rem "pozorujeme" scΘnu. Tohle jsme u₧ jsme pou₧ili v naÜem p°φkladu, ale nastavovali jsme vÜe "natvrdo" a toto nastavenφ neÜlo zm∞nit za chodu aplikace. Jak²m zp∙sobem zinicializujeme a nastavφme tyto parametry si ukß₧eme za chvilku.
Dßle je t°eba peΦliv∞ nastavit perspektivu. I zde mßme n∞kolik parametr∙, navφc perspektivu m∙₧eme nastavit n∞kolika zp∙soby, z nich₧ ka₧d² pou₧ije jinΘ hodnoty. Definujeme tyto hodnoty:
T∞mito parametry vlastn∞ p°esn∞ definujeme jak²si Φty°st∞n. NejlΘpe je vÜe vid∞t na nßsledujφcφm obrßzku:
Vypadß to docela slo₧it∞, ale je to velice jednoduchΘ. VÜe co je za fialov²m obdΘlnφkem se u₧ nevykreslφ, tak₧e nap°φklad kdy₧ chceme vykreslit rozlehlou scΘnu, omezφme takto "nekoneΦn∞" velkΘ obzory a zv²Üφme FPS. Stejn∞ tak se nic nevykreslφ p°ed modr²m obdΘlnφkem. Nynφ budeme chtφt rotovat kolem bodu, na kter² se dφvßme. Budeme pohybovat pouze s tφmto bodem a potΘ dopoΦφtßme EyePoint. Zde budeme pot°ebovat dalÜφ obrßzek, kde je vid∞t v²poΦet sou°adnic obou bod∙:
Op∞t obrßzky vypadajφ pon∞kud slo₧it∞, tentokrßt °eÜenφ nemusφ b²t ihned vid∞t. V₧dy nejd°φve umφstφme LookAtPoint a potΘ z n∞ho dopoΦφtßme EyePoint (to platφ pro vÜechny sou°adnice). Vidφte, ₧e jsme pou₧ili t°i novΘ parametry scΘny: ·hel Z, co₧ je vlastn∞ ·hel sklopenφ kamery, dßle v²Üka kamery nad terΘnem a ·hel pohledu, co₧ je ·hel urΦujφcφ sm∞r pohledu. Pomocφ t∞chto parametr∙ m∙₧eme urΦit polohu obou bod∙.
U₧iteΦn²m atributem kamery by mohla b²t tzv. Billboard matice. Jist∞ jste si v n∞kterΘ h°e vÜimli, ₧e n∞kterΘ "3D" objekty vlastn∞ nejsou 3D, ale jsou to pouze 2D placky, na kter²ch je nanesenß textura. T∞chto jednoduch²ch objekt∙ m∙₧e b²t ve scΘn∞ °ßdov∞ mnohem vφc, ne₧ kdyby to byly skuteΦn∞ 3D objekty (meshe). Typick²m p°φkladem jsou stromy, kdy₧ jsou ud∞lßny Üikovn∞, laik ani nepoznß, ₧e je to billboard. Aby byl efekt co nejlepÜφ, je t°eba tyto placky natßΦet kolmo v∙Φi kame°e p°i ka₧dΘm pohybu kamery. A prßv∞ billboard matice je specißlnφ transformaΦnφ matice, kterou transformujeme vÜechny tyto objekty, aby se otoΦily kolmo ke kame°e. Tuto matici nenφ velk² problΘm vytvo°it, nebo¥ se jednß pouze o rotaΦnφ matici tak ve sm∞ru osy Z, aby se t∞leso ocitlo kolmo ke kame°e a my znßme sm∞rov² vektor kamery, tak₧e snadno spoΦφtßme ·hel, kter² naÜe placka svφrß prßv∞ s tφmto vektorem a p°ipoΦteme (p°φpadn∞ odeΦteme) 90 stup≥∙, Φφm₧ t∞leso natoΦφme proti kame°e. Podrobn∞ si postup ukß₧eme v p°φkladu za chvilku.
V DirectX SDK je p°φklad, kter² demonstruje billboarding - jmenuje se Billboard.
Poj∩me se nynφ v∞novat t°φd∞ XCamera. Tento objekt bude internφ v knihovn∞ Display a bude jedineΦn² (povolφme tedy pouze jednu kameru v systΘmu). Ovlßdat kameru bude samoz°ejm∞ mo₧no i zvenku (jinak by to celΘ nem∞lo smysl). V₧dy kdy₧ budeme chtφt pohnout s kamerou, nastavφme sm∞r a rychlost pohybu. T°φda je relativn∞ jednoduchß. Budeme mφt metodu pro inicializaci, dalÜφ metodu, kterß spoΦφtß aktußlnφ pozice vÜech bod∙ a nastavφ matici pohledu a projekΦnφ matici. Tuto metodu budeme volat ka₧d² cyklus aplikace.
JeÜt∞ jedna poznßmka, tato kamera je urΦena pro pohyb nad komplexnφm terΘnem tj. kamera se m∙₧e pohybovat ve sm∞ru os X a Y. Umo₧≥uje p°iblφ₧enφ (Zoom) (tj. zkrßcenφ sm∞rovΘho vektoru), pohyb kamery nahoru a dol∙, naklßp∞nφ tj. zm∞na ·hlu Z a samoz°ejm∞ rotace tj. zm∞na ·hlu pohledu. Dßle bychom mohli p°idat funkci volnΘho letu (ovlßdanou nap°φklad pomocφ myÜky).
Nejd°φve nadefinujeme pßr konstant, urΦujφcφ parametry kamery (tyto parametry m∙₧ete podle pot°eby ud∞lat jako prom∞nnΘ).
#define _C_MAX_ROTATION_SPEED 5.0f #define MAX_ZOOM 40 #define Z_ANGLE D3DX_PI / 2 #define MAX_Z_ANGLE D3DX_PI / 2 const float NEAR_CLIP = 0.1f; |
Dßle uve∩me deklaraci t°φdy XCamera:
class DISPLAY_API XCamera
public:
} |
Zde najdeme vÜechny v²Üe vysv∞tlenΘ atributy. Nejprve je zde odkaz na objekt za°φzenφ Direct3D, dßle omezenφ kamery v prostoru, rychlosti pohybu ve vÜech mo₧n²ch sm∞rech, pozice kamery, sm∞r natoΦenφ atd. a nakonec matice projekce, pohledu a billboard matice. JeÜt∞ se zmφnφm o inline funkcφch pohybu. Tyto funkce pouze nastavujφ rychlost v po₧adovanΘm sm∞ru pohybu. Tato rychlost je zachycena v metod∞ ProcessCamera(), kde je zßrove≥ vynulovßna, aby se anuloval efekt v dalÜφm cyklu.
P°i inicializaci objektu XCamera je nejprve nutnΘ volat metodu InitCamera(), kde nastavφme za°φzenφ a omezenφ kamery. Ve funkci UpdateFrame() je t°eba volat metodu ProcessCamera(), pomocφ nφ₧ aplikujeme pohyb kamery, spoΦφtßme novou matici pohledu a nastavφme ji. Na zßv∞r takΘ spoΦφtßme billboard matici. Nynφ si podrobn∞ rozebereme vÜechny metody t°φdy XCamera.
ZaΦneme konstruktorem a destruktorem:
XCamera::XCamera()
} XCamera::~XCamera()
} |
Ukazatel na objekt za°φzenφ je t°eba vynulovat, abychom poznali, ₧e je objekt ziniciliazovßn Φi nikoliv. Dßle nastavφme implicitnφ hodnoty vÜech prom∞nn²ch na n∞jakΘ rozumnΘ hodnoty. Kamera se bude "dφvat" z poΦßtku systΘmu kamsi do prostoru pod ·hlem 45 stup≥∙.
Dßle se podφvejme na zajφmav∞jÜφ metodu InitCamera():
HRESULT XCamera::InitCamera(IDirect3DDevice8* lpDevice, int iMinBound, int MaxBound)
} |
Zde si zapamatujeme vstupnφ parametry. Druh²m ·kolem je spoΦφtat pozici kamery, kdy₧ znßme bod, na kter² se kamera "dφvß". Tento bod jsme nastavili v konstruktoru t°φdy. Zde pou₧ijeme trochu chytrΘ matematiky a obrßzk∙ z ·vodu lekce. Nastavenφ sv∞tovΘ matice je zde jen symbolickΘ, nebo¥ se nastavuje pro ka₧d² objekt jinß matice. OvÜem dalÜφ °ßdky nemusφ b²t zcela jasnΘ. Vychßzφme z bodu, na kter² se dφvßme. Zam∞°φme se zatφm na X a Y sou°adnice. D∙le₧itΘ hodnoty jsou p°φmß vzdßlenost od LookAtPointu, co₧ je hodnota m_fZoomDistance. Dßle pot°ebujeme ·hel, pod kter²m pozorujeme scΘnu, to je m_fViewAngle. Pomocφ goniometrick²ch funkcφ spoΦφtßme protilehlou resp. p°ilehlou stranu troj·helnφka z obrßzku pro zφskßnφ X resp. Y sou°adnice kamery. Nakonec bod posuneme na sprßvnΘ mφsto v prostoru. Nynφ se soust°e∩me na Z-tovou sou°adnici. Zde je situace o trochu jednoduÜÜφ, jen vyu₧ijeme ·hel Z definovan² jako konstanta (mimochodem, tato konstanta se nastavφ jen p°i inicializaci tj. sklon m∙₧eme takΘ nastavovat).
Nakonec tu mßme metodu, kterß spoΦφtß vÜechny parametry kamery v pr∙b∞hu programu:
HRESULT XCamera::ProcessCamera(float fElapsedTime)
} |
Zde je situace hodn∞ podobnß. Zde ovÜem navφc musφme p°epoΦφtat i LookAtPoint. Zde tedy modifikujeme vÜechny parametry v zßvislosti na rychlosti v danΘm sm∞ru.Dßle jsou tu omezenφ pohybu (nap°φklad sklßp∞nφ kamery je mo₧nΘ jen v urΦitΘm intervalu atd.). Zde pou₧ijeme podobn² princip jako v p°edchozφ metod∞. Ke ka₧dΘ slo₧ce vektoru LookAtPoint p°ipoΦteme p°φr∙stky danΘ rychlostmi ve sm∞ru X a Y. V dalÜφm kroku omezφme kameru na jistΘm prostoru. Dßle spoΦφtßme EyePoint ·pln∞ stejn∞ jako v metod∞ InitCamera(). Vytvo°φme a nastavφme novou matici pohledu, vynulujeme rychlosti ve vÜech sm∞rech a nakonec spoΦφtßme billboard matici. U tΘto Φinnosti se na chvilku zastavφme. Prom∞nnß vDir je vektor sm∞°ujφcφ ve sm∞ru pohledu kamery. Z funkce atan() (arc tg) vypadne ·hel, o kter² je t°eba pootoΦit p°φpadn² billboard tak, aby byl ve sm∞ru vektoru vDir. My ale chceme, aby objekt byl kolmo na kameru, tudφ₧ je t°eba ho otoΦit o dalÜφch 90 stup≥∙. UrΦit∞ jste si vÜimli, ₧e p°i zm∞n∞ pozice kamery nßsobφme vÜe jeÜt∞ jakousi konstantou (i o tom jist∞ byla °eΦ). Nenφ to samoz°ejm∞ nic jinΘho ne₧ uplynul² Φas od p°edchozφho snφmku a d∞lßme to z toho d∙vodu, aby kamera byla na vÜech systΘmech stejn∞ rychlß-pomalß tj. aby pohyb-rychlost kamery nebyl zßvisl² na FPS.
Na zßv∞r lekce trochu upravφme nßÜ p°φklad (projekt Tester), abychom vyzkouÜeli novinky v naÜem enginu.
Za prvΘ je t°eba p°idat objekt kamery do t°φdy XDisplay, dßle p°idßme inline metodu GetCamera(), kterß nßm bude vracet ukazatel na uveden² objekt. Od te∩ m∙₧eme s kamerou pracovat.
Nejprve tedy zavolßme metodu InitCamera() a p°edßme n∞jakΘ rozumnΘ parametry:
// inicializace Direct3D g_theDisplay.Init(g_hWnd); g_theInput.Init(hInstance, g_hWnd, g_theDisplay.GetResolution(), g_theDisplay.GetDevice()); g_theDisplay.GetCamera()->InitCamera(g_theDisplay.GetDevice(), -20, 20); |
NßÜ vesmφr je mal², jen 40x40 (Φeho? t°eba metr∙:) Dßle upravφme funkci UpdateFrame():
DWORD dwOldAdrMode; float fFactor = cmnGetTime(TIMER_GETELAPSEDTIME1); g_theInput.ProcessInput(); |
V²poΦet faktoru (Φasu) p°esuneme na zaΦßtek funkce, abychom ho mohli vyu₧φt v metod∞ ProcessCamera().
Nakonec p°idßme ovlßdanφ kamery:
if(g_theInput.IsKeyDown(DIK_ADD , FALSE)) {
}
}
}
}
}
} int iMouseZone = int(g_theDisplay.GetResolution()->x * 5 / 1024);
} else {
}
} else {
}
} else {
}
} else {
} |
Zde vyu₧ijeme metody z minulΘ lekce, kde jsme vklßdali projekt Input. V₧dy otestujeme po₧adovanou klßvesu (bez obsluhy tzn. projevφ se reakce na vφcenßsobnΘ dr₧enφ klßvesy). PotΘ volßme p°φsluÜnou metodu, kterß pohne s kamerou. U pohybu ve sm∞ru X a Y je zapojena i myÜka. Sledujeme, kdy₧ je myÜ na okraji obrazovky a potΘ spustφme pohyb v po₧adovanΘm sm∞ru.
Po kompilaci je mo₧no ovlßdat kameru Üipkami ve sm∞rech osy X a Y. Zoom (p°iblφ₧enφ, oddßlenφ) provedeme klßvesou + a -. Sklopenφ kamery je na klßvesßch Num 8 a Num 2. Rotaci provedeme pomocφ Num 1 doleva a Num 3 doprava.
Jak bylo zmφn∞no v ·vodu, v p°φÜtφ lekci se budeme v∞novat optimalizacφm p°i vykreslovßnφ terΘnu, kter² se bude sklßdat ze sφt∞ polygon∙, ale my chceme vykreslit pouze ty polygony, kterΘ jsou viditelnΘ. Na to je n∞kolik metod, my zaΦneme s metodou quadtree.