![]() |
C/C++ & Visual C++Kurz DirectX (31.) |
Úvodem | Datové struktury | Kurz DirectX | Downloads | Otázky a odpovědi |
|
V minulé lekci jsem slíbil, že se dnes podíváme na vylepšení vykreslovací fronty, protože způsob uvedený minule, byl značně neefektivní. Největší žrout času je totiž funkce SetVertexStream() a tedy časté přepínání vertex bufferů vede ke zbytečným výkonovým ztrátám. Naším úkolem v dnešní lekci je tedy vytvořit postup, jak minimalizovat počet volání metody SetVertexStream(). 31.1. Dynamický index bufferMinule jsme použili jeden index buffer, který byl společný pro všechny listy. Mohli jsme si to dovolit, protože listy se vykreslovaly jeden po druhém. Každý měl svůj vlastní vertex buffer, který se musel před vykreslením KAŽDÉHO listu nastavit. Zde tedy vznikalo nadměrné přepínaní vertex bufferů. Zato index buffer se nastavil jen jednou pomocí metody SetIndices(). Dnes si ukážeme jiný způsob, který je elegantnější ačkoliv složitější na implementaci. Budeme postupovat opačně, čili vytvoříme jeden velký vertex buffer pro celý terén a několik menších index bufferů, jejichž obsah se bude každý snímek měnit. Takový index buffer nazýváme dynamický a vytváří se s příznakem D3DUSAGE_DYNAMIC. Tento příznak zajistí polohu index bufferu v AGP paměti (statické buffery se vytvářejí ve video paměti). Dynamický index buffer musíme zamykat s příznaky D3DLOCK_DISCARD nebo D3DLOCK_NOOVERWRITE. První z nich zajistí, že cely bufferu bude zahozen a funkce Lock() vrátí ukazatel na novou oblast pamětí, což zamezí čekání na vykreslení dat z předchozího snímku. Druhý zmíněný příznak naopak zajistí, že se nepřepíší žádné indexy, které byly vložený předchozím voláním metody Lock(). Tímto způsobem se dají přidávat indexy do bufferu. Stejným způsobem můžeme pracovat i s vertex bufferem. 31.2. Rozdělení terénuNáš terén rozdělíme na lokace, které budou mít předem definovanou velikost (například 128x128 políček). Každá taková lokace bude mít vlastní index buffer. Lokaci definujeme strukturou Location: struct Location
}; Kromě zmíněného IB struktura obsahuje několik dalších parametrů využívaných převážně při vykreslování. Za prvé je to počet viditelných listů. Z toho parametru později zjistíme počet trojúhelníků, které vykreslit. Dále jsou tu atributy MinIndex a MaxIndex, pomocí kterých určíme rozsah použitých vertexů při volání DrawIndexedPrimitive(). Dále je tu pomocný ukazatel na pole indexů, který použijeme při zamykání IB. Nakonec okrajové body lokace, abychom mohli přiřadit listům jejich lokaci. Upravme tedy strukturu uzlu QTNode: struct QTNode {
}; Zde tedy přidáme ukazatel na lokaci, kde se daný list nachází (proměnnou inicializujeme pouze pro listy). Dále již nebudeme potřebovat vertex buffer, ale místo toho uložíme dynamické pole obsahující indexy vertexů listu. Ještě ukládáme pomocnou informaci, počet indexů (abychom toho číslo nemuseli počítat později a ušetřili tak čas). Hlavní rozdíl bude v tom, že celý terén bude po lokacích uložen v jediném Vertex bufferu. Z toho plyne, že VB bude obsahovat okrajové vrcholy lokací dvakrát. Tím zajistíme jednodušší implementaci, ale zaplatíme vyšší paměťovou složitostí. U každé lokace budeme během rozpoznávání ukládat viditelné listy a protože víme, v jaké lokaci list je, zkopírujeme dané indexy do IB lokace. Při vykreslovaní pak budeme opět postupovat po jednotlivých lokacích. Využijeme všechny parametry, které jsme si ukládali během rozpoznávání viditelných listů. Podrobnosti si uvedeme u konkrétního algoritmu. Upravíme také samotnou třídu CTerrain. Již nebude potřeba jeden IB, ale místo něj vložíme globální Vertex buffer. Dále bude potřeba dynamické pole lokací a počet lokací: Location *m_arLoc; Kromě těchto atributů ještě vymažeme funkci FillLeaves(), protože listy se budou plnit zcela jinak. Nyní se již můžeme podívat na změny v metodách třídy CTerrain. Začneme metodou InternalInit(), kde jsme minule inicializovali také globální index buffer. Dnes ovšem žádný takový buffer nemáme (nový vertex buffer můžeme inicializovat až později, protože ještě nevíme rozměry terénu). Nic nového v této metodě nebude.
Nejdříve tedy určíme počet lokací. Víme jak je velký terén a víme jak je velká lokace (to je určeno konstantou). Poté alokujeme příslušené místo a lokace jednu podruhé nastavíme. Zde vytváříme nový objekt IIndexBuffer. Do index bufferu se musí vejít indexy listů celé lokace, která má v našem případě 128x128 políček. Určíme tedy počet listů v jedné lokaci (víme velikost lokace a velikost listu). Každé políčko má dva trojúhelníky, na každý jsou třeba 3 indexy.
Z toho vypočítáme velikost bufferu. Všimněte si, že index buffer vytváříme s příznakem D3DUSAGE_DYNAMIC. Metody GenerateTerrain() a GenerateTerrainFromFile() zůstanou nezměněny. Za prvé spočítáme posun v index bufferu dané lokace (zde již mají listy odkaz na svou lokaci - viz. dále). Víme kolik listů je již v tomto IB a také víme kolik indexů jeden list zabere. Překopírujeme indexy funkcí memcpy(). IB již máme otevřený (viz. metoda Render()) a ukazatel je uložen v atributu pIndices. Dále určíme maximální a minimální použitý index. Minimální index má vrchol nejblíže k počátku, maximální je naopak ten nejdál v rámci jednoho listu. Tyto hodnoty platí pro celou lokaci, tudíž je musíme testovat pro každý list, který vkládáme do IB. Nakonec musíme zvýšit hodnotu viditelných listů (aby se další nalezený list vložil do IB nakonec). Záznam, že list je vidět je důležitý jen kvůli mapě, kterou lze zobrazit klávesou M. Metoda Render() zaznamenala taky spoustu změn. V první části připravíme Index buffery. Tato část lze zcela přeskočit (pak je hezky vidět vykreslovaná oblast). Dále provedeme samotné vykreslení: Opět zde uvádím jen částečný výpis, zbytek metody je stejný. Jak jsem tedy zmínil, nejdříve uzamkneme IB všech lokací a vynulujeme parametry. Všimněte si jak ukládáme ukazatele na otevřené IB. Tyto ukazatele vzápětí použijeme v metodě CullTerrain(). Nakonec je třeba IB opět odemknout. Před vykreslením nastavíme
standardní
věci jako te textura, vertex shader a náš nový VB. Metoda SetStreamSource() se tedy volá jen jednou za cyklus. Nyní se podrobně podíváme na způsob vykreslování.
Metoda má za úkol vytvořit a naplnit pole indexů každého listu a přiřadit list jedné lokaci.
Abychom zjistili, ve které lokaci se daný list nachází, použijeme okrajové body lokací a listu. Když najdeme tu správnou lokaci, hledání ukončíme. Poznámka: Jistě jste si všimli, že se v kódu vyskytují řádky, které zajistí výpis obsahuj Vertex buffer a index buffer do souboru. Tato volba je volitelná a může se hodit při ladění programu. Výpis těchto dat do výstupního okna vývojového prostředí není vhodný, protože je příliš pomalý. Kompletní zdrojový kód aplikace si můžete stáhnout v sekci Downloads. 31.3. ZávěrA co nás čeká v příští lekci? Doděláme slíbenou oblohu a pohrajeme se s funkcemi mlhy. Direct3D má docela slušnou podporu mlhy, takže to nebude nic obtížného. Těším se příště nashledanou.
|
|