V sedmé části seriálu o multimediální knihovně Pyglet si na několika demonstračních příkladech ukážeme, jakým způsobem je možné na zobrazovaná 2D i 3D tělesa nanášet rastrové textury. K tomu využijeme kombinaci funkcí nabízených přímo knihovnou Pyglet a nepřímo grafickou knihovnou OpenGL.

Obsah

1. Použití textur v knihovně Pyglet

2. Textury reprezentované rastrovými obrázky

3. Kdy a proč používat texturování?

4. Podpora pro práci s texturami v knihovnách OpenGL a Pyglet

5. Postup při použití textur ve skriptech používajících knihovnu Pyglet

6. Načtení textury z externího souboru

7. Nastavení parametrů textur a povolení texturování

8. Specifikace souřadnic v prostoru textury

9. První demonstrační příklad: převod rastrového obrázku na texturu

10. Nastavování parametrů textur

11. Druhý demonstrační příklad: nastavení způsobu opakování textur

12. Třetí demonstrační příklad: zrcadlení textur při opakování vzorku

13. Chování texturovacího engine při přibližování a vzdalování textur

14. Čtvrtý demonstrační příklad: výběr nejbližšího texelu

15. Pátý demonstrační příklad: použití bilineárního filtru

16. Repositář s demonstračními příklady

17. Odkazy na Internetu

1. Použití textur v knihovně Pyglet

