Hlavní navigace

Grafická knihovna OpenGL (22): texturování

2. 12. 2003
Doba čtení: 9 minut

Sdílet

Tímto dílem seriálu o grafické knihovně OpenGL se začínáme věnovat poměrně rozsáhlé a přitom důležité a zajímavé oblasti polygonální trojrozměrné grafiky. Řeč je o texturování, což je, zjednodušeně řečeno, metoda pro vykreslování trojrozměrných těles, která mají na svém povrchu naneseny rastrové obrazce. Při použití texturování je možné vytvářet a zobrazovat vizuálně složité scény bez větších nároků na výpočetní výkon grafického subsystému.

Texturování 1

Co si představit pod pojmem texturování?
Kdy a proč používat texturování
Výhody použití rastrových textur
Nevýhody rastrových textur
Podpora textur v OpenGL
Omezení texturování v OpenGL
Pokračování
Demonstrační příklady
 

Co si představit pod pojmem texturování?

Texturování (resp. přesněji nanášení textur) se označuje princip obarvení povrchu zobrazovacích těles různými obrazci. Důležité přitom je, že se nijak nemění geometrické vlastnosti těles, pouze se jinak zobrazuje jejich povrch. Obrazce, které se na povrch těles nanášejí, se nazývají textury (textures). Tyto textury jsou většinou představovány plošnými obrázky (dvoudimenzionální textury), některé grafické systémy však podporují i vykreslování jednorozměrných a dokonce trojrozměrných (objemových) textur.

Obrazce pro textury se mohou vytvářet několika způsoby. Buď je možné použít klasické rastrové obrázky (vzniklé například namalováním, vyfocením nebo naskenováním), nebo se textura může vytvářet pomocí různých algoritmů založených většinou na fraktálních technikách – tímto způsobem vznikají takzvané procedurální textury. Procedurální textury lze použít buď pro výpočet rastrových obrázků před vlastním vykreslováním (po výpočtu obrázku se tato textura chová jako každý jiný rastrový obrázek se všemi výhodami i nevýhodami), nebo se může výpočet textur provádět v reálném čase až při vykreslování, přičemž se parametry výpočtu textury nastaví podle aktuální velikosti a orientace plošky, na kterou se textura nanáší. Tuto druhou možnost však OpenGL přímo nepodporuje, výpočet procedurálních textur je tedy nutné provádět „ručně“. Na prvním obrázku je ukázána procedurální textura vzniklá sloučením více fraktálních obrazců s procedurální texturou mramoru (marble).

Ukázka procedurální textury
Obrázek 1: Ukázka procedurální textury

V dalším textu se budeme téměř výhradně zabývat texturami reprezentovanými rastrovými obrázky, nezávisle na tom, jakým způsobem vznikly. I když řeč bude převážně o dvoudimenzi­onálních texturách (tedy bitmapách a pixmapách), většina zde popisovaných vlastností se vztahuje i na jednorozměrné a trojrozměrné textury. Případné výjimky a odlišnosti si samozřejmě postupně popíšeme. Na druhém obrázku je ukázána dvoudimenzionální, „ručně“ nakreslená textura. Rastrové textury budeme v demonstračních příkladech vyrábět buď programově (různé šachovnice apod.), nebo je budeme načítat z externích souborů.

Podobně, jako je bitmapa či pixmapa složena ze základních rastrových elementů (pixelů), je textura složena z texelů. Pixel a texel mají stejné vlastnosti a podobný či dokonce ekvivalentní způsob uložení v paměti. V dalším textu však budeme oba pojmy navzájem oddělovat, tj. pixel je element vykreslovaný na obrazovce, kdežto texel je rastrový element (většinou) dvourozměrné textury. Texturování potom spočívá v nanášení texelů na vykreslovaný povrch.

Ukázka nakreslené rastrové textury
Obrázek 2: Ukázka nakreslené rastrové textury

Kdy a proč používat texturování

Texturu lze použít ve všech případech, kdy je nutné vykreslovat tělesa se složitě strukturovanými povrchy, která však nevykazují velké změny v geometrii (tedy tvaru) povrchu. Typickým příkladem je cihlová zeď, která je v reálném světě složená z jednotlivých cihel spojených maltou. Při modelování této zdi sice můžeme každou cihlu reprezentovat například kvádrem s vhodně zvolenou barvou, ale v případě vykreslování velkých zdí by počet vykreslovaných těles rostl příliš rychle, takže by se zbytečně plýtvalo jak pamětí, tak i výpočetním výkonem grafického subsystému. Ještě horší situace by nastala například u koberce, kde by bylo nutné vytvářet všechna barevná vlákna apod.

