J3DT_7 - Textury

Cíle kapitoly

Po přečtení této kapitoly budete schopni:

Vzhled mnoha objektů v reálném světě záleží na jejich texturách. Textura objektu je opravdu realtivně složitou geometrií na povrchu objektu. Abychom docenili roli textury povrchu ve vzhledu skutečných objektů, uvažujme například o koberci. I když jsou všechna vlákna koberce stejné barvy, tak se nám koberec nejeví jako jednobarevný díky interakci světla s geometrií vláken. I když je Java3D schopná modelovat geometrii jednotlivých vláken, tak paměťové nároky a vykreslovací výkon potřebný k zobrazení koberce v místnosti s takovými detaily činí takový model nepoužitelným. Na druhou stranu pokud máme jen plochý polygon jedné barvy není také plnohodnotnou náhradou koberce.

Do této chvíle tohto tutoriálu bylo veškerých vizuálních detailů dosaženo pomocí geometrie. Důsledkem takových vizuálně bohatých objektů, jako stromů, je velké množství geometrie což vyžaduje příslušnou paměť a výpočetní výkon. To se na jisté úrovni detailů může stát nepřijatelným. Tato kapitola ukazuje jak přidat vizuální detaily na povrchu vizuálního objektu bez nutnosti další geometrie pomocí textur.

7.1 Co je to texturování

Koberec může být extrémním příkladem co se týká složitosti a hustoty geometrie povrchu, ale není zdaleka jediným objektem jehož texturu můžeme vnímat. Cihly, beton, dřevo, trávník, zdi a papír jsou některé z objektů pro které můžeme použít rovinnou geometrii (polygony) abychom vyjádřily jejich tvar, ale ne jejich detail. Stejně jako u koberce je cena při reprezentaci textury povrchu geometrií docela vysoká.

Možnou alternativou jak modelovat vlákna koberce je modelovat jej jako polygon s mnoha vrcholy a odlišnými barvami. Pokud budou vrcholy dostatečně blízko, pak můžeme vyprodukovat obraz koberce. Tento model vyžaduje mnohem méně paměti než ten s vlákny; avšak pořád to může být mnoho pro model koberce ve skutečné velikosti. Tato myšlenka, reprezentovat obrázek objektu na povrchu je základním kamenem texturování. Avšak pomocí textur můžeme geometrii velmi zjednodušit.

Texturování, nebo také mapování textur, je způsob jak přidat vizuální detaily povrchu bez nadbytečné geometrie. Vizuální bohatost je dodána obrázkem, nazývaném také textura, která dodává detily povrchu vizuálního objektu. Obrázek je namapován na geometrii vizuálního objektu při vykreslování. Proto termín mapování textury.

Následující obrázek ukazuje některé z textur použitých v ukázkových příkladech této kapitoly. Jak můžete vidět, textura může poskytnout vizuální bohatost objektům různých velikostí. Proužkovaná textura se slovy je použita v mnoha příkladech demonstrujících flexibilitu texturovaných objektů, slova slouží k lehčímu rozlišení hran obrázku textury.

doplnit fig 7-1

7.2 Základy texturování

Texturování polygonů v Java3D je dosaženo vytvořením příslušného objektu vzhledu a načtením textury do tohoto objektu, určením umístění obrázku textury na geometrickém objektu a nastavením texturovacích atributů. Jak uvidíte, nastavení textur může být složité. Naštěstí existují třídy utilit které vám s tím pomohou a základní nastavení textur je vhodné pro jednoduché aplikace textur.

Abychom vysvětlili texturování, sekce 7.2.1 obsahuje jednoduchý recept; v sekci 7.2.2 vyvineme ukázkový program dle receptu a řekneme více o texturování. Zbývající podsekce představují další ukázkové programy a blíže vysvětlují nastavení textur. Volby nastavení textur nejsou vysvětleny v kontextu s příklady a budou diskutovány spolu s detily API v sekci 7.7.

7.2.1 Jednoduchý recept pro texturování

Díky flexibilitě texturování v Java3D API může být počet voleb nastavení textur na rpvní dojem trochu frustrující. I když samotné texturování nemusí být obtížné. K ulhečení práce s nastavením textur poslouží následující recept.

