![]() |
C/C++ & Visual C++Kurz DirectX (34.) |
Úvodem | Datové struktury | Kurz DirectX | Downloads | Otázky a odpovědi | 2001 | 2002 | 2003 | 2004 |
|
V této lekci dále upravíme pohyb kamery, který jsme načali v minulé lekci. Doplníme formát souboru cesty a použijeme jiný typ interpolace mezi segmenty. Dále si něco povíme o technice Level Of Detail (LOD). V této lekci si povíme, jak LOD obecně funguje a příště zkusíme implementovat jednoduchý LOD do našeho enginu. 34.1. Pohyb kamery - pokračováníPředstavte si, že chceme rotovat kolem nějakého objektu. Zde bychom měli velký problém při definici směrových vektorů. V tomto případě by se náramně hodilo, abychom směr kamery definovali pomocí druhého bodu. Definujeme tedy look-at point a eye point. V některých případech se ovšem hodí obě varianty, proto zavedeme speciální značení řádků, které používají první nebo druhou možnost: Pokud na začátek řádku napíšeme znak dolar ($), poslední parametr se interpretuje jako směrový vektor. Pokud místo dolaru použijeme křížek (#), parametr se interpretuje jako bod s tím, že pokud použijete starý formát, použije se první varianta. Změny kódu jsou minimální, v metodě LoadPath() přibude testování prvního znaku na řádku: // Second vector is look at point V tomto případě se tento znak přeskočí a zároveň nastavíme příslušný flag, na který reagujeme po načtení ostatních hodnot na řádku: // vDir is look at point Touto operací spočítáme směrový vektor, který bychom jinak museli vkládat přímo do souboru cesty. V případě křížku se tento znak pouze přeskočí. Na závěr této metody ještě provedeme zacyklení spojového seznamu. To se nám bude hodit v zápětí, až budeme implementovat novou interpolační metodu: // Cycle path - last next is first and first previous is last Následníka posledního segmentu nastavíme na první a předcházející prvního nastavíme jako poslední. Dejte si ovšem pozor, až budete spojový seznam odstraňovat z paměti, kde se sleduje právě hodnota NULL posledního prvku - nyní žádná NULL hodnota nikde není. Catmull-rom interpolace Jedná se o speciální interpolaci, která pro výpočet využívá hned 4 bodů na křivce (u lineární stačily pouze dva). Křivka mezi těmito body připomíná Bezierovu křivku, rozdíl je v tom, že v případě Catmull-rom křivka prochází přes řídící body. Nebudu se tu věnovat konkrétním vzorcům, jak spočítat interpolovaný vektor. Na internetu se dá najít mnoho podrobných článků k této problematice. Direct3D podporuje tuto interpolaci v podobě funkce D3DXVec3CatmullRom(), která vlastně udělá všechno za vás. Má šest parametrů: ukazatel na výstupní vektor, čtyři ukazatele na vektory, z kterých se výsledný vektor počítá a faktor interpolace - podobně jako u lineární interpolace. Pro určení výstupního vektoru je kromě bodů ohraničující aktuální segment potřeba další dva body - jsou to sousedi hraničních bodů. Z toho plyne, že nejde určit správně vektor v prvním a posledním segmentu pokud není křivka cyklická. Proto jsme tedy náš spojový seznam zacyklili. Na následujícím obrázku je vidět, co jsem teď popsal:
Body P0-P3 jsou potřeba pro výpočet interpolace v aktuálním segmentu. Parametr s je náš interpolační faktor, který počítáme v metodě ProcessCamera(). Důležitým momentem je tedy výběr správných bodů, z nichž se počítá interpolace: ps1 = ps2 = ps3 = ps4 = m_curSeg; Testovat hodnoty bychom ani nemuseli, protože v celém spojovém seznamu by se neměla vyskytovat hodnota NULL. Poté již můžeme zavolat funkci D3DXVec3CatmullRom() pro oba vektory: D3DXVec3CatmullRom(&m_curInterpolation.vPos, &ps1->vPos, &ps2->vPos, &ps3->vPos, &ps4->vPos, s); Zachoval jsem i lineární interpolaci pro srovnání. Velká výhoda této nové interpolace totiž spočívá v tom, že pohyb je mnohem plynulejší a nedochází k ostrým změnám směru. 34.2. Level of Detail (LOD)Tato technika využívá toho, že vzdálené části terénu nemusí být vykresleny ve vysokých detailech jako části nejblíže k pozorovateli. My teď vykreslujeme vše stejně detailně a u vzdálených listů ani jednotlivé detaily nerozeznáme. Tento problém řeší právě LOD, kde kromě jiného dosáhneme výrazného navýšení výkonu. Čím je list dále od kamery, tím víc vynecháme trojúhelníků a malé trojúhelníky spojujeme do větších a větších. Nejvzdálenější listy nahradíme pouze dvěma trojúhelníky! Jak už to bývá, i zde se najdou problémy, které se obtížně řeší. Představte se dva navazující listy, které jsou velmi členité v místě přechodu (pokud mluvím o členitosti, mám na mysli velké rozdíly nadmořské výšky na malém prostoru). Algoritmus nahradí druhý list méně detailním listem a druhý list ponechá. Nyní nastane nežádoucí efekt, kdy mezi prvním a druhým listem vznikne mezera, buď díra "za" terén nebo převis. Toto chování může být značně nepříjemné a terén ve výsledku nevypadá moc pěkně. Řešení je mnoho. My si zde ukážeme jedno z těch jednodušších, které není dokonalé. Pro každý list spočítáme při inicializaci faktor převýšení listu. Pokud je tato hodnota příliš vysoká, znamená to, že tento list je velmi členitý a tudíž ho budeme nahrazovat se zpožděním - čili až bude opravdu daleko. Takže nahrazování nebude závislé pouze na vzdálenosti od kamery, ale také na reliéfu listu. Dalším vylepšením by třeba mohl být způsob, kdy navíc porovnáme faktory okolních listů apod. Možností je hodně. Problém s dírami jsme vyřešili, ale zároveň jsme trochu zhoršili účinnost celé techniky. Ale i přesto je redukce kreslených trojúhelníků výrazná a nárůst FPS rovněž. Jak budeme techniku implementovat v našem terénu? Každý list obsahuje seznam svých indexů VŠECH trojúhelníků. Zde máme na výběr. Buď využijeme tento seznam pro všechny úrovně LODu a budeme vybírat ty správné trojúhelníky, což se projeví mírnou časovou ztrátou při ořezávání listů nebo vytvoříme další seznamy nových indexů, které obsahují vždy indexy pro jednu úroveň. Druhá možnost zase zabere více paměťového místa, ale bude trochu rychlejší. Zkusit si můžete obě varianty, já zde ukáži pouze druhou zmíněnou. Upravíme tedy strukturu QTNode tak, že přidáme nové pole indexů celkem pro 5 úrovní (máme listy 16x16, to bude 1. úroveň, dále budeme vytvářet listy 8x8, 4x4, 2x2 a 1x1 pro 5. úroveň). Přidejme tedy tyto atributy: // pIndicesX je pole indexů X-té úrovně, wIndXCount je počet indexů X-té úrovně a wTilesXCount je počet políček na X-té úrovni (to bude 16x16, 8x8, 4x4, 2x2 nebo 1x1). Atribut fLODFac je již zmíněný faktor zvlnění listu. Počty indexů a políček bychom samozřejmě mohli počítat až v době ořezávání, ale jsou to konstanty, tak proč je nemít připravené (všimněte si, že opět na úkor paměti, ale tak to je téměř vždy). Nejdůležitější nyní bude inicializace všech seznamů. Nejprve alokujeme paměť pro tyto seznamy: // allocate space for indices Každá další úroveň má 1/4 indexů co předchozí, protože obsahuje 4x méně políček. Nyní následuje smyčka přes celý list, ve které najednou naplníme všechny seznamy: for(y = pNode->arBounds[0].y; y < pNode->arBounds[3].y; y++)
pNode->pIndices1[i + 0] = bx + by * LOC_V_SIZE; if(x % 2 == 0 && y % 2 == 0) pNode->pIndices1[i + 0] = (bx+1) + by * LOC_V_SIZE; if(x % 2 == 0 && y % 2 == 0) V každém opakování inicializujeme hned 6 indexů pro celé políčko (dva trojúhelníky). Pro přehlednost jsem barevně oddělil jednotlivé úrovně. Základní 1. úroveň je zelená a nikterak se neliší od předešlých dílů. Jen si všimněte, že každý cyklus se do seznamu zapíše 6 indexů. Zajímavější bude červená neboli 2. úroveň. Zde se zapisuje jen každý vertex, čili skáče se ob jeden vertex a tím se vytvoří síť s poloviční hustotou. Takto pokračujeme přes modrou, fialovou a nakonec žlutou úroveň kdy z listu uděláme jedno velké pole (zapíše vlastně jen 6 indexů pro dva trojúhelníky). Také si všimněte, že každá úroveň má své počítadlo iX pro index na X-té úrovni. V první části cyklu vybíráme maximální a minimální výšku vertexu. Tyto hodnoty v zápětí použijeme na výpočet faktoru: pNode->fLODFac = MaxZ - MinZ; Naposledy upravíme metodu CullTerrain(), kde vybereme správnou úroveň a zkopírujeme příslušné indexy. V případě, že je LOD aktivní, spočítáme vzdálenost od kamery a list "přiblížíme" o faktor. Složitější listy se tak budou zdát blíže a nenahradí se, i když ve skutečnosti jsou daleko: // Vzdalenost listu od kamery - pocita se pouze pro LOD urovne > 1 Dále budeme vybírat úrovně podle vzdálenosti: if(d < 150.0f) ... } Kód zde neuvádím celý, protože podmínky se stále opakují až na úroveň 5. Vždy ovšem musíme kopírovat indexy ze správné úrovně a nakonec zvýšit počet políček v rámci lokace - každá úroveň má tento počet jiný! A to je vše! Nyní se bude místo 400 000 polygonů vykreslovat pouze okolo 40 000 polygonů. Celý příklad si samozřejmě můžete stáhnout v sekci Downloads. 34.3. ZávěrNa závěr uvedu obrázek ze současné verze našeho 3D enginu s technikou LOD:
Je vidět, že vzdálené kopce už jsou málo detailní a jeví se jen jako mraky v dálce... V programu můžete měnit interpolační metodu klávesou I. Pokud zapnete lineární interpolaci, je vidět, že pohyb je více sekaný než v případě Catmull-rom interpolace. V příští lekci se vrátím k normálovým vektorům a ukážeme si jednoduchý způsob, jak je spočítat přesně! Krom toho si tyto vektory zobrazíme nad terénem. Těším se příště nashledanou.
|
|