Hlavní navigace

Open Inventor: Vesmírná scéna (4)

10. 10. 2003
Doba čtení: 9 minut

Sdílet

Dnes jsou na pořadu dne textury. Otexturujeme si nejen planety, ale vytvoříme i velmi efektní pozadí pro naši scénu, které úplně změní dojem uživatelů naší grafické aplikace.

Texturování je technika, která posunula virtuální realitu do úplně nových rozměrů. Otexturovat trojúhelník znamená nanést na něj nějaký obrázek. Jak ho správně nanést a jak aktivovat různé módy nanášení, to se dozvíte v článku.

Tabulka č. 501
2.6 – Sky Box 1 Vesmírné pozadí 1 (s drobnými mouchami)
2.7 – Sky Box 2 Vesmírné pozadí 2 (finální verze naší aplikace)

V prvním příkladu budeme řešit, jak nanést textury na naše planety a jak proměnit naši „drátěnou krabici“ z dřívějších příkladů v hvězdné pozadí. V prvním příkladu jsou také z demonstračních důvodů záměrně nechány určité chyby. Druhý příklad představuje finální řešení naší Sluneční soustavy. Do hry to má sice ještě daleko, ale opustíme tuto tématiku, abychom si ukázali i další věci, které nám Open Inventor nabízí.

Jinak vás prosím o čtenářský ohlas, protože jsem získal pocit, že na článek sice pokliká hodně lidí, ale nepřečte si ho skoro nikdo. Proto pokud mi přímo nechcete odpovědět na několik otázek v závěru článku, tak mi alespoň pošlete prázdného maila. Každý mail budu brát jako hlas k pokračování v tutoriálu. Jinak je možné, že se rozhodnu zaměřit své pracovní úsilí jiným směrem.

Příklad 2.6: Sky Box 1 (Vesmírné pozadí)

Centrem pozornosti tohoto příkladu budou textury. Nejjednodušší věc, co teď můžeme udělat, je otexturovat naše planety. Na internetu jsem kdysi sehnal texturu pro Zemi, Venuši a několik dalších planet. Merkur však chybí. Trochu složitější bude natexturovat hvězdné pozadí. Zde jsou zdrojáky i s texturami (Linux, Windows). Po spuštění se nám naskytne následující pohled:

Pokud je vše v pořádku, měli bychom vidět krásnou hvězdnou oblohu a otexturovanou Zemi a Venuši.

Kdyby se náhodou po spuštění v konzoli objevila hláška, že formát jpg není podporován, tak jste v době provádění configure pro simage neměli nainstalovánu knihovnu libjpeg development verzi. Je třeba ji nainstalovat a provést znova configure a make install pro simage. Po configure mrkněte do výpisu, které formáty jsou na vašem systému podporovány. Formáty Targa (.tga), PIC (.pic), SGI RGB (.rgb, .bw) a XWD (.xwd) jsou pevně vestavěny do simage, pro ostatní musíte mít nainstalovány balíčky na svém systému a simage si jejich přítomnost detekuje při configure. Pro Windowsáky je řešení mnohem jednodušší – stačí si stáhnout novou simage.dll knihovnu a nahrát ji k exe souboru. Ještě připomínka – exe a textury musí být v našem případě ve stejném adresáři. simage je k dosašení na ftp://ftp.coin3d­.org/pub/coin/bin/win32/ jako simage_bin.zip nebo simage_dev.zip. Jako nouzové řešení pro všechny OS můžete také překonvertovat jpg textury do tga a modifikovat zdrojáky, aby se odkazovaly na tyto soubory.

Nyní se pojďme podívat, jak vše funguje. Jednoduchost texturování v Inventoru je v tom, že nám v nejjednodušším případě stačí pouze specifikovat jméno dané textury:

SoTexture2 *texture = new SoTexture2;
texture->filename.setValue(textureName);
root->addChild(texture);

Tento kód způsobí, že všechna následná geometrie bude rendrována s texturou dle parametru textureName. Programátoři OpenGL si jistě vzpomenou, že pro texturování potřebujeme specifikovat i texturovací souřadnice. Jak to provést, si ukážeme až u hvězdného pozadí. My zde texturovací souřadnice nepotřebujeme, protože Inventor obsahuje automatický generátor těchto souřadnic. Pro kouli (SoSphere) jsou souřadnice generovány tak, že se textura obtočí kolem koule a vrch textury odpovídá „severnímu pólu“ a spodek „jižnímu“, použijeme-li geografické terminologie. Rovník odpovídá vodorovné čáře umístěné doprostřed výšky obrázku.

Ještě jednu věc musíme provést: Změnit materiál planety na bílou. Jinak by původně modrá barva materiálu obarvila i texturu.