Recept jen naznačuje kroky které vedou přímo k texturování. Čtenář by si měl uvědomit že geometrie a vzhled se nastavují pro objekt Shape3D který je přidán do grafu scény. Předchozí kapitoly tohoto tutoriálu, kapitoly 1 a 2 částečně pokryly kroky tohoto receptu:

  1. připravte si obrázky textur
  2. načtěte textury
  3. vložte textury do objektu Appearance
  4. určete TextureCoordinates objektu Geometry

Stejně jako s mnoha ostatními recepty v těchto tutoriálech, i v tomto receptu můžete provést kroky v jiném pořadí. Popravdě kroky tohto receptu mohou být provedeny v jakémkoli pořadí (jestliže kroky 2 a 3 jsou provedeny spolu).

Texturování, krok 1: připravte si obrázek textury

Tento recept začíná krokem který se netýká programování: "připravte si obrázky textur". Vytváření a upravování textur je něco co se obvykle děje mimo programy v Java3D. Většina obrázků textur je připravena před programem. Existují dva důležité úkoly při přípravě obrázků textur:

  1. vytvoření obrázků se správnými rozměry
  2. uložení obrázku ve formátu který můžete načítat

Samozřejmě obrázek můžete upravovat abyste dosáhli požadovaných barev, průhlednosti a návaznosti.

Pro efektivní vykreslování vyžaduje Java3D velikost textury která je mocninou čísla dvě (1, 2, 4, 8, 16 ...) v každém rozměru. opomenutí tohoto požadavku se projeví chybou za běhu programu.

Pokud obrázek nemá správné rozměry, musí být upraven (zmenšen nebo oříznut) aby měl správné rozměry před prvním použitím, Úpravování obrázku můžete provést v řadě programů včetně Java Advanced Imaging API (JAI). Na předchozím obrázku jsou dva menší obrázky s rozměry 128x128. Strom má rozměr 256x128 a zeměkoule o rozměru 256x256.

Můžete uvažovat o jakémkoli formátu souboru, pokud pro formát souboru textury (obrázku) existuje metoda jak jej načíst. Programy v této kapitole používají třídu TextureLoader (více informací o loaderech textur je v dalším kroku, detaily API jsou v sekci 7.7). Object TextureLoader načítá formáty JPEG, GIF a jiné.

Ještě jednou něco řekneme o ukázkových programech předtím než půjdeme na další krok. Ukázky kódu a ukázkové programy v této kapitole používají jména některých souborů které jaou uloženy ukázkovém balíčku s příklady jako jar archiv. Na těchto souborech není nic zvláštního kromě faktu že odpovídají požadavkům o rozměrech mocniny dvou. V těchto programech může být použit jakýkoli jiný soubor odpovídající tomuto požadavku. Vyzkoušejte si zkompilovat a spustit ukázkové programy se svými vlastními obrázky. Teď když máme připravené obrázky textur, můžeme začít programovat.

Texturování, krok 2: Načtení textury

Dalším krokem je načíst připravený obrázek do objektu. Tento krok je známý jako načítání textury. Textury mohou být načítány ze souboru nebo URL použitím stejného postupu. Načtení textury může být otázkou mnoha řádků kódu, nebo pomocí dvou řádků kódu s použitím objektu třídy TextureLoader. Výsledkem obou způsobů je načtení obrázku do objektu třídy ImageComponent2D. Následující ukázka kódu obsahuje dva řádky kódu které používají TextureLoader. Výsledkem těchto dvou řádků je načtení souboru stripe.gif do objektu ImageComponent2D který může být použit k vytvoření nezbytného objektu vzhledu v kroku 3.

1.	TextureLoader loader = new TextureLoader("stripe.gif", this);
2.	ImageComponent2D image = loader.getImage();

Předtím než se přesuneme na krok 3 našeho receptu, podívejme se blíže na objekt TextureLoader Druhým argumentem v konstruktoru je objekt který slouží jako pozorovatel načítání obrázku (ImageObserver). Třída TextureLoader používá balíček java.awt.image pro načítání obrázků. Tento balíček načítá obrázky asnychronně, což je z části užitečné při načítání obrázku z URL. K zajištění řízení asynchronního načítání obrázků, jsou komponenty AWT schopny být pozorovateli tohoto procesu. Pozorovatel obrázku může podávat detailní informace o průběhu načítání.