Texturováním (resp. přesněji řečeno nanášením či promítáním textur) se označuje princip obarvení povrchu zobrazovaných (většinou trojrozměrný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 textur (barevné přechody) 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 knihovna OpenGL a tím pádem nepřímo ani multimediální knihovna Pyglet 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 (mramor, marble).

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

2. Textury reprezentované rastrovými obrázky

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 dvoudimenzionálních texturách (tedy bitmapách a či v řeči knihovny OpenGL 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ů, k čemuž použijeme již známé funkce nabízené knihovnou Pyglet.

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

Podobně, jako je bitmapa či pixmapa složena ze základních rastrových elementů (pixelů), je textura složena z takzvaných 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.

3. Kdy a proč používat texturování?

Texturu je možné 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 zpracovávaný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 takzvaným 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. 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).

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 nebo vhodně natočený obdélník pokrytý texturou (jak již víme, Pyglet pro ně má přímou podporu). Texturování může být v tomto případě výhodnější, protože se (na rozdí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.

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

4. Podpora pro práci s texturami v knihovnách OpenGL a Pyglet

V knihovně OpenGL a tím pádem i v knihovně Pyglet jsou podporovány pouze rastrové textury, které mohou být buď jednodimenzionální, dvoudimenzionální, nebo třídimenzionální. V případě jednodimenzioná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ě ovladače/driveru, který musí textury přenášet přes port do grafického akcelerátoru).

Třídimenzionální textury (tj. objemové či takzvané 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 dvoudimenzioná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 (dnes je to spíše neobvyklá situace). 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í.

Knihovna Pyglet taktéž podporuje práci s texturami, protože umožňuje načíst rastrový obrázek (to již známe) a převést ho na objekt reprezentující dvourozměrnou texturu. Právě tuto funkcionalitu využijeme v demonstračních příkladech.

5. Postup při použití textur ve skriptech používajících knihovnu Pyglet

Vykreslování objektů s nanesenou texturou vyžaduje provést několik vzájemně provázaných kroků:

  1. Vytvoření rastrové předlohy textury nebo její načtení ze souboru. K tomu lze využít funkce nabízené knihovnou Pyglet.
  2. Vytvoření nového texturovacího objektu, přiřazení textury tomuto objektu a nastavení formátu textury. Poslední část není nutno provést explicitně, to za nás opět zajistí knihovna Pyglet.
  3. Nastavení způsobu nanášení textury na vykreslované povrchy.
  4. Zapnutí (povolení) nanášení textur.
  5. Vykreslení scény se zadanými texturovacími souřadnicemi pro každý vrchol.

6. Načtení textury z externího souboru

Načtení textury z externího souboru je ve skutečnosti velmi snadné, protože můžeme využít nám již známé funkce poskytované knihovnou Pyglet. Nejprve otevřeme soubor s rastrovým obrázkem, dále obrázek načteme a nakonec – což je nový krok – s využitím metody get_texture() získáme z rastrového obrázku kýženou texturu:

import pyglet

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()

Poznámka: v posledním kroku může docházet k nuceným konverzím bitmapy, proto tuto operaci proveďte pouze jedenkrát při inicializaci aplikace, nikoli uvnitř event loopu.

7. Nastavení parametrů textur a povolení texturování

V knihovně OpenGL a nepřímo tedy i v Pygletu je možné parametry textur, tj. například způsob, jakým je textura mapována na povrch vykreslovaného tělesa, nastavit voláním funkce:

void glTexParameterf(
    GLenum  target,
    GLenum  pname,
    GLfloat value
);

popř. funkce:

void glTexParameteri(
    GLenum target,
    GLenum pname,
    GLint  value
);

V případě volání funkcí Pygletu z programovacího jazyka Python se samozřejmě nemusíme zabývat explicitní specifikací typu parametrů, takže obě výše zmíněné funkce vypadají zhruba následovně:

def glTexParameterf(
    target,
    pname,
    value)
def glTexParameteri(
    target,
    pname,
    value)

První parametr (nazvaný target) může podle zvolené dimenze textury nabývat hodnot GL_TEXTURE_1D (jednodimenzionální textura, tj. barevný přechod, nepoužijeme), GL_TEXTURE_2D (nejběžnější, rastrová textura) nebo GL_TEXTURE_3D (objemová textura, taktéž nepoužijeme). Hodnoty dalších dvou parametrů nazvaných pname a value jsou uvedeny v následující tabulce:

Parametr Význam
GL_TEXTURE_WRAP_S tímto parametrem specifikujeme, zda se má při překročení rozsahu texturovací souřadnice ve směru osy s provést opakování motivu na textuře nebo „protažení“ první či poslední hodnoty. Opakování motivu na textuře je vhodné použít v případech, kdy zobrazujeme různé na sebe navazující motivy, například cihlové zdi, podlahy apod. Protažením textury lze v některých případech zamezit vizuálním artefaktům, které by se mohly objevit při napojování textur. Překročení rozsahu tedy není v žádném případě chybou.
GL_TEXTURE_WRAP_T tento parametr má podobný význam jako parametr předchozí s tím rozdílem, že se místo na souřadnici ve směru osy s vztahuje na souřadnici ve směru osy t. U 1D textur nemá hodnota tohoto parametru vliv na zobrazení textury, použitelný je pouze u 2D a 3D textur.
GL_TEXTURE_WRAP_R tento parametr má opět podobný význam jako předchozí dva parametry, ale vztahuje se na třetí souřadnici r, která je použita například u objemových textur. U jednorozměrných ani u dvourozměrných textur nemá tento parametr žádný význam.
GL_TEXTURE_MIN_FILTER tímto parametrem je možné zvolit filtr použitý při zmenšování textury, tj. tehdy, jestliže na plochu jednoho vykreslovaného pixelu musíme použít barvy několika sousedních texelů.
GL_TEXTURE_MAX_FILTER tímto parametrem se volí filtr použitý při zvětšování textury, tj. v případě, že vykreslovaný pixel obsahuje pouze malou plochu texelu.

V další tabulce jsou uvedeny vztahy mezi parametry pname a value:

Jméno parametru Hodnota Význam
GL_TEXTURE_WRAP_S GL_REPEAT opakování textury ve směru osy s
GL_TEXTURE_WRAP_S GL_CLAMP protažení textury ve směru osy s
GL_TEXTURE_WRAP_T GL_REPEAT opakování textury ve směru osy t
GL_TEXTURE_WRAP_T GL_CLAMP protažení textury ve směru osy t
GL_TEXTURE_WRAP_R GL_REPEAT opakování textury ve směru osy r
GL_TEXTURE_WRAP_R GL_CLAMP protažení textury ve směru osy r
GL_TEXTURE_MIN_FILTER GL_NEAREST nejjednodušší a nejrychlejší filtr, u tohoto filtru se barva vykreslovaného pixelu vypočte z barvy texelu, jehož souřadnice nejpřesněji odpovídají souřednicím zadaným do textury
GL_TEXTURE_MIN_FILTER GL_LINEAR sofistikovanější filtr, kdy se barva vykreslovaného pixelu spočítá pomocí bilineární interpolace z barev sousedních texelů
GL_TEXTURE_MIN_FILTER GL_NEAREST_MIPMAP_NEAREST sofistikovanější filtr, použití takzvaných mipmap
GL_TEXTURE_MIN_FILTER GL_LINEAR_MIPMAP_NEAREST sofistikovanější filtr, použití takzvaných mipmap
GL_TEXTURE_MIN_FILTER GL_NEAREST_MIPMAP_LINEAR sofistikovanější filtr, použití takzvaných mipmap
GL_TEXTURE_MAX_FILTER GL_NEAREST nejrychlejší způsob, použití nejbližšího texelu
GL_TEXTURE_MAX_FILTER GL_LINEAR použití lineárního či bilineárního filtru

V prvním demonstračním příkladu, který bude uveden v deváté kapitole, použijeme následující nastavení parametrů textury. Volání první funkce určuje způsob uložení (a dekódování) texelu v textuře:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
glBindTexture(GL_TEXTURE_2D, texture.id)

Poslední krok je jednoduchý – pouze potřebujeme povolit texturování, tj. přepnout grafický akcelerátor do režimu, v němž texturovací jednotka grafického akcelerátoru pokrývá povrch těles jednotlivými texely:

glEnable(GL_TEXTURE_2D)

8. Specifikace souřadnic v prostoru textury

Vykreslení tělesa s otexturovanými stěnami probíhá podobně jako u barevných stěn. Jedinou změnou je, že pro každý vrchol musíme k jeho 2D/3D/4D souřadnicím navíc specifikovat souřadnice v textuře. Každá textura má nezávisle na své velikosti souřadnice v rozsahu od 0.0 do 1.0, což znamená, že 2D textura je chápána jako čtverec o straně délky 1. Souřadnice do textury však můžeme zadávat libovolně, protože díky opakování (pokud je zapnuto) se obrázek textury může na zobrazované stěně šachovnicově skládat, podobně jako například obrázky na pozadí HTML stránek. Otexturovaný čtyřúhelník lze specifikovat takto:

glBegin(GL_QUADS) 

glTexCoord2f(0.0, 0.0);
glVertex2i(x, y) 

glTexCoord2f(1.0, 0.0);
glVertex2i(x+150, y) 

glTexCoord2f(1.0, 1.0);
glVertex2i(x+150, y+150) 

glTexCoord2f(0.0, 1.0);
glVertex2i(x, y+150) 

glEnd() 

9. První demonstrační příklad: převod rastrového obrázku na texturu

V prvním demonstračním příkladu se provádí následující činnosti:

  1. Načte se rastrový obrázek s texturou.
  2. Z obrázku se získá textura.
  3. Nastaví se parametry textury.
  4. Povolí se texturování a vykreslí se čtyřúhelník s nanesenou texturou.
  5. Texturování se zakáže a vykreslí se jednobarevný čtyřúhelník.
  6. Opět se povolí texturování a vykreslí se čtyřúhelník s nanesenou texturou.

Načtení rastrového obrázku a získání textury:

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()

Nastavení parametrů textury a navázání na konkrétní obrázek:

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, texture.id)

Vykreslení otexturovaného čtyřúhelníku, čtyřúhelníku s jednobarevným povrchem a dalšího otexturovaného čtyřúhelníku:

glEnable(GL_TEXTURE_2D)
draw_quad(50, 50) 

glDisable(GL_TEXTURE_2D)
draw_quad(210, 50) 

glEnable(GL_TEXTURE_2D)
draw_quad(410, 50) 

Samotná funkce pro vykreslení čtyřúhelníku vypadá takto:

def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.0, 0.0);
    glVertex2i(x, y) 

    glTexCoord2f(1.0, 0.0);
    glVertex2i(x+150, y) 

    glTexCoord2f(1.0, 1.0);
    glVertex2i(x+150, y+150) 

    glTexCoord2f(0.0, 1.0);
    glVertex2i(x, y+150) 

    glEnd() 