V těchto případech je možné zeď nebo koberec reprezentovat jednou plochou (složenou například ze dvou trojúhelníků nebo jednoho čtyřúhelníku) a na tuto plochu potom nanést předem vytvořený rastrový obrázek. V případě zdi se tak sice připravíme o geometrické nerovnosti povrchu (ty lze částečně – při vhodném natočení tělesa – simulovat například bump-mappingem), ale vykreslení bude na dnešních počítačích dostatečně rychlé a v případě dostatečné velikosti (rozlišení) textury i kvalitní.

Textury se také někdy používají poněkud jiným způsobem pro vytváření a následné vykreslení různých složitých modelů, například stromů. Buď je možné strom namodelovat jako těleso obsahující až několik tisíc polygonů, nebo je možné vytvořit dvourozměrný obrázek stromu z několika směrů a strom vykreslit jako několik vzájemně se protínajících ploch s nanesenou konturou stromu – viz třetí obrázek. V tomto případě však textura musí být v některých místech průhledná, což ovšem při vykreslování nepředstavuje větší problém. Tato technika se nazývá billboarding, protože se vychází z podobnosti s klasickými billboardy (například ty u dálnic).

Vytvoření modelu stromu (billboardu) s využitím texturování

Obrázek 3: Vytvoření modelu stromu (billboardu) s využitím texturování

V minulosti se často také používaly otexturované objekty (většinou obdélníky), které byly k pozorovateli natočeny vždy stejnou stranou. Tyto objekty se nazývají sprity a byly použity zejména v mnoha úspěšných hrách, například Doom, pro vykreslování předmětů a potvůrek ve hře. Sprity samozřejmě můžeme používat i v OpenGL, lze například vykreslovat pixmapy (viz již dříve popsaný příkaz glDrawPixels()) nebo vhodně natočený obdélník pokrytý texturou. Texturování může být v tomto případě výhodnější, protože se (narozdíl od vykreslování pixmap) nemusí stále přenášet pixmapová data po sběrnici/portu, ale jsou uložena přímo v paměti grafického akcelerátoru.

Ukázka 3D scény z několika sprity
Obrázek 4. Ukázka 3D scény s několika sprity (snad nemusím psát, odkud ten screenshot pochází)

Výhody použití rastrových textur

Použití rastrových textur s sebou přináší samozřejmě své výhody i nevýhody. Jak jsme si již řekli, používá se technika texturování jako určitá náhrada při zobrazování složitých povrchů těles (zeď, omítka, dřevo, kámen), kdy se tento obecně nehomogenní povrch nahradí ploškou s nanesenou texturou. Záleží na vhodné volbě textury, velikosti objektu a nasvícení celé scény, zda tato náhrada bude dostatečná, či zda uživatel uvidí vizuální chyby ve scéně.

Velkou výhodou rastrových textur je jejich snadná implementace ve vykreslovacím řetězci. Jednoduché grafické akcelerátory řešily texturování tak, že se do jejich vykreslovacího řetězce přidala vyrovnávací paměť pro textury a několik interpolátorů, pomocí kterých se řešil přístup do texturovací paměti. Dnešní grafické akcelerátory jdou mnohem dál: textury je možné komprimovat, jsou podporovány mipmapy (textury ve více rozlišeních), antialiasing, multitextury apod. Texturovací jednotka však stále patří k těm částem vizuálního systému, která má velmi dobrý poměr složitost/vizu­ální efekt.

Další výhodou texturování je, že pokud u textur použijeme průhlednost (nazývanou také alfa kanál), je možné vizuálně změnit geometrii předmětů, protože se předmět může na některých místech jevit jako děravý. Ukázka této techniky je zobrazena na prvním obrázku. Poznamenejme, že při použití průhlednosti je nutné programově setřídit průhledné nebo poloprůhledné plošky, jelikož by v tomto případě Z-buffer (paměť hloubky) nepracoval korektně – i průhledné texely by se „vykreslily“ a poškodily tak informaci o hloubce fragmentu uloženou v Z-bufferu.

Nevýhody rastrových textur

První vážnou nevýhodou těchto textur je jejich předem dané rozlišení, tedy počet pixelů, ze kterých se textura skládá. Při volbě rozlišení textury se vždy musí zvolit kompromis mezi dvěma extrémy. Prvním extrémem je volba textury s malým rozlišením, což však při zobrazování vede k viditelným chybám (vzpomeňme například starší hry typu Wolfenstein nebo Doom, které používaly poměrně hrubé textury s rozlišením 64×64 pixelů; u těchto her se samozřejme ani OpenGL ani grafická akcelerace nepoužívala). Druhým extrémem je volba textury s neúměrně velkým rozlišením, kdy se naopak plýtvá pamětí na grafickém akcelerátoru.

Druhá nevýhoda spočívá ve faktu, že pokud zobrazujeme texturu na obrazovce a použije se zvětšení či zmenšení počtu zobrazovaných pixelů (což se stává při texturování téměř vždy, protože se na texturovaný povrch díváme z různých směrů a vzdáleností), dochází k aliasu (zjednodušeně řečeno tvorbě moaré), který není možné principiálně odstranit, lze pouze zmírnit jeho vizuální podobu. Proto se používají různé metody odstranění aliasu (tj. metody antialiasingu), které však komplikují zobrazovací řetězec a obecně vedou ke zpomalení vykreslování.