Pro účely psaní Java3D programů stačí když budete vědět že jakákoli AWT komponenta může sloužit jako pozorovatel obrázku. Protože Applet rozšiřuje AWT komponentu Panel, objekt třídy Applet Java3D programu může sloužit jako pozorovatel obrázku pro objekt TextureLoader. Další detaily o pozorovateli obrázků jsou za hranicemi tohoto tutoriálu. Pro více informací viz. referenční příručka AWT.

Texturování, krok 3: vytvoření objektu Appearance

Abychom mohli obrázek načtení v kroku 2 použít jako texturu pro vizuální objekt, musíme ho vložit do objektu Texture, který poté může být použit v objektu vzhledu odkazovaném vizuálním objektem. Přesnějí, objekt Texture2D drží odkaz na obrázek textury. Obrázek ImageComponent2D načtený v kroku 2 je klíčem vytvoření objektu vzhledu.

Následující ukázka kódu obsahuje dva řádky kódu spolu s dalším kódem které dohromady vytváří jednoduchý texturovací objekt vzhledu. Když máme načtenu texturu (řádky 1 a 2), je obrázek vložen do objektu Texture2D (řádek 4). Poté je objekt Texture2D vložen do objektu Appearance (řádek 6).

1.	TextureLoader loader = new TextureLoader("stripe.gif", this);
2.	ImageComponent2D image = loader.getImage();
3.	Texture2D texture = new Texture2D();
4.	texture.setImage(0, image);
5.	Appearance appear = new Appearance();
6.	appear.setTexture(texture);

Objekt vzhledu vytvořený v předchozí ukázce by mohl mít i jiné kompnenty uzlu, např. z uzlu TextureAttributes. Pro tento jednoduchý příklad si ale vystačíme bez vlastností TextureAttributes.

Texturování, krok 4: nastavení souřadnic textury

Po načtení textury do objektu vzhledu je úkolem programátora určit umístění textury na geometrii pomocí souřadnic textury. Souřadnice textury se určují po geometrických bodech. Každá souřadnice textury představuje bod který bude nanesen na geometrický bod. Díky tomu můžeme obrázek rotovat, natahovat, deformovat anebo duplikovat tak aby vyhovoval obecným požadavkům (přesněji, souřadnice textur jsou lineárně interpolovány mezi vrcholy tak aby byly přesně namapovány na geometrii objektu, viz. sekce 7.2.3).

TextureCoordinates jsou určeny pomocí s (horizontální) a t (vertikální) rozměrů obrázku textury v rozmezí 0.0 až 1.0, viz. následující obrázek.

doplnit fig 7-3

Referenční blok uvedený níže obsahuje jen některé z mnoha metod třídy GeometryArray schopných nastavit souřadnice textury. Tento blok obsahuje zavrhnutou metodu settextureCoordinate(), více najdete v sekci 7.7 nazvané "O změnách v API". Pro více informací o metdách setTextureCoordinate() viz Java3D API specifikace. Více informací o třídě GeometryArray najdete v kapitole 2 tohoto tutoriálu.


Částečný seznam metod setTextureCoordinate() třídy GeometryArray

Souřadnice textury jsou určeny geometrickými body skrze jednu z mnoha metod setTextureCoordinate což jsou metody třídy GeometryArray. Od verze Java3D API 1.2 se však metody pro práci se souřadnicemi textur změnily. Více najdete níže.

Tento referenční blok obsahuje pouze dvě nové metody z celkem devíti. Pro více informací viz. Java3D API specifikace.

void setTextureCoordinate(int texCoordSet, int index, TexCoord* texCoord)

Nastavuje souřadnice textury spojené s vrcholem na určený index souřadnice textury tohto objektu. Existuje mnoho podobných metod.

texCoordSet - souřadnice textury v daném geometrickém poli

index - cílový index vrcholu v daném geometrickém poli

texCoord - jakýkoli objekt třídy TexCoord (např. TexCoord2f nebo TexCoord3f obsahující nové souřadnice textury)

void setTextureCoordRefFloat(int texCoordSet, float[] texCoords)

Nastavuje odkaz pole souřadnic textury s přesností float na určené souřadnice textury daného pole.