Teď se ale pojďme podívat, jak vytvoříme otexturované hvězdné pozadí. Kód je trošičku delší:

  // Souřadnice pro rendrované trojúhelníky jsou
  // předpřipravené ve skyBoxVertices.

  SoCoordinate3 *coords = new SoCoordinate3;
  coords->point.setValues(0, 8, skyBoxVertices);
  root->addChild(coords);

  // Texturovací souřadnice (v našem případě jsou
  // společné pro všechny strany krychle)
  SoTextureCoordinate2 *texCoord = new SoTextureCoordinate2;
  texCoord->point.set1Value(0, SbVec2f(0,0));
  texCoord->point.set1Value(1, SbVec2f(1,0));
  texCoord->point.set1Value(2, SbVec2f(0,1));
  texCoord->point.set1Value(3, SbVec2f(1,1)); root->addChild(texCoord);

  // Textury,
  // pro každou stranu krychle máme jednu
  // model DECAL znamená, že textura přepíše původní barvu objektu
  SoTexture2 *textures[6];
  for (i=0; i<6; i++) {
    textures[i] = new SoTexture2;
    textures[i]->model.setValue(SoTexture2::DECAL);
  }
  textures[0]->filename.setValue("sky00.tga");
  textures[1]->filename.setValue("sky06.tga");
  textures[2]->filename.setValue("sky12.tga");
  textures[3]->filename.setValue("sky18.tga");
  textures[4]->filename.setValue("skyN0.tga");
  textures[5]->filename.setValue("skyS0.tga");

for (i=0; i<6; i++) {
    root->addChild(textures[i]);


    // SoIndexedTriangleStripSet nám vyrendruje trojúhelníky.
    // Každá položka z coordIndex je index do pole vertexů
    // z SoCoordinate3::point. Speciální hodnota -1 ukončuje
    // aktuální "triangle strip".
    SoIndexedTriangleStripSet *strip = new SoIndexedTriangleStripSet;
    strip->coordIndex.setValues(0, 5, skyBoxIndices[i]);
    strip->textureCoordIndex.setValues(0, 5, skyBoxTexCoordIndex);
    root->addChild(strip);
  }

Vidíme několik kroků:

  1. Specifikujeme prostorové souřadnice (SoCoordinate3)
  2. Specifikujeme texturovací souřadnice (SoTextureCoor­dinate3)
  3. Vytvoříme objekty šesti textur (SoTexture2)
  4. V cyklu pro všech šest textur provedeme:
     – vložíme objekt dané textury
     – vložíme SoIndexedTrian­gleStripSet
     – specifikujeme správně indexy pro prostorové i texturovací souřadnice

Protože používáme indexovaný triangle strip set, stačí specifikovat prostorové i texturovací souřadnice pouze jednou v SoCoordinate3 a SoTextureCoor­dinate2 (body 1 a 2). Pak si předpřipravíme šest objektů SoTexture2 s texturami hvězdné oblohy. Poté v cyklu vytváříme postupně všech šest stran krychle. Vždy přiřadíme jednu z textur a v SoIndexedTri­angleStripSetu nastavíme prostorové souřadnice, které jsou pro každou stranu samozřejmě jiné. Dále texturovací souřadnice, které náhodou vyšly tak, že jsou pro všechny strany stejné (textury jsou správně zrotované).

Texturovací souřadnice jsou poměrně jednoduchá věc. Obyčejně je textura dvojrozměrná. To odpovídá dvěma texturovacím souřadnicím s a t. Hodnota [0,0] odpovídá levému hornímu rohu textury, hodnota [1,0] pravému hornímu rohu a hodnota [1,1] pravému spodnímu. My si však texturu můžeme nanést, jak se nám zlíbí – například jen výsek z ní nebo třeba vzhůru nohama. My použijeme tyto hodnoty pro náš čtverec:

  (0,1)---(1,1)
    |    /  |
    |   /   |
    |  /    |
  (0,0)---(1,0)

takže se levý horní roh obrázku zobrazí dole.

Ještě je tu jedna věc týkající se texturování hvězdné oblohy. Při vytváření textur přibyl řádek:

  textures[i]->model.setValue(SoTexture2::DECAL);

Máme totiž tři operace, kterými můžeme texturu nanést:

Tabulka č. 502
MODULATE Defaultní mód. Objekt se osvětlí stejným způsobem, jako kdyby byl neotexturovaný, a pak se výsledná barva vynásobí s barvou textury.
DECAL Textura přeplácne původní barvu objektu.
BLEND Textura udává míchací poměr mezi barvou vzniklou po osvětlení objektu a blendovací barvou specifikovanou v SoTexture2::blen­dColor.

Detailnější popis podává OpenGL dokumentace (funkce glTexEnv). My si zde však vystačíme s infomacemi zde uvedenými.

V praxi se nejčastěji používá mód MODULATE, který jsme my použili pro otexturování našich planet. Nemuseli jsme ho ani nastavovat, protože je to standardní hodnota pro SoTexture2::model. Pro tento model je charakteristické, že respektuje osvětlení ve scéně. Proto vidíme strany planet odvrácené od Slunce jako tmavé.

Pro hvězdné pozadí jsme však zvolili mód DECAL, protože nechceme, aby nám světlo Slunce osvětlovalo toto pozadí. Tak bude mít pozadí opravdu takovou barvu, jakou nám specifikuje textura (obdoba předsvětlené scény).