Třetí nevýhodou je skutečnost, že textury zabírají poměrně velké množství paměti. Pokud je v paměti grafického akcelerátoru dostatečné místo, je možné textury nahrát do této paměti a vykreslování tak několikanásobně urychlit. Pokud je však v této paměti místa málo (oblast paměti grafického akcelerátoru je mimo jiné obsazena všemi barvovými buffery, pamětí hloubky, stencil bufferem apod.), musí se textury při vykreslování nahrávat z hlavní paměti počítače, což zatěžuje sběrnici nebo port a zpomaluje vykreslování. Tuto nevýhodu částečně řeší různé metody komprimace textur.

Podpora textur v OpenGL

V OpenGL jsou podporovány pouze rastrové textury, které mohou být buď jednodimenzionální, dvoudimenzionální, nebo třídimenzionální. V případě jednodimenzio­nálních textur se prakticky jedná o pruh pixelů, pomocí něhož se dají realizovat různé barevné přechody. Dvoudimenzionální textury (rastrové bitmapy a pixmapy) jsou v dnešní době zdaleka nejpoužívanější a jsou podporovány na naprosté většině grafických akcelerátorů (dokonce bych napsal na všech, protože jsem zatím nezažil, že by texturování nefungovalo; veškeré chyby texturování spočívají buď v nedostatku paměti pro textury, nebo v chybě driveru, který musí textury přenášet přes port do grafického akcelerátoru).

Třídimenzionální textury (tj. objemové či voxelové textury) se používají především ve specializovaných aplikacích (medicína apod.), kdy je potřeba zobrazovat objemová data. Větší podporu tohoto typu textur můžeme očekávat až v dalších generacích grafických akcelerátorů, protože pro uložení dat prostorových textur je zapotřebí velkého množství paměti, která současně musí být dostatečně rychlá pro čtení.

Největší podpora ze strany grafických akcelerátorů je tedy u dvoudimenzi­onálních textur, které také splňují značnou část nároků programátora počítačové grafiky. Především tímto typem textur se budeme zabývat v dalším textu.

V OpenGL lze také zvolit různé filtrace textur, režimy mapování textur na plošky, multitexturování a další grafické efekty, které si dále probereme. Musíme si však uvědomit, že pokud použijeme některý grafický efekt, který není grafickým akcelerátorem podporován, dojde k výpočtům pomocí hlavního procesoru počítače, což značně zpomaluje celý systém. Proto je nejprve vhodné zjistit, které efekty jsou na grafickém akcelerátoru podporovány, a případně dát uživateli naší aplikace na výběr mezi kvalitou a rychlostí zobrazení.

Omezení texturování v OpenGL

V OpenGL jsme při použití rastrových textur omezeni poměrně málo. První omezení spočívá ve faktu, že texturování je podporováno pouze pro true-color barevné režimy, v indexových režimech (s barevnou paletou) není povoleno. Toto omezení však dnes již téměř nemá význam, protože paletové režimy se používají minimálně a velká část moderních grafických akcelerátorů je ani nemá implementovány.

Další omezení spočívá v maximální povolené velikosti textury a rozlišeních textury. Všechny textury musí mít v OpenGL rozměry, které jsou mocninou čísla 2, například 32×32 pixelů. Rozlišení 31×33 není povoleno. Maximální velikost textury závisí na mnoha faktorech, především na velikosti paměti grafického akcelerátoru. Všechny implementace OpenGL by však měly podporovat textury alespoň do velikosti 64×64 pixelů. Maximální podporovanou velikost textur lze zjistit pomocí příkazů OpenGL.

Pokračování

V dalším pokračování si popíšeme základní postup, který se při texturování musí dodržet.

Demonstrační příklady

Dnes ješte nemáme dostatečné informace o postupu, jakým lze texturování pomocí OpenGL provést, proto si podrobnější příklady necháme až na další pokračování. Ukážeme si pouze jednoduchý prográmek, který zjistí maximální povolenou velikost textury. Zdrojový kód tohoto programu je k dispozici zde, kód s obarvenou syntaxí zde. Po překladu a spuštění se do okna programu vypíšou požadované informace, program skončí stiskem klávesy Escape.

UX DAy - tip 2

Na grafické kartě nVidia RIVA TNT2 Model 64 se mi hlásí maximální velikost textury 1024×1024. Zajímavé by bylo zjistit od vás, čtenářů, jaká rozlišení jsou na vašich kartách podporována. Mnohým by to mohlo pomoci při vývoji přenositelných aplikací. Dnes považuji za minimum alespoň 256×256.

Pro majitele pomalejšího připojení k internetu je zde k dispozici celý článek i s přílohami zabalený do jednoho zip souboru.

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