Úplný zdrojový kód dnešního prvního demonstračního příkladu vypadá následovně:

#!/usr/bin/env python

import pyglet
from pyglet.gl import *

window = pyglet.window.Window(width=600,
                              height=250,
                              caption="Pyglet library")

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()



def init():
    glClearColor(0.0, 0.0, 0.3, 0.0)          # barva pozadi obrazku
    glPolygonMode(GL_FRONT, GL_FILL)          # nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL)
    glDisable(GL_CULL_FACE)                   # zadne hrany ani steny se nebudou odstranovat

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, texture.id)



@window.event
def on_resize(width, height):
    init()
    glViewport(0, 0, width, height)           # viditelna oblast pres cele okno



def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.0, 0.0);
    glVertex2i(x, y) 

    glTexCoord2f(1.0, 0.0);
    glVertex2i(x+150, y) 

    glTexCoord2f(1.0, 1.0);
    glVertex2i(x+150, y+150) 

    glTexCoord2f(0.0, 1.0);
    glVertex2i(x, y+150) 

    glEnd() 


@window.event
def on_draw():
    glClear(GL_COLOR_BUFFER_BIT)              # vymazani vsech bitovych rovin barvoveho bufferu
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)            # nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT)                # vymazani bitovych rovin barvoveho bufferu

    glEnable(GL_TEXTURE_2D)
    draw_quad(50, 50) 

    glDisable(GL_TEXTURE_2D)
    draw_quad(210, 50) 

    glEnable(GL_TEXTURE_2D)
    draw_quad(410, 50) 