Teď už opouštíme téma textur. Mnozí z vás si možná všimli zajímavého chování hvězdného pozadí, pokud letíme dále od Slunce. To napravíme v následujícím příkladu.

Příklad 2.7: Sky Box 2 (Vesmírné pozadí)

Pokud jste si pořádně odzkoušeli minulý příklad, určitě jste si všimli, že pokud se příliš vzdalujete od Slunce, hvězdy na skyboxu se nejprve zvětšují a pak proletíme jeho stěnami do „temnoty“. Zde je obrázek demonstrující problém ve chvíli těsně před průletem stěnou:

Jedno z řešení je udělat skybox velmi velikých rozměrů. Neměli bychom příliš přestřelovat, protože to může mít negativní vliv například na přesnost z-bufferu. Je tu ale ještě lepší řešení, a to posunovat skybox s kamerou. Tak se nikdy nestane, že by uživatel doletěl až do blízkosti stěn skyboxu. Zde jsou zdrojáky: Linux, Windows.

Celé řešení se skrývá v následujících dvou kusech kódu. V prvním z nich vložíme do scény kameru (již se nadále nebudeme spoléhat na implicitní kameru vkládanou SoFlyViewerem):

  // Kamera pozorovatele,
  // přestože viewer by nám ji vytvořil automaticky,
  // spolehneme se tentokrat na vlastní síly.
  // Budeme totiž kameru potřebovat v createSkyBox().

  SoPerspectiveCamera *camera = new SoPerspectiveCamera;
  root->addChild(camera);

  // Vytvoř hvězdné pozadí
  root->addChild(createSkyBox(camera));


Druhý kus kódu je ze začátku funkce createSkyBox:

  // Translace.
  // Posunujeme skybox tak, aby jeho střed
  // byl vždy na pozici kamery.

  SoTranslation *trans = new SoTranslation;
  trans->translation.connectFrom(&camera->position);
  root->addChild(trans);

V tomto kódu vytvoříme objekt SoTranslation, který slouží k posunutí objektů ve scéně. Jeho field translation, který obsahuje vektor posunutí, připojíme na field position u kamery. Tím se, kdykoliv posuneme kameru, posune i skybox. Nemůže se už tedy nikdy stát, že bychom se byť jen přiblížili k jeho stěnám.

Program si můžete vyzkoušet. Můžete na něm postavit i nějakou hru. V našem tutoriálu už ale opustíme vesmírnou tématiku. Možná se k ní zase časem vrátíme, ale je čas začít další sérii příkladů.

Bonus

Jiří Hnídek mi zaslal univerzální makefile, který sám používá ke kompilování příkladů tohoto tutoriálu. Zkompiluje všechny cpp soubory v daném adresáři do spustitelných souborů stejného jména jako zdrojové cpp soubory. Kdo chcete, si ho můžete stáhnout.

Jak jsem kdysi slíbil, tak pro všechny zájemce jsem vyrobil třídy dědičnosti v Inventoru. Je jich opravdu hodně a graf určitě není kompletní (nové třídy neustále přibývají). Ale kdysi jsem si ho vylepil na skříň vedle počítače a v dobách, kdy jsem s Inventorem začínal, mi to hodně pomohlo. Kdo máte zájem můžete si to stáhnout. Je to tam pro jistotu v několika formátech (ps, pdf, bmp), protože jsem měl kdysi velké problémy to vytisknout.

Také jsem připravil dokumentaci k funkcím vícehodnotových fieldů. Ne, že bych vás chtěl zahlcovat, ale bude následovat kratší pauza, než bude připravena další série. Dokumentaci si můžete stáhnout či prohlédnout zde. (Připomínám, že tyto funkce v originální dokumentaci chybí. To je způsobeno chybou v Doxigen, což je nástroj pro generování dokumentace.)

Závěr

Prosil bych vás o zaslání jakýchkoliv komentářů či nápadů do diskuse pod článkem nebo na mou adresu. Především by mě zajímalo:

root_podpora

  • Připadají vám příklady zajímavé a ocenili byste například méně vysvětlování a více příkladů? Nebo je to v pohodě?
  • Je pro vás instalace Inventoru příliš složitá? Byl by zájem o balíčky? Pokud ano, připojte jméno vaší distribuce. Měli byste zájem o borlandí verzi, nebo o verzi pro Mingw?.
  • Postrádáte nějaké informace, dokumentaci nebo máte nějaký tip třeba na příklady, které byste uvítali?

Pokud nemáte vůbec žádné připomínky, pošlete mi alespoň prázdný email jakožto hlas pro to, aby tutoriál i nadále vycházel.

Jinak děkuji za pozornost, za všechny vaše emaily, a snad za měsíc bychom mohli začít další sérii, jejíž téma nebude situováno ve vesmíru, ale na zemském povrchu jako „zobrazování a generování krajiny“.

Byl pro vás článek přínosný?