![]() |
C/C++ & Visual C++Kurz DirectX (32.) |
||||||||||||||||||||||||||||||||||||
Ve 32. lekci programování v Direct3D postoupíme dál a zbavíme se ošklivých černých okrajů kolem terénu. V další části článku se podíváme jak v Direct3D pracuje mlha. Nakonec ještě upravíme třídu XMesh, aby se automaticky počítaly normálové vektory. 32.1. Obloha a zakončení terénuObjekt, který ohraničuje náš svět nazýváme skybox, což je doslova krychle, který má na vnitřní straně texturu okolí (v našem případě to bude nebe, ale může to byt třeba horizont apod.). Celý náš svět je uvnitř této velké krychle, takže se zdá, že svět nemá žádné viditelné hranice. Protože jsem nevymyslel lepší české slovo, budu používat anglický výraz skybox. Náš skybox vlastně nebude ani box, protože je to při omezení kamery zcela zbytečné. Stačí nám jen vlastně okraje kolem terénu (čili svislé stěny krychle, které vlastně nebudou svislé, viz. dále). Okraje rozšíříme, abychom zabránili vypadnutí kamery z prostoru (kamera může ve skutečnosti zcela opustit plochu terénu!) - čili uděláme takový "trychtýř". Nicméně budeme potřebovat 8 vrcholů, stejně jako kdybychom použili krychli. Za to ale nebudeme definovat seznam trojúhelníků a vystačíme si s pásem (triangle strip), který se renderuje rychleji, ačkoli zde se to samozřejmě neprojeví. Skybox bude reprezentovat objekt ID3Object: I3DObject *m_pSkyBox; Tomuto objektu musíme vytvořit vertex i index buffer: // horni rozsireni oblohy dif = (i & 0x2) ? 200.0f : -200.0f; m_pSkyBox->GetIB()->GetBuffer()->Lock(0,0, (BYTE**) &pIndices, 0); Kód vypadá složitě a nepřehledně a je lepší, když si objekt nakreslíte na papír. Očíslujte si hrany podle tabulky:
Šedivé řádky označují "horní" vrcholy, bílé dolní. Všimněte si, že tyto řádky mají v kódu na prvním bitu jedničku. Tímto způsobem přiřazujeme vlastnosti vrcholům. Nejprve se tedy vytvoří objekt I3DObject, v dalším kroku se naplní vertex a index buffer. Indexů je třeba jen 10, protože chceme použít triangle strip (na první trojúhelník potřebujeme 3 vrcholy, na další jen jeden, 3 + 7 * 1 = 10). Inicializaci VB provádíme ve zdvojené smyčce for, a v každém cyklu vytvoříme dva vrcholy, které jsou pod sebou (čili mají stejné x a y souřadnice, ale z se liší) - je to vždy dvojice řádků v tabulce, bílý a šedý. Dolní vrchol je jednoduchý, známe velikost terénu a tento vrchol "pasuje" na roh terénu. Horší je to s texturovými souřadnicemi (v našem příkladu by nebyly potřeba, protože texturu nepoužívám). Všimněte si, že u-souřadnice je kopií prvního bitu kódu vrcholu. Tento řádek kopii provede: pVertices[i].tu1 = (i & 0x1) ? 1.0f : 0.0f; U souřadnice v je to složitější. Když se ovšem podíváme do tabulky, opět je vidět, že souřadnice v je 1, pokud se 2. a 3. bit kódu liší. Pokud jsou tyto bity stejné (0 nebo 1), je souřadnice nulová. Následující řádek provede popsanou operaci: pVertices[i].tv1 = ((i & 0x6) == 0x6 || (~i & 0x6) == 0x6) ? 0.0f : 1.0f; Tilda před proměnnou i znamená negaci. Proměnná dif, určuje rozšíření objektu. Problém je, že u každého vrcholu má tato proměnná jiné znaménko. Zde se problém rozdělí na x a y souřadnice (pro každou souřadnici děláme samostatný test). Pokud je x-souřadnice 0, je třeba parametr dif odečíst, v opačném případě přičíst. A x-souřadnice je jedničková, právě když je jedničkový 2. bit kódu: dif = (i & 0x2) ? 200.0f : -200.0f; Pro souřadnici y platí totéž, jen se díváme na 3. bit: dif = (i & 0x4) ? 200.0f : -200.0f; Texturové souřadnice druhého vrcholu počítáme úplně stejně. Index buffer je tak malý, že jsem ho naplnil výčtem hodnot z pole. Když si objekt nakreslíte, bude vám jasné, proč jsou vrcholy spojeny zrovna takto. Je totiž důležité, aby normálové vektory ploch směřovaly dovnitř - kamera je uvnitř objektu. Ještě si krátce povíme něco o zakončení terénu. Zakončení znamená, že mezi hranou skyboxu a hranou terénu nebude díra. Tento problém se dá řešit tak, že přidáte další trojúhelníky mezi zmíněné hrany, což je ale poměrně náročné (kvůli optimalizaci byste museli tyto trojúhelníky přiřadit okrajovým listům stromu). Já jsem to vyřešil úplně jednoduše, stačí všem krajním vertexům přiřadit z-souřadnici 0.0: if(x == 0 || x == m_iTerrainVerticesX-1) Tento kód provedeme již při plnění pole m_arTerrain v metodě GenerateTerrainFromFile(). Stěna na hraně terénu sice nebude zcela svislá, ale to zas tolik nevadí (je to tak zanedbatelné, že to ani není vidět). 32.2. Mlha v Direct3DV Direct3D existují dva typy mlhy - pixelová a vertexová. Hlavní rozdíl mezi nimi je, že vertexová mlha je počítána během fáze transformací a osvětlování pro každý vertex, naproti tomu pixelovou mlhu počítá ovladač zařízení pro každý pixel. V dokumentaci MSDN máte podrobně popsáno, jakým způsobem se mlha počítá. Pro naše účely bude stačit, když vám ukáži, jak se prakticky mlha používá. V našem příkladu jsem použil pixelovou mlhu. Co musíme udělat proto, abychom mlhu spustili? Je to velice snadné. 1) Nastavíme pomocí SetRenderState() parametr
D3DRS_FOGENABLE na hodnotu TRUE. Tím mlhu zapneme. D3DFOG_EXP - exponenciální mlha prvního typu. "Hustota" mlhy se zvyšuje nepřímo úměrně vzdálenosti od kamery. U exponenciální mlhy definujeme pouze jeden reálný parametr a tím je hustota mlhy. Vše si samozřejmě ukážeme na jednoduchém příkladu: float fDensity = 0.005f; Tímto nastavíme exponenciální lehkou mlhu žluté barvy, nakonec mlhu zapneme. Pokud bychom použili lineární mlhu, je třeba definovat dva parametry: float fFogStart = 1.0f; V příkladu z lekce používáme první typ, ale dobré je vytvořit metodu třídy XDisplay pro nastavení všech typů mlhy, včetně vertexové. O tom si ovšem povíme příště. 32.3. Modifikace třídy XMeshKdyž exportujete model například z 3D Studia Max, můžete k modelu přidat i normálové vektory, které jsou potřeba pro výpočet osvětlení. V tomto případě není co řešit, protože Direct3D tyto vektory automaticky načte spolu s ostatními atributy vrcholů. Nyní si ale představte, že model normály neobsahuje. Mesh bez normál se zobrazí buď černý anebo bílý pokud vypnete osvětlení. Abychom tento problém vyřešili, použijeme funkci knihovny D3DX D3DXComputeNormals(). To vypadá náramně jednoduše, ale tak snadné to není. Uvedená funkce předpokládá, že ve formátu vrcholů meshe bude složka normál (D3DFVF_NORMAL), v opačném případě se totiž nic nestane. Takže nejprve musíme "přeformátovat" mesh do vlastního formátu. To znamená, že všem vrcholům meshe určíme vlastní formát s tím, že společné vlastnosti se kopírují. Proto jsem vytvořil nový typ MESH_VERTEX a formát tohoto typu MESH_FORMAT: struct MESH_VERTEX #define MESH_FORMAT D3DFVF_XYZ|D3DFVF_NORMAL|D3DFVF_TEX1|D3DFVF_DIFFUSE Všimněte si, že vrchol obsahuje to samé co VERTEX. Nyní použijeme metodu CloneMeshFVF(), abychom mesh přeformátovali. Tato metoda vytvoří identickou kopii zdrojového meshe, ale vrcholy mají pochopitelně požadovaný formát. m_lpMesh->CloneMeshFVF(m_lpMesh->GetOptions(), MESH_FORMAT, m_lpDevice, &lpMesh); V dalším kroku zjistíme, zda-li původní mesh již obsahoval normály, v tomto případě nemá smysl generovat nové. Jinak použijeme výše uvedenou funkci D3DXComputeNormals(). if(!(m_lpMesh->GetFVF() & D3DFVF_NORMAL)) Dále jsem přidal novou metodu třídě XMesh, která nastaví barvu určitého podobjektu (subset) meshe. K tomu, abychom mohli přistupovat k vrcholům jednotlivým podobjektům je třeba mít k dispozici tabulku atributů. Tato tabulka po vytvoření meshe neexistuje a je třeba ji vytvořit metodou Optimize() s parametrem D3DXMESHOPT_ATTRSORT. lpMesh->Optimize(D3DXMESHOPT_ATTRSORT, NULL, NULL, NULL, NULL, &m_lpMesh); Nyní již je tabulka k dispozici a můžeme použít metodu GetAtrributeTable(). int XMesh::SetSubsetColor(int iSubSet, D3DCOLOR dwColor)
} V metodě nejprve získáme tabulku atributů, v prvním kroku je třeba získat velikost tabulky (v tomto případě zadáváme hodnotu NULL), dále získáme vlastní tabulku opakovaným voláním. V tabulce jsou mimojiné uloženy offsety ve vertex bufferu pro jednotlivé podobjekty a počet vrcholů. Poté VB zamkneme a modifikujeme vybraným vrcholům barvu. Na závěr VB opět odemkneme. 32.1. ZávěrPříště se ještě podrobněji vrátíme k mlze, protože v Direct3D je možností mnoho. Nakonec si ještě trochu pohrajeme s kamerou, aby se nemohla dostat pod terén. Těším se příště nashledanou.
|
|||||||||||||||||||||||||||||||||||||