Hlavní navigace

OpenGL Imaging Subset

2. 3. 2004
Doba čtení: 8 minut

Sdílet

V předchozí sérii článků o grafické knihovně OpenGL jsme se zaměřili především na možnosti vykreslování trojrozměrné grafiky. Na tyto články navážeme pokračováním, ve kterém si popíšeme rozšiřující operace, které lze provádět nad právě vykreslovanými fragmenty a nad pixely uloženými ve framebufferu.

Jedná se veskrze o rastrové operace, jejichž význam tkví především ve velmi rychlém zpracování obrazu bez nutnosti přístupu do operační paměti počítače – všechny operace tedy probíhají v ideálním případě přímo na grafickém akcelerátoru bez zbytečných datových přenosů po sběrnici.

Obsah

Význam termínu Imaging Subset
Kompatibilita a dostupnost imaging subsetu
Skupiny funkcí, které jsou v imaging subsetu definovány
Funkce provádějící blokový přenos pixelů
Typy pixelů, které se mohou ukládat do framebufferu nebo textury
Demonstrační příklad
Pokračování
Seznam funkcí zmíněných v této části
Nové funkce popsané v této části
Zkomprimovaná ver­ze

Význam termínu Imaging Subset

Termínem Imaging subset označujeme sadu funkcí, pomocí nichž je možné ovlivnit operace, které se provádějí nad vykreslovanými fragmenty i nad pixely uloženými ve framebufferu.

Zejména jsou ovlivněny operace pro vykreslování a kopii rastrových obrazů (tj. funkce glDrawPixels(), glReadPixels() aglCopyPixels()), ale také operace pro specifikaci rastrové předlohy textury, tj. funkce glTexImage1D() a glTexImage2D(). Bližší informace o těchto operacích byly uvedeny v úvodním seriálu o grafické knihovně OpenGL, seznam jednotlivých dílů tohoto seriálu hledejte v jeho XXXIII části.

Na rastrové obrazy lze během přenosu aplikovat téměř libovolně zadané konvoluční filtry, měnit význam hodnot jednotlivých pixelů a v neposlední řadě také měnit barvy pixelů podle předem zadaných tabulek. Tyto operace se samozřejmě dají jednoduše naprogramovat, ale s tím rozdílem, že implementace konvolučních filtrů a tabulek na grafickém akcelerátoru je několikanásobně rychlejší než programová implementace (projevuje se zde především organizace cache pamětí).

Mezi další operace, které jsou v imaging subsetu podporovány, patří funkce, které zjišťují statistické informace o obrazu. V rastrovém obrazu se dají hledat minima a maxima, mezi nejužitečnější funkce při zpracování obrazu však patří vytvoření histogramu, který jistě znáte z mnoha programů pro práci s obrazem (jmenujme například oblíbený The Gimp).

Kompatibilita a dostupnost imaging subsetu

Valná část dále popsaných funkcí aplikovaných na pixely a fragmenty je dostupná pouze v některých implementacích OpenGL. Tyto implementace musí imaging subset plně podporovat. Výhodné pro nás jistě je, že pokud některá implementace OpenGL deklaruje, že podporuje imaging subset, musí být aplikovány všechny dále popsané funkce, takže programátor nemusí složitě zjišťovat, které funkce z imaging subsetu jsou a které nejsou podporovány.

Pokud není imaging subset (dále budeme používat pouze zkratku IS) podporován, generuje volání těchto funkcí chybu GL_INVALID_OPE­RATION, použití nových symbolických konstant vrátí zase chybu GL_INVALID_ENUM. Pro test dostupnosti IS existuje jednoduchý postup: na začátku aplikace (ale samozřejmě až po inicializaci OpenGL a rendering kontextu OpenGL) se zavolá nějaká funkce z IS a otestuje se návratová (chybová) hodnota pomocí funkce:

GLenum glGetError(
    void
);

Pokud tato funkce vrátí hodnotu GL_NO_ERROR, je imaging subset podporován, pokud však vrátí hodnotu GL_INVALID_OPE­RATION nebo GL_INVALID_ENUM, znamená to, že funkce IS nejsou grafickým akcelerátorem ani programovou implementací OpenGL podporovány.

V případě, že IS není podporován, tak se barvy vykreslovaných a kopírovaných pixelů či fragmentů dále nijak neupravují, systém se tedy chová tak, jako by se žádná funkce z IS nevolala. Ve většině případů je tak alespoň částečně zachována funkčnost aplikace, i když se samozřejmě vykreslované obrázky mohou značně odlišovat od zamýšleného výsledku.