texCoordSet - souřadnice textury tohoto geometrického pole

texCoords - pole hodnot 2*n nebo 3*n na které bude odkaz nastaven

void setTextureCoordinate(int index, Point2f texCoord) <deprecated>

Nastavuje souřadnici textury spojenou s vrcholem na daný index tohoto objektu


Následující ukázka kódu vytváří rovinu pomocí objektu QuadArray. Souřadnice textur jsou přiřazeny každému vrcholu. Řádky dva až deset vytváří čtyři rohy čtverce v prostoru. Řádky 12 až 20 mapují polohu textury na geometrii. Tato ukázka kódu vytváří rovinu s délkou strany 2 metry a umisťuje na ni obrázek textury s normální orientací.

1.	QuadArray plane = new QuadArray(4, GeometryArray.COORDINATES | GeometryArray.TEXTURE_COORDINATE_2);
2.	Point3f p = new Point3f();
3.	p.set(-1.0f, 1.0f, 0.0f);
4.	plane.setCoordinate(0, p);
5.	p.set(-1.0f, -1.0f, 0.0f);
6.	plane.setCoordinate(1, p);
7.	p.set(1.0f, -1.0f, 0.0f);
8.	plane.setCoordinate(2, p);
9.	p.set(1.0f, 1.0f, 0.0f);
10.	plane.setCoordinate(3, p);
11.
12.	TexCoord2f q = new TexCoord2f();
13.	q.set(0.0f, 1.0f);
14.	plane.setTextureCoordinate(0, 0, q);
15.	q.set(0.0f, 0.0f);
16.	plane.setTextureCoordinate(0, 1, q);
17.	q.set(1.0f, 0.0f);
18.	plane.setTextureCoordinate(0, 2, q);
19.	p.set(1.0f, 1.0f);
20.	plane.setTextureCoordinate(0, 3, q);

Následující obrázek ukazuje vztah mezi souřadnicemi vrcholu a souřadnicemi textury pro náš příklad s otexturovanou rovinou.

doplnit fig 7-4

Teď když máme hotové tři kroky texturování, můžeme otexturovaný objekt vložit do grafu scény. Následující sekce představuje sérii ukázkových příkladů demonstrujících některé možnosti texturování.

O změnách v API... a o multitexturování

Předchozí referenční blok obsahuje jedinou zavrženou metodu v tomto tutoriálu (momentálně, od verze Java3D API 1.3 a 1.4 je jich trochu víc, pozn. překladatele). Měli byste se vyhnout zavrženým částem API (tzn. konstruktorům, metodám, třídám...). Ale metoda setTextureCoordinate(int index, Point2f texCoord) třídy GeometryArray je zde uvedena ze dvou důvodů.

První důvod je poukázat na nejvíce opravené třídy ve verzi 1.2 Java3D API. Změnily se všechny metody setTextureCoordinate() třídy GeometryArray. V předchozích verzích API používaly metody setTextureCoordinate() třídy Point* místo tříd TexCoord*. Přechod ke třídám TexCoord* byl učiněn právě ve verzi 1.2 a všechny metody setTextureCoordinate() používají třídy TexCoord* jak bylo původně původně zamýšleno.

Druhým důvodem pro uvedení zavržené metody spolu s novými metodami je představit multitexturování pomocí vysvětlení nového parametru v metodách setTextureCoordinate(). Možná jste si všimli přidaného přidaného v těchto metodách. První parametr metody setTextureCoordinate() vybírá souřadnice textury které se použijí. Což dává smysl pouze pokud má objekt GeometryArray více množin souřadnic textur. A to je přesně tento případ; objekt GeometryArray může mít více množin (setů, sad) souřadnic textur.

Java3D API verze 1.1 představilo multitexturování. Multitexturování (multitexturing) je schopnost aplikovat na jeden vizuální objekt více textur. Pokud aplikujeme více textur na jeden vizuální objekt, pak pro tento vizuální objekt je potřeba více sad souřadnic textur takže každá textura má svoje pole souřadnic.