pyglet.app.run()

Obrázek 5: Screenshot dnešního prvního demonstračního příkladu.

10. Nastavování parametrů textur

V následujících čtyřech demonstračních příkladech si ukážeme vliv parametrů textur na způsob jejich vykreslování. Obrázek u textur budeme opakovat, zrcadlit a textury budeme zvětšovat s využitím různých typů filtrů (bilineárního).

Obrázek 6: Textury nanesené na trojrozměrné těleso. Přes samotnou texturu prosvítá barva stěn.

11. Druhý demonstrační příklad: nastavení způsobu opakování textur

Ve druhém příkladu je nastaveno opakování obrázku s texturou:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)

To se projeví ve chvíli, kdy použijeme texturovací souřadnice mimo rozsah 0..1 (což je zcela legální). Tento kód například požaduje, aby se textura na stěně čtverce opakovala v šachovnici 2×2:

def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.0, 0.0);
    glVertex2i(x, y) 

    glTexCoord2f(2.0, 0.0);
    glVertex2i(x+400, y) 

    glTexCoord2f(2.0, 2.0);
    glVertex2i(x+400, y+400) 

    glTexCoord2f(0.0, 2.0);
    glVertex2i(x, y+400) 

    glEnd() 

Obrázek 7: Screenshot dnešního druhého demonstračního příkladu. Povšimněte si, že se obrázek zeměkoule skutečně opakuje.

Úplný zdrojový kód tohoto demonstračního příkladu:

#!/usr/bin/env python

import pyglet
from pyglet.gl import *

window = pyglet.window.Window(width=500,
                              height=500,
                              caption="Pyglet library")

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()



def init():
    glClearColor(0.0, 0.0, 0.3, 0.0)          # barva pozadi obrazku
    glPolygonMode(GL_FRONT, GL_FILL)          # nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL)
    glDisable(GL_CULL_FACE)                   # zadne hrany ani steny se nebudou odstranovat

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, texture.id)



@window.event
def on_resize(width, height):
    init()
    glViewport(0, 0, width, height)           # viditelna oblast pres cele okno



def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.0, 0.0);
    glVertex2i(x, y) 

    glTexCoord2f(2.0, 0.0);
    glVertex2i(x+400, y) 

    glTexCoord2f(2.0, 2.0);
    glVertex2i(x+400, y+400) 

    glTexCoord2f(0.0, 2.0);
    glVertex2i(x, y+400) 

    glEnd() 


@window.event
def on_draw():
    glClear(GL_COLOR_BUFFER_BIT)              # vymazani vsech bitovych rovin barvoveho bufferu
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)            # nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT)                # vymazani bitovych rovin barvoveho bufferu

    glEnable(GL_TEXTURE_2D)
    draw_quad(50, 50) 

pyglet.app.run()

12. Třetí demonstrační příklad: zrcadlení textur při opakování vzorku

Zajímavé je zrcadlení textury, což se může hodit ve chvíli, kdy potřebujeme, aby na sebe vzorky textury plynule navazovaly. To je při zrcadlení samozřejmě vždy splněno:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)

Obrázek 8: Screenshot dnešního třetího demonstračního příkladu. Povšimněte si, že se obrázek zeměkoule opakuje, ovšem současně je zrcadlen.

Úplný zdrojový kód tohoto demonstračního příkladu:

#!/usr/bin/env python

import pyglet
from pyglet.gl import *

window = pyglet.window.Window(width=500,
                              height=500,
                              caption="Pyglet library")

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()