Dalším způsobem, jakým lze získat informace o dostupnosti IS, je kontrola, zda je definována symbolická konstanta GL_ARB_imaging. Prakticky na všech platformách lze tuto konstantu najít v souboru gl.h nebo glext.h, známou výjimkou jsou zastaralé hlavičkové soubory dodávané s vývojovým prostředím Visual Studio 6.0.

Korektní (tj. maximálně přenositelný) je však test až v době běhu aplikace. Lze totiž zavolat funkci:

const GLubyte * glGetString(
    GLenum name
);

které se jako parametr předá konstanta GL_EXTENSIONS. Funkce následně vrátí ukazatel na řetězec, v němž jsou mezerou oddělena všechna podporovaná rozšíření. V tomto řetězci stačí najít slovoGL_ARB_i­maging. Bližší informace o rozšíření (extensions) OpenGL budou uvedeny v navazujících seriálech a také v dnešním demonstračním příkladu.

Při použití funkcí z imaging subsetu musí překladač samozřejmě znát hlavičky všech jeho funkcí. Ty mohou být uvedeny v souboru gl.h, což však odporuje normě, která velmi rozumně předepisuje, že v tomto souboru mají být uvedeny pouze maximálně přenositelné funkce a jim odpovídající datové typy a symbolické konstanty.

Proto se, zejména v nových distribucích, používá další hlavičkový soubor glext.h. Firma SGI vydala svůj hlavičkový soubor s některými rozšířeními, který si můžete stáhnout na adrese http://oss.sgi­.com/projects/o­gl-sample/ABI/glex­t.h, neznám však přesné podmínky pro jeho šíření v rámci open source produktů. Tento soubor má být, stejně jako hlavičkový soubor gl.h, umístěn v podadresáři gl nebo GL v include adresáři.

Další podmínkou pro zdárný překlad je dostupnost knihovny pro linkování a taktéž dostupnost dynamické knihovny pro spuštění aplikace využívající IS. Pokud se při překladu hlásí chyba, že funkce není definována, není dostupný hlavičkový soubor, pokud se chyba nahlásí při linkování, je pravděpodobně zastaralá statická knihovna.