Mnoho aplikací může těžit z multitexturování. Například textura může dodat objektu vzhled cihlové zdi. Avšak tyto objekty mohou vypadat nevěrohodně díky opakovanému nanesení stejné textury na velký objekt. Můžeme tedy aplikovat druhou texturu představující prach nečistoty nebo graffiti přes původní textur s cihlami abychom vytvořili objekt který vypadá více realisticky. Další apliakce zahrnující multitexturování je textura představující stín přes už otexturovaný objekt.

Protože zbývá vysvětlit spoustu detailů o základech texturování, necháme si multitexturování do sekce 7.8. Důvodem proč jsme se tohoto tématu dotkli nyní je vysvětlit důvod přítomnosti parametru texCoordSet metod setTextureCoordinate(). Ale také povědomí o možnosti použít více textur na jeden vizuální objekt vás může inspirovat a pomůže vám více experimentovat s texturami.

7.2.2 Ukázkové programy jednoduchého texturování

Následováním receptu jsme vyvinuli jednoduchý texturovací program. Kompletní příklad najdete v souboru SimpleTexturingApp.java v adresáři examples/texture. Následující obrázek vlevo ukazuje scénu vyrenderovanou programem SimpleTexturingApp. Tento program ukazuje jen o trochu víc než program pro zobrazení obrázků.

doplnit fig 7-5

Použitím objektu RotationInterpolator (ty jsou probírány v kapitole 5 - Animace) byl vytvořen jiný program nazvaný SimpleTextureSpinApp. V tomto programu rotuje stejná otexturovaná rovina jako v předchozím příkladě. Předchozí obrázek ukazuje běh programu vpravo. Všimněte si jedné věci, a to že zadní strana roviny je bílá.

Třída NewTextureLoader

Java3D programy používající textury mohou obsahovat spoustu řádek které jen načítají textury a vytvářejí objekty vzhledu. Pokud je to možné, můžete si ušetřit mnoho řádek kódu a výjimek za běhu, sdílením objektů vzhledu. Avšak to úplně nezmenší úsilí potřebné k programování. Další redukce může být uštřeno vytvořením třídy která vytváří objekty vzhledu textury. Výzva vytvoření takové třídy spočívá v napsání pozorovatele objektu TextureLoader.

Jako pozorovatel obrázku může sloužit objekt Canvas3D nebo Applet, avšak mít všude odkaz na komponentu vás může obtěžovat. Abych se tomu vyhnul, rozšířil jsem třídu TextureLoader a eliminoval tak potřebu pozorovatele. "instead a single method is used to specify an image observer for all future uses of the texture loader."

Konstruktory třídy NewTextureLoader jsou stejné jako konstruktory třídy TextureLoader kromě toho že nevyžadují pozorovatele obrázku (třída ImageObserver). Metody třídy TextureLoader jsou ty samé jako metody TextureLoader plus jedna navíc pro nastavení pozorovatele obrázku.

Jiný ukázkový program, TexturedPlaneApp, načítá tři textury a zobrazuje je na rovinách jak vidíte na obrázku níže. Významným rozdílem je že textury jsou načteny pomocí samostatné třídy TexturedPlane pomocí třídy NewTextureLoader. Návrh třídy TexturedPlane není dost flexibilní na to aby se dala použít ve více aplikacích, ale slouží jako demonstrativní příklad pro podobné třídy.

doplnit fig 7-6

Následující ukázka kódu je výtah z programu TexturedPlaneApp a je téměř celým kódem potřebným k vytvoření tří texturovaných rovin této aplikace. Objektu NewtextureLoader je předán objekt pozorovatele v třídě TexturedPlane.

1.	scene.addChild(tg0);
2.	tg0.addChild(tg1);
3.	tg1.addChild(new TexturedPlane("stripe.gif"));
4.	
5.	tg0.addChild(tg2);
6.	tg2.addChild(new TexturedPlane("brick.gif"));
7.
8.	tg0.addChild(tg3);
9.	tg3.addChild(new TexturedPlane("earth.jpg"));

7.2.3 Více o souřadnicích textury

Jak je uvedeno v "Texturování, krok 4", obrázek textury je namapován na geometrii pomocí určení souřadnic textury. Probíhá to tak že se texely textury namapují na pixely geometrie v době vykreslování. Každý pixel textury se nazývá texel, neboli "prvek textury". To je proces zvaný mapování textury.

Mapování textury začíná určením souřadnic textury pro vrcholy geometrie.