def init():
    glClearColor(0.0, 0.0, 0.3, 0.0)          # barva pozadi obrazku
    glPolygonMode(GL_FRONT, GL_FILL)          # nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL)
    glDisable(GL_CULL_FACE)                   # zadne hrany ani steny se nebudou odstranovat

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, texture.id)



@window.event
def on_resize(width, height):
    init()
    glViewport(0, 0, width, height)           # viditelna oblast pres cele okno



def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.0, 0.0);
    glVertex2i(x, y) 

    glTexCoord2f(2.0, 0.0);
    glVertex2i(x+400, y) 

    glTexCoord2f(2.0, 2.0);
    glVertex2i(x+400, y+400) 

    glTexCoord2f(0.0, 2.0);
    glVertex2i(x, y+400) 

    glEnd() 


@window.event
def on_draw():
    glClear(GL_COLOR_BUFFER_BIT)              # vymazani vsech bitovych rovin barvoveho bufferu
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)            # nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT)                # vymazani bitovych rovin barvoveho bufferu

    glEnable(GL_TEXTURE_2D)
    draw_quad(50, 50) 

pyglet.app.run()

13. Chování texturovacího engine při přibližování a vzdalování textur

Ve chvíli, kdy se kamera (pozorovatel) přibližuje k textuře, je nutné nějakým způsobem zajistit zvětšení texelů. To lze provést buď triviálně a velmi rychle namapováním texelů na povrch tělesa na základě nejbližšího texelu nebo sofistikovaněji s využitím takzvaného bilineárního filtru. Jak již víme z předchozího textu, je možné toto nastavení provést přes funkci glTexParameteri(), kde jméno parametru je GL_TEXTURE_MAX_FILTER a možné hodnoty jsou GL_NEAREST (použije se nejbližší texel) nebo GL_LINEAR (použije se lineární interpolace mezi barvami sousedních texelů). Rozdíly v obou typech filtrů uvidíme na dvojici demonstračních příkladů:

Obrázek 9: Screenshot dnešního čtvrtého demonstračního příkladu při přiblížení textury.

Obrázek 10: Screenshot dnešního pátého demonstračního příkladu při přiblížení textury a použití takzvaného bilineárního filtru.

14. Čtvrtý demonstrační příklad: výběr nejbližšího texelu

Ve čtvrtém demonstračním příkladu se při přiblížení textury mapují na stěnu tělesa vždy nejbližší texely, takže vznikají charakteristické „kostičky“:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)

Úplný zdrojový kód tohoto demonstračního příkladu:

#!/usr/bin/env python

import pyglet
from pyglet.gl import *

window = pyglet.window.Window(width=500,
                              height=500,
                              caption="Pyglet library")

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()



def init():
    glClearColor(0.0, 0.0, 0.3, 0.0)          # barva pozadi obrazku
    glPolygonMode(GL_FRONT, GL_FILL)          # nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL)
    glDisable(GL_CULL_FACE)                   # zadne hrany ani steny se nebudou odstranovat

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)
    glBindTexture(GL_TEXTURE_2D, texture.id)



@window.event
def on_resize(width, height):
    init()
    glViewport(0, 0, width, height)           # viditelna oblast pres cele okno



def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.4, 0.4);
    glVertex2i(x, y) 

    glTexCoord2f(0.6, 0.4);
    glVertex2i(x+400, y) 

    glTexCoord2f(0.6, 0.6);
    glVertex2i(x+400, y+400) 

    glTexCoord2f(0.4, 0.6);
    glVertex2i(x, y+400) 

    glEnd() 


@window.event
def on_draw():
    glClear(GL_COLOR_BUFFER_BIT)              # vymazani vsech bitovych rovin barvoveho bufferu
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)            # nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT)                # vymazani bitovych rovin barvoveho bufferu

    glEnable(GL_TEXTURE_2D)
    draw_quad(50, 50) 

pyglet.app.run()

15. Pátý demonstrační příklad: použití bilineárního filtru

V příkladu pátém se naproti tomu používá bilineární filtr, který vede k vizuálně lepším výsledkům, ovšem výpočet celé scény je pomalejší:

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)

Úplný zdrojový kód tohoto demonstračního příkladu:

#!/usr/bin/env python

import pyglet
from pyglet.gl import *

window = pyglet.window.Window(width=500,
                              height=500,
                              caption="Pyglet library")

image_stream = open("gnome-globe.png", "rb")
image = pyglet.image.load('gnome-globe.png', file=image_stream)
texture = image.get_texture()