Multiplatformní knihovna MESA (viz též její oficiální stránky http://mesa3d­.org/) by již v novějších verzích měla imaging subset podporovat, u Microsoft Windows se situace liší podle ovladačů grafické karty. Původní verze knihovny OpenGL dodávaná firmou Microsoft s operačním systémem (soubor opengl32.dll) imaging subset nepodporovala, ovladače grafických karet si však instalují vlastní knihovnu OpenGL.

V těchto operačních systémech tak ve skutečnosti existuje více současně nainstalovaných dynamických knihoven OpenGL. V každém systému (od Windows 95B) by měla být nainstalována originální knihovna od Microsoftu s tím, že každý ovladač si doplní i svoji vylepšenou verzi. Majitelé karet s čipy nVidia hledejte například soubor nvopengl32.dll, který by měl být umístěn ve stejném adresáři jako opengl32.dll.

Skupiny funkcí, které jsou v imaging subsetu definovány

Imaging subset obsahuje poměrně velké množství funkcí, které jsou určeny pro zpracování rastrových obrazů. Tyto funkce si můžeme rozdělit do několika kategorií:

  1. Práce s barevnou paletou, tj. vytvoření a následná změna barevné palety nebo její části.
  2. Vytváření a aplikace konvolučních filtrů, tj. filtrů, které jsou na rastrový obraz aplikovány v „časové“ rovině.
  3. Práce s barvovými maticemi, specifikace transformace barev.
  4. Statistické funkce prováděné nad rastrovým obrazem včetně histogramů.
  5. Pokročilý blending dvou rastrových obrazů.

Jednotlivé kategorie funkcí si budeme postupně popisovat v navazujících částech.

Funkce provádějící blokový přenos pixelů

Pro provedení blokového přenosu pixelů mezi jednotlivými buffery, popř. mezi vybraným bufferem a operační pamětí počítače, se používají následující funkce.

Přenos pixelů z operační paměti do vybraného bufferu:

void glDrawPixels(
    GLsizei width,
    GLsizei height,
    GLenum format,
    GLenum type,
    const GLvoid *pixels
);

Přenos pixelů z vybraného bufferu do operační paměti počítače:

void glReadPixels(
    GLint x,
    GLint y,
    GLsizei width,
    GLsizei height,
    GLenum format,
    GLenum type,
    GLvoid *pixels
);

Přenos pixelů mezi dvěma buffery, částmi dvou bufferů nebo částmi pouze jednoho bufferu:

void glCopyPixels(
    GLint x,
    GLint y,
    GLsizei width,
    GLsizei height,
    GLenum type
);

Tyto funkce jsme si již popsali ve čtvrtém a pátém dílu úvodního seriálu o grafické knihovně OpenGL.

Poznámka: funkce glReadPixels(), glDrawPixels() aglCopyPixels() provádějí blokový přenos rastrových dat, proto se v literatuře označují názvem Bit Block Transfer, zkráceně také BitBlt nebo Blit. Operace typu BitBlt jsou při správném použití velmi efektivní, zejména při optimálním naprogramování, a používají se například při tvorbě celého grafického uživatelského rozhraní v jazyce SmallTalk – viz podrobnější článek ze seriálu, který v této době vychází taktéž na Rootu.

Mezi další funkce, které provádějí blokový přenos rastrových dat, patří i příkazy pro načtení rastrových obrazů textur:

void glTexImage1D(
    GLenum target,
    GLint level,
    GLint components,
    GLsizei width,
    GLint border,
    GLenum format,
    GLenum type,
    const GLvoid *pixels
);

a

void glTexImage2D(
    GLenum target,
    GLint level,
    GLint components,
    GLsizei width,
    GLsizei height,
    GLint border,
    GLenum format,
    GLenum type,
    const GLvoid *pixels
);

Tyto dvě funkce jsme si podrobněji popsali v dvacátém čtvrtém dílu seriálu o OpenGL.

Na prvním obrázku si připomeneme, které moduly jsou ve vykreslovacím řetězci použity v případě práce s rastrovými daty. Na tomto obrázku je také zvýrazněn modul, ve kterém je implementována velká část funkcí poskytovaných image subsetem.

Část vykreslovacího řetězce určená pro práci s rastrovými daty
Obrázek 1: Část vykreslovacího řetězce určená pro práci s rastrovými daty

Typy pixelů, které se mohou ukládat do framebufferu nebo textury

Operace pro změnu hodnot přenášených pixelů pracují obecně se čtyřmi typy pixelů: barvou v režimu true-color, indexem do tabulky barev (paletový režim práce s barvou), hloubkou (tj. hodnotou ukládanou do paměti hloubky) a stencil hodnotou (tj. hodnotou ukládanou do paměti šablony – stencil bufferu).

Pixel, který nese informace o barvě v true-color (RGBA) režimu, je popsán čtyřmi hodnotami reprezentovanými v systému pohyblivé desetinné tečky (floating point) v takovém rozsahu, že 0.0 znamená nulovou intenzitu barvové složky a 1.0 plné vysvícení této barvové složky.

Pixel, jenž nese informace o barvě v paletovém režimu, je reprezentován hodnotou v systému pevné řádové čárky, normou však není specifikována přesnost této reprezentace. Některé implementace indexy před prováděním dalších operací zaokrouhlují nebo usekávají na celá čísla typu GLint, což je však chyba. Podobnou vnitřní reprezentaci mají i pixely, které nesou informaci o šabloně.

Pixel s informací o hloubce je reprezentován hodnotou v systému pohyblivé řádové čárky. Hodnoty pixelů jsou upraveny tak, aby hodnota 0.0 značila minimální hloubku a 1.0 maximální hloubku pixelu, kterou lze do paměti hloubky zaznamenat.

Demonstrační příklad

V této úvodní části si ukážeme pouze jeden demonstrační příklad. V tomto příkladu se po inicializaci OpenGL a následném renderingu kontextu OpenGL provede dotaz na podporovaná rozšíření. Tato rozšíření (či alespoň jejich část) se vypíší do vytvořeného okna a současně na konzoli. Všimněte si způsobu, jakým jsou jednotlivá rozšíření v řetězci uložena a jaký je použit oddělovač.

Zdrojový kód prvního demonstračního příkladu je dostupný zde, HTML verze se zvýrazněním syntaxe zde.

Screenshot příkladu spuštěného na grafické kartě GeForce2 MX 100/200
Obrázek 2: Screenshot příkladu spuštěného na systému s grafickou kartou GeForce2 MX 100/200

Screenshot příkladu spuštěného na grafické kartě bez akcelerace
Obrázek 3: Screenshot příkladu spuštěného na systému bez grafického akcelerátoru

CS24 tip temata

Pokračování

V dalším pokračování si popíšeme způsoby, jimiž lze nastavit změnu hodnot pixelů během přenosu rastrových dat. Zejména se zaměříme na look-up tables.

Seznam funkcí zmíněných v této části

glDrawPixels()
glReadPixels()
glCopyPixels()
glTexImage1D()
glTexImage2D()
 

Nové funkce popsané v této části

glGetError()
glGetString()

Zkomprimovaná verze článku

Zkomprimovaná verze tohoto článku je umístěna zde.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.