def init():
    glClearColor(0.0, 0.0, 0.3, 0.0)          # barva pozadi obrazku
    glPolygonMode(GL_FRONT, GL_FILL)          # nastaveni rezimu vykresleni modelu
    glPolygonMode(GL_BACK, GL_FILL)
    glDisable(GL_CULL_FACE)                   # zadne hrany ani steny se nebudou odstranovat

    glPixelStorei(GL_UNPACK_ALIGNMENT, 1)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
    glBindTexture(GL_TEXTURE_2D, texture.id)



@window.event
def on_resize(width, height):
    init()
    glViewport(0, 0, width, height)           # viditelna oblast pres cele okno



def draw_quad(x, y):
    glBegin(GL_QUADS) 

    glTexCoord2f(0.4, 0.4);
    glVertex2i(x, y) 

    glTexCoord2f(0.6, 0.4);
    glVertex2i(x+400, y) 

    glTexCoord2f(0.6, 0.6);
    glVertex2i(x+400, y+400) 

    glTexCoord2f(0.4, 0.6);
    glVertex2i(x, y+400) 

    glEnd() 


@window.event
def on_draw():
    glClear(GL_COLOR_BUFFER_BIT)              # vymazani vsech bitovych rovin barvoveho bufferu
    glLoadIdentity()
    glClearColor(0.0, 0.0, 0.0, 0.0)            # nastaveni mazaci barvy na cernou
    glClear(GL_COLOR_BUFFER_BIT)                # vymazani bitovych rovin barvoveho bufferu

    glEnable(GL_TEXTURE_2D)
    draw_quad(50, 50) 

pyglet.app.run()

16. Repositář s demonstračními příklady

Všechny dnes popsané demonstrační příklady byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/presentations. Příklady si můžete v případě potřeby stáhnout i jednotlivě bez nutnosti klonovat celý repositář. Pro jejich spuštění je nutné mít nainstalovanou jak knihovnu Pyglet, tak i podpůrné grafické knihovny OpenGL a GLU (což se většinou provede automaticky v rámci instalace balíčku s Pygletem, viz též úvodní díl tohoto seriálu):

Příklad Odkaz
36_textures.py https://github.com/tisnik/presentations/blob/master/pyglet/36_textures.py
37_texture_wrapping.py https://github.com/tisnik/presentations/blob/master/pyglet/37_texture_wrapping.py
38_texture_mirror.py https://github.com/tisnik/presentations/blob/master/pyglet/38_texture_mirror.py
39_texture_magnification.py https://github.com/tisnik/presentations/blob/master/pyglet/39_texture_magnification.py
40_texture_smooth.py https://github.com/tisnik/presentations/blob/master/pyglet/40_texture_smooth.py

Poznámka: všechny demonstrační příklady navíc vyžadují v aktuální adresáři přítomnost tohoto obrázku, z něhož se textura vytváří.

17. Odkazy na Internetu

  1. Pyglet Home Page
    https://bitbucket.org/pyglet/pyglet/wiki/Home
  2. Dokumentace k verzi 1.2
    https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/
  3. Dokumentace k verzi 1.2 ve formátu PDF
    https://readthedocs.org/projects/pyglet/downloads/pdf/pyglet-1.2-maintenance/
  4. PyOpenGL
    http://pyopengl.sourceforge.net/
  5. The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours
    https://www.in-ulm.de/~mascheck/various/shebang/
  6. Shebang (Unix)
    https://en.wikipedia.org/wiki/Shebang_%28Unix%29
  7. Domovská stránka systému LÖVE
    http://love2d.org/
  8. Simple DirectMedia Layer (home page)
    http://www.libsdl.org/
  9. Simple DirectMedia Layer (Wikipedia)
    https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer
  10. Seriál Grafická knihovna OpenGL
    https://www.root.cz/serialy/graficka-knihovna-opengl/
  11. Pyglet event loop
    http://pyglet.readthedocs.io/en/latest/programming_guide/eventloop.html
  12. Decorators I: Introduction to Python Decorators
    http://www.artima.com/weblogs/viewpost.jsp?thread=240808
  13. 3D Programming in Python – Part 1
    https://greendalecs.wordpress.com/2012/04/21/3d-programming-in-python-part-1/
  14. A very basic Pyglet tutorial
    http://www.natan.termitnjak.net/tutorials/pyglet_basic.html
  15. Alpha blending
    https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending