Ve třetí části seriálu o multimediální knihovně Pyglet si ukážeme dva způsoby zobrazení prostorových (3D) scén, přičemž samotné vykreslování bude prováděno přes grafickou knihovnu OpenGL.
Obsah
1. Multimediální knihovna Pyglet: zobrazení prostorové scény
2. Nastavení kamery sledující prostorovou scénu
3. Pomocné funkce z GLU (OpenGL Utilities)
4. Scéna snímaná ortografickou kamerou
5. Funkce glOrtho() a gluLookAt()
6. Změna transformačních matic a nastavení aktuální transformační matice
7. První demonstrační příklad: scéna snímaná ortografickou kamerou
9. Druhý demonstrační příklad: otáčení objektem ve scéně
10. Scéna snímaná kamerou s perspektivou
11. Základní funkce pro nastavení kamery v perspektivním režimu
12. Třetí demonstrační příklad: perspektivní kamera
13. Repositář s demonstračními příklady
14. Seznam dnes popsaných funkcí knihovny OpenGL a GLU
1. Multimediální knihovna Pyglet: zobrazení prostorové scény
Ve třetí části seriálu o multimediální knihovně Pyglet si ukážeme dva způsoby zobrazení prostorových (3D) scén, přičemž samotné vykreslování bude prováděno přes grafickou knihovnu OpenGL. Při zobrazení prostorové scény musíme vyřešit dva základní problémy. Prvním problémem je zadání údajů o scéně (tj. geometrických a vizuálních údajů o prostorových tělesech, které jsou v ní umístěny), druhým problémem je nastavení kamery neboli pozorovatele, kterou se na scénu díváme, tak, aby byla korektně zobrazena požadovaná část scény. To vyžaduje určitou dávku prostorové představivosti, protože při prvních pokusech se kamera pravděpodobně bude „dívat“ mimo oblast, v níž se nachází tělesa.
První problém je možné pro jednoduché scény řešit triviálně: tělesa ve scéně rozložíme na jednodušší části, které lze reprezentovat pomocí grafických primitiv popsaných minule. U trojrozměrných scén musíme pro každý vrchol (vertex) grafického primitiva zadat minimálně jeho souřadnice v prostoru (tj. buď trojici [x, y, z], nebo v některých případech dokonce čtveřici [x, y, z, w]). Ve skutečnosti však pro většinu těles musíme specifikovat i barvu vrcholu, jeho normálu, souřadnici do textury a/nebo materiál.
Druhý problém, tj. nastavení kamery, pomocí které pozorujeme scénu, lze řešit více způsoby. První způsob spočívá v nastavení transformačních matic Projection a ModelView s využitím dále popsaných funkcí glRotate(), glScale() a glTranslate(). Tento způsob má nevýhodu v tom, že nastavení transformačních matic tímto způsobem není příliš intuitivní (jedním z častých problémů při práci s grafickou knihovnou OpenGL je nesprávné nastavení pohledu na zobrazovanou scénu, takže programátor v horším případě uvidí pouze černou obrazovku, protože se kamera dívá mimo scénu někam do nekonečna). Řešení tohoto problému je popsáno ve druhé a ve třetí kapitole.
2. Nastavení kamery sledující prostorovou scénu
Aby nebylo nutné nastavovat pozici kamery jen nepřímo funkcemi glRotate(), glScale() a glTranslate(), byly do knihovny OpenGL přidány další funkce určené pouze pro práci s kamerou. Ve skutečnosti se pomocí těchto funkcí nepřímo nastavují transformační matice, takže výsledný efekt je stejný jako přímé volání funkcí pro změnu transformačních matic (samozřejmě s korektními parametry). Mezi funkce, které umožňují manipulaci s kamerou, patří:
- glOrtho(): nastavení takzvané ortogonální kamery, tj. takové projekce, u které není zkreslována velikost objektů se vzdáleností od kamery.
- glFrustum(): nastavení takzvané perspektivní kamery, tj. takové projekce, u které se zdánlivá velikost objektů na obrazovce zmenšuje s rostoucí vzdáleností od kamery.
3. Pomocné funkce z GLU (OpenGL Utilities)
Kromě základních funkcí glOrtho() a glFrustum() můžeme využít i pomocné funkce, které nám nabízí nadstavbová knihovna GLU (OpenGL Utilities). Tyto funkce mají výhodu v tom, že umožňují nastavení kamery pomocí poměrně snadno pochopitelných parametrů, které známe i z reálného světa. Jedná se o následující funkce:
- gluLookAt(): tato funkce nastaví pozici kamery, směr jejího pohledu a orientaci souřadného systému vůči kameře (směr „stropu“).
- gluOrtho2D(): nastavení jednoduché 2D ortografické projekce, tj. pohledu na rovinu X-Y. Blízká a vzdálená ořezávací rovina (viz dále) je v tomto případě nastavena na hodnotu –1 resp. +1.
- gluPerspective(): nastavení perspektivní kamery, ale s použitím jiných, intuitivnějších parametrů než u funkce glFrustum().
4. Scéna snímaná ortografickou kamerou
Termínem ortografická kamera (resp. přesněji řečeno ortografická projekce) označujeme takovou projekci, u které není zkreslována velikost objektů se vzdáleností od kamery. Ortografická projekce se používá například v různých CAD systémech pro zobrazení součástek nebo v architektonických aplikacích, protože zde lze jednoduše zjistit délky všech hran bez nutnosti složitých přepočtů.
Nastavení ortografické kamery lze provést například pomocí dvojice funkcí gluLookAt() a glOrtho(). Pomocí funkce glOrtho() se vhodně nastaví transformační matice Projection takovým způsobem, že všechna vykreslovaná grafická primitiva jsou ořezána podle šesti ořezávacích rovin. Tyto ořezávací roviny jsou paralelní s osami souřadného systému, vykreslovaný prostor je tedy tvořen osově orientovaným kvádrem (viewing volume). Všechny vrcholy ležící mimo tento kvádr jsou odstraněny.
5. Funkce glOrtho() a gluLookAt()
Funkce glOrtho() akceptuje šest parametrů nazvaným left, right, bottom, top, near a far s následujícím významem:
Index | Parametr | Význam |
---|---|---|
1 | left | souřadnice levé ořezávací roviny |
2 | right | souřadnice pravé ořezávací roviny |
3 | bottom | souřadnice dolní ořezávací roviny |
4 | top | souřadnice horní ořezávací roviny |
5 | near | souřadnice blízké ořezávací roviny |
6 | far | souřadnice vzdálené ořezávací roviny |
Význam jednotlivých ořezávacích rovin a jejich orientace k pozorovateli je naznačena na následujícím obrázku:
Obrázek 1: Význam parametrů funkce glOrtho().
Důležité je, že vzdálená a blízká ořezávací rovina jsou zadány ve směru záporné osy z. Pokud je kamera umístěna v počátku, tj. na souřadnicích[0, 0, 0], mají parametry funkce glOrtho() tento význam:
- Trojice parametrů [left, bottom, -near] specifikuje bod, který je mapován (transformován) do levého dolního rohu vykreslovaného okna.
- Trojice parametrů [right, top, -near] specifikuje bod, který je mapován (transformován) do pravého horního rohu vykreslovaného okna.
- Parametr -far specifikuje umístění vzdálenější ořezávací roviny.
Druhým krokem v nastavení kamery je její umístění a natočení v prostoru. Tato operace se dá provádět přímou změnou matice ModelView, ale pro intuitivnější práci je výhodnější použít funkci gluLookAt(). Tato funkce akceptuje devět parametrů s následujícím významem:
Index | Parametr | Význam |
---|---|---|
1-3 | eyex, eyey, eyez | souřadnice, na kterých se kamera nachází |
4-6 | centerx, centery, centerz | souřadnice bodu, na který se kamera dívá |
7-9 | upx, upy, upz | vektor směřující „nahoru“ (na vykreslované scéně vertikální směr) |
Pomocí těchto devíti parametrů lze vytvořit lokální souřadný systém kamery, který se skládá z vektorů F (forward), L (left) a U (up). Z prvních třech parametrů se vytvoří bod E (pozice kamery), ze druhých třech parametrů bod C (střed pohledu) a z rozdílu souřadnic těchto dvou bodů získáme vektor F (forward). Zbývá nám tedy dopočítat vektory L a U. Vektor L získáme vektorovým součinem vektorů F a up (poslední tři parametry):
L = up × F
Poslední vektor U se získá dalším vektorovým součinem vektorů F a L:
U = F × L.
Celá situace je znázorněná na dalším obrázku.
Obrázek 2: Kamera ve scéně a výpočet vektorů F, L a U.
6. Změna transformačních matic a nastavení aktuální transformační matice
V knihovně OpenGL existují tři transformační matice, které se postupně aplikují na body (vrcholy, vertexy) popř. i na normálové vektory vrcholů. První transformační matice se jmenuje ModelView matrix. Na tuto matici se můžeme dívat jako na spojení modelové matice a pohledové matice, protože se používá jak pro nastavení pozice kamery, tak i pro manipulaci s objekty (modely) ve scéně. Druhá transformační matice se jmenuje Projection matrix a používá se pro nastavení perspektivní projekce kamery. Třetí transformační matice se jmenuje Viewport matrix a používá se po provedení perspektivní projekce k mapování objektů z abtraktních souřadnic do souřadnic okna. Tato poslední matice ve skutečnosti pouze provádí transformaci v dvojrozměrné ploše, proto se s ní v knihovně OpenGL nepracuje jako s „plnohodnotnou“ maticí. Kromě těchto tří matic můžeme měnit matici, která se používá při mapování textur na povrch objektů. Tato matice se nazývá Texture matrix.
Při změně některé z transformačních matic musíme nejprve určit, kterou transformační matici budeme měnit. K tomuto účelu se používá funkce glMatrixMode(mode). Tato funkce má jeden parametr mode, kterým určujeme matici, kterou budeme dalšími příkazy změnit. Parametr může nabývat tří hodnot, reprezentovaných symbolickými konstantami:
- GL_MODELVIEW – bude se měnit ModelView matrix, tj. matice, ve které jsou uloženy modelové a pohledové transformace (transformace objektů a nastavení kamery).
- GL_PROJECTION – bude se měnit Projection matrix, tj. matice, která se používá pro nastavení perspektivní nebo ortogonální projekce kamery.
- GL_TEXTURE – bude se měnit Texture matrix, tj. matice, která se používá při mapování textur na povrch objektů.
7. První demonstrační příklad: scéna snímaná ortografickou kamerou
Jak již bylo napsáno výše, funkcí glOrtho() se mění prvky v Projection matici. Před zavoláním této funkce je tedy nutné nastavit tuto matici jako aktuální a matici samotnou nastavit jako jednotkovou:
glMatrixMode(GL_PROJECTION) # zacatek modifikace projekcni matice
glLoadIdentity() # vymazani projekcni matice (=identita)
glOrtho(left, right, bottom, top, near, far) # nastaveni ortogonalni projekce
Vzhledem k tomu, že pomocí funkce gluLookAt() se mění prvky transformační matice ModelView, je ve většině běžných případů zapotřebí zavolat tuto sekvenci příkazů:
glMatrixMode(GL_MODELVIEW) # bude se menit modelova matice
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(eyex, eyey, eyez, # bod, odkud se kamera diva
centerx, centery, centerz, # bod, kam se kamera diva
upx, upy, upz # poloha "stropu" ve scene
Základní práce s ortografickou kamerou, tedy nastavení pozice a orientace kamery pomocí funkce gluLookAt(), je ukázána v prvním demonstračním příkladu. Na scéně je zobrazeno jednoduché trojrozměrné těleso ve tvaru domečku složené z trojúhelníků a čtyřúhelníků. Tato grafická primitiva jsou vykreslena jen úsečkami, takže výsledkem je drátový model (wireframe):
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
from pyglet.window import key
window = pyglet.window.Window(width=500,
height=500,
caption="Pyglet+OpenGL")
def init():
glClearColor(0.0, 0.0, 0.3, 0.0) # barva pozadi obrazku
glPolygonMode(GL_FRONT, GL_LINE) # nastaveni rezimu vykresleni dratoveho modelu
glPolygonMode(GL_BACK, GL_LINE) # jak pro predni tak pro zadni steny
glDisable(GL_CULL_FACE) # zadne hrany ani steny se nebudou odstranovat
@window.event
def on_resize(width, height):
init()
glViewport(0, 0, width, height) # viditelna oblast pres cele okno
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT) # vymazani vsech bitovych rovin barvoveho bufferu
glMatrixMode(GL_PROJECTION) # zacatek modifikace projekcni matice
glLoadIdentity() # vymazani projekcni matice (=identita)
glOrtho(-10, 10, -10, 10, -30, 30) # nastaveni ortogonalni projekce
glMatrixMode(GL_MODELVIEW) # bude se menit modelova matice
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(4.0, 6.0, 18.0, # bod, odkud se kamera diva
0.0, 2.0, 0.0, # bod, kam se kamera diva
0.0, 1.0, 0.0) # poloha "stropu" ve scene
glBegin(GL_QUADS) # vykresleni otevrene krychle - sten domecku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glEnd()
glBegin(GL_TRIANGLES) # vykresleni strechy domecku z trojuhelniku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glEnd()
pyglet.app.run()
Obrázek 3: Trojrozměrná scéna vykreslená prvním demonstračním příkladem.
8. Skládání transformací
S obsahem aktuálně nastavené transformační matice je možné manipulovat pomocí funkcí glLoadIdentity(), glLoadMatrix(), glMultMatrix(), glTranslate(), glScale() a glRotate().
Nejjednodušší z těchto funkcí je funkce glLoadIdentity(). Tato funkce nahraje do aktuálně nastavené transformační matice koeficienty odpovídající jednotkové matici, tj. matici, ve které jsou všechny prvky vynulovány s výjimkou prvků hlavní diagonály, které jsou nastaveny na jedničku. Taková matice hraje úlohu neutrálního prvku při násobení matic nebo při násobení vektoru maticí. Při nastavování některé transformační matice se v naprosté většině případů začíná touto funkcí, neboť pomocí ní matici „připravíme“ na aplikaci dalších transformací.
Funkce glLoadMatrix() se používá pro přímé nastavení prvků matice. Další funkce glMultMatrix() slouží k vynásobení aktuálně nastavené transformační matice maticí zadanou jako parametr této funkce.
Další tři funkce glTranslate(), glScale() a glRotate() jsou používány mnohem častěji než předchozí dvě funkce. U těchto funkcí se nemanipuluje přímo s jednotlivými prvky matice, ale zadávají se základní lineární transformace – posun, změna měřítka a rotace. Pro zadané transformace se vytvoří dočasná matice a aktuální matice (většinou je to matice ModelView) je touto dočasnou maticí vynásobena.
9. Druhý demonstrační příklad: otáčení objektem ve scéně
Ve druhém demonstračním příkladu je možné tělesem – domečkem – ve scéně rotovat, což je zařízeno dvojicí globálních proměnných, které se mění stiskem kurzorových kláves:
r1 = 0.0
r2 = 0.0
Změna proměnných r1 a r2:
keys = key.KeyStateHandler()
window.push_handlers(keys)
global r1, r2
if keys[key.LEFT]:
r2 = r2 - 1
if keys[key.RIGHT]:
r2 = r2 + 1
if keys[key.UP]:
r1 = r1 - 1
if keys[key.DOWN]:
r1 = r1 + 1
Rotace tělesa pomocí skládání transformací:
glMatrixMode(GL_MODELVIEW) # bude se menit modelova matice
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(4.0, 6.0, 18.0, # bod, odkud se kamera diva
0.0, 2.0, 0.0, # bod, kam se kamera diva
0.0, 1.0, 0.0) # poloha "stropu" ve scene
glRotatef(r1, 1.0, 0.0, 0.0) # rotace objektu
glRotatef(r2, 0.0, 1.0, 0.0)
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
from pyglet.window import key
r1 = 0.0
r2 = 0.0
window = pyglet.window.Window(width=500,
height=500,
caption="Pyglet+OpenGL")
keys = key.KeyStateHandler()
window.push_handlers(keys)
def init():
glClearColor(0.0, 0.0, 0.3, 0.0) # barva pozadi obrazku
glPolygonMode(GL_FRONT, GL_LINE) # nastaveni rezimu vykresleni dratoveho modelu
glPolygonMode(GL_BACK, GL_LINE) # jak pro predni tak pro zadni steny
glDisable(GL_CULL_FACE) # zadne hrany ani steny se nebudou odstranovat
@window.event
def on_resize(width, height):
init()
glViewport(0, 0, width, height) # viditelna oblast pres cele okno
@window.event
def on_draw():
global r1, r2
if keys[key.LEFT]:
r2 = r2 - 1
if keys[key.RIGHT]:
r2 = r2 + 1
if keys[key.UP]:
r1 = r1 - 1
if keys[key.DOWN]:
r1 = r1 + 1
glClear(GL_COLOR_BUFFER_BIT) # vymazani vsech bitovych rovin barvoveho bufferu
glMatrixMode(GL_PROJECTION) # zacatek modifikace projekcni matice
glLoadIdentity() # vymazani projekcni matice (=identita)
glOrtho(-10, 10, -10, 10, -30, 30) # nastaveni ortogonalni projekce
glMatrixMode(GL_MODELVIEW) # bude se menit modelova matice
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(4.0, 6.0, 18.0, # bod, odkud se kamera diva
0.0, 2.0, 0.0, # bod, kam se kamera diva
0.0, 1.0, 0.0) # poloha "stropu" ve scene
glRotatef(r1, 1.0, 0.0, 0.0) # rotace objektu
glRotatef(r2, 0.0, 1.0, 0.0)
glBegin(GL_QUADS) # vykresleni otevrene krychle - sten domecku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glEnd()
glBegin(GL_TRIANGLES) # vykresleni strechy domecku z trojuhelniku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glEnd()
pyglet.app.run()
Obrázek 4: Trojrozměrná scéna vykreslená druhým demonstračním příkladem.
Obrázek 5: Trojrozměrná scéna vykreslená druhým demonstračním příkladem.
10. Scéna snímaná kamerou s perspektivou
Nastavení perspektivní kamery je poněkud složitější, než nastavení kamery ortogonální, protože prostor viditelný touto kamerou nemá obecně tvar hranolu, ale komolého jehlanu. Musíme tedy vhodným způsobem specifikovat parametry tohoto jehlanu. Podobně jako u kamery nastavené do ortogonálního režimu, i zde se ty části těles, které se nachází mimo viditelný prostor, ořezávají.
11. Základní funkce pro nastavení kamery v perspektivním režimu
Základní funkcí pro nastavení perspektivní kamery je funkce glFrustum(), pomocí které se nastavují prvky transformační matice GL_PROJECTION. Při volání této funkce musíme zadat šest parametrů s následujícím významem:
Index | Parametr | Význam |
---|---|---|
1 | left | vzdálenost levé ořezávací roviny od počátku. Vzdálenost se udává ve směru osy x. |
2 | right | vzdálenost pravé ořezávací roviny od počátku. Vzdálenost se opět udává ve směru osy x, podobně jako u předchozího parametru. |
3 | bottom | vzdálenost spodní ořezávací roviny od počátku. Vzdálenost se udává ve směru osy y. |
4 | top | vzdálenost horní ořezávací roviny od počátku. Vzdálenost je opět zadána ve směru osy y, podobně jako u předchozího parametru. |
5 | near | vzdálenost od kamery k bližší ořezávací rovině kolmé na směr promítání. Vzdálenost je tedy zadána ve směru osy z. |
6 | far | vzdálenost od kamery k vzdálenější ořezávací rovině kolmé na směr promítání. Stejně jako u předchozího parametru, i tato vzdálenost je zadána ve směru osy z. |
Význam parametrů této funkce je znázorněn na šestém obrázku. Výsek viditelného prostoru ve tvaru komolého jehlanu je zadán rozměry menší podstavy (parametry left, right, bottom a top) a vzdáleností obou podstav od kamery. Úhel stěn je možné z těchto hodnot dopočítat.
Obrázek 6: Význam parametrů funkce glFrustum().
Kromě této funkce (která nastavuje transformační matici GL_PROJECTION) lze samozřejmě použít i výše popsanou funkci gluLookAt(), pomocí které se nastaví prvky transformační matice MODELVIEW. Pro korektní práci je nutné nejdříve nastavit měněnou matici funkcí glMatrixMode() a poté tuto matici nastavit na jednotkovou pomocí funkce glLoadIdentity().
S využitím funkce glFrustum() lze sice nastavit projekční transformační matici pro kameru v perspektivním režimu, ale zadávání šesti parametrů komolého jehlanu není příliš intuitivní. Proto byla do knihovny GLU (což je, jak již víme, nadstavba nad knihovnou OpenGL s mnoha užitečnými funkcemi) přidána funkce pro nastavení vlastností kamery v perspektivním režimu pomocí parametrů, jejichž význam známe z reálného světa (používají se například při popisu vlastností objektivů). Tato funkce se jmenuje gluPerspective() a akceptuje čtyři parametry:
Index | Parametr | Význam |
---|---|---|
1 | fovy | zorný úhel (Field Of View) kamery. Tento úhel je zadán v rovině x-z, která prochází počátkem. Běžně používané zorné úhly se pohybují v rozsahu 30–80 stupňů, je však možné zadat i úhly menší (teleobjektiv) nebo větší (rybí oko). |
2 | aspect | poměr mezi šířkou a výškou zadávaného jehlanu. Tento parametr lze použít pro roztažení popř. smrsknutí obrázku ve směru jedné ze souřadných os. Hodnotu tohoto parametru můžeme získat podílem šířky a výšky podstavy jehlanu. Většinou se tato hodnota zjišťuje z rozměrů okna pro kreslení. |
3 | near | vzdálenost od kamery k bližší ořezávací rovině kolmé na směr promítání. Vzdálenost je tedy zadána ve směru osy z. Význam tohoto parametru je tedy stejný jako u funkce glFrustum(). |
4 | far | vzdálenost od kamery k vzdálenější ořezávací rovině kolmé na směr promítání. Stejně jako u předchozího parametru, i tato vzdálenost je zadána ve směru osy z. Význam tohoto parametru je opět totožný s parametrem funkce glFrustum(). |
Význam jednotlivých parametrů funkce gluPerspective() je vysvětlen i na sedmém obrázku. Z tohoto obrázku je také patrné, jak jednotlivé parametry ovlivňují nastavení kamery. Dvojice parametrů fovy a aspect nastavuje tvar menší podstavy komolého jehlanu, zatímco parametry near a far nastavují výšku a velikost jehlanu.
Obrázek 7: Význam parametrů funkce gluPerspective().
12. Třetí demonstrační příklad: perspektivní kamera
Ve třetím a dnes i posledním demonstračním příkladu si ukážeme postup při nastavení kamery s perspektivním promítáním. Způsob zobrazení je řízen trojicí globálních proměnných:
fov = 70.0 # hodnota zorneho uhlu - field of view
nearPlane = 0.1; # blizsi orezavaci rovina
farPlane = 90.0; # vzdalenejsi orezavaci rovina
Nastavení projekční matice:
glMatrixMode(GL_PROJECTION) # zacatek modifikace projekcni matice
glLoadIdentity() # vymazani projekcni matice (=identita)
gluPerspective(fov, 1.0, nearPlane, farPlane);
Nastavení modelview matice s rotací tělesa:
glMatrixMode(GL_MODELVIEW)
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(4.0, 6.0, 18.0, # bod, odkud se kamera diva
0.0, 2.0, 0.0, # bod, kam se kamera diva
0.0, 1.0, 0.0) # poloha "stropu" ve scene
glRotatef(r1, 1.0, 0.0, 0.0) # rotace objektu
glRotatef(r2, 0.0, 1.0, 0.0)
Úplný zdrojový kód třetího demonstračního příkladu vypadá následovně:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
from pyglet.window import key
fov = 70.0 # hodnota zorneho uhlu - field of view
nearPlane = 0.1; # blizsi orezavaci rovina
farPlane = 90.0; # vzdalenejsi orezavaci rovina
r1 = 0.0
r2 = 0.0
window = pyglet.window.Window(width=500,
height=500,
caption="Pyglet+OpenGL")
keys = key.KeyStateHandler()
window.push_handlers(keys)
def init():
glClearColor(0.0, 0.0, 0.3, 0.0) # barva pozadi obrazku
glPolygonMode(GL_FRONT, GL_LINE) # nastaveni rezimu vykresleni dratoveho modelu
glPolygonMode(GL_BACK, GL_LINE) # jak pro predni tak pro zadni steny
glDisable(GL_CULL_FACE) # zadne hrany ani steny se nebudou odstranovat
@window.event
def on_resize(width, height):
init()
glViewport(0, 0, width, height) # viditelna oblast pres cele okno
@window.event
def on_draw():
global r1, r2
if keys[key.LEFT]:
r2 = r2 - 1
if keys[key.RIGHT]:
r2 = r2 + 1
if keys[key.UP]:
r1 = r1 - 1
if keys[key.DOWN]:
r1 = r1 + 1
glClear(GL_COLOR_BUFFER_BIT) # vymazani vsech bitovych rovin barvoveho bufferu
glMatrixMode(GL_PROJECTION) # zacatek modifikace projekcni matice
glLoadIdentity() # vymazani projekcni matice (=identita)
gluPerspective(fov, 1.0, nearPlane, farPlane);
glMatrixMode(GL_MODELVIEW)
glLoadIdentity() # nahrat jednotkovou matici
gluLookAt(4.0, 6.0, 18.0, # bod, odkud se kamera diva
0.0, 2.0, 0.0, # bod, kam se kamera diva
0.0, 1.0, 0.0) # poloha "stropu" ve scene
glRotatef(r1, 1.0, 0.0, 0.0) # rotace objektu
glRotatef(r2, 0.0, 1.0, 0.0)
glBegin(GL_QUADS) # vykresleni otevrene krychle - sten domecku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, -5.0, 5.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 0.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glEnd()
glBegin(GL_TRIANGLES) # vykresleni strechy domecku z trojuhelniku
glColor3f(0.0, 0.0, 1.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 0.0, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glColor3f(0.0, 1.0, 1.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glColor3f(1.0, 1.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 1.0)
glVertex3f( 0.0, 11.0, 0.0)
glEnd()
pyglet.app.run()
Obrázek 8: Trojrozměrná scéna vykreslená třetím demonstračním příkladem.
Obrázek 9: Trojrozměrná scéna vykreslená třetím demonstračním příkladem.
13. Repositář s demonstračními příklady
Všechny tři 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 Pygletu, viz též úvodní díl tohoto seriálu):
Příklad | Odkaz |
---|---|
17_3d_projection.py | https://github.com/tisnik/presentations/blob/master/pyglet/17_3d_projection.py |
18_rotation.py | https://github.com/tisnik/presentations/blob/master/pyglet/18_rotation.py |
19_perspective.py | https://github.com/tisnik/presentations/blob/master/pyglet/19_perspective.py |
14. Seznam dnes popsaných funkcí knihovny OpenGL a GLU
- glLoadIdentity
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadIdentity.xml - glTranslate
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTranslate.xml - glRotate
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml - glScale
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glScale.xml - glMatrixMode
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMatrixMode.xml - glOrtho
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glOrtho.xml - glFrustum
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glFrustum.xml - glViewport
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glViewport.xml - gluLookAt
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml - gluPerspective
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluPerspective.xml
15. Odkazy na Internetu
- Pyglet Home Page
https://bitbucket.org/pyglet/pyglet/wiki/Home - Dokumentace k verzi 1.2
https://pyglet.readthedocs.io/en/pyglet-1.2-maintenance/ - Dokumentace k verzi 1.2 ve formátu PDF
https://readthedocs.org/projects/pyglet/downloads/pdf/pyglet-1.2-maintenance/ - PyOpenGL
http://pyopengl.sourceforge.net/ - The #! magic, details about the shebang/hash-bang mechanism on various Unix flavours
https://www.in-ulm.de/~mascheck/various/shebang/ - Shebang (Unix)
https://en.wikipedia.org/wiki/Shebang_%28Unix%29 - Domovská stránka systému LÖVE
http://love2d.org/ - Simple DirectMedia Layer (home page)
http://www.libsdl.org/ - Simple DirectMedia Layer (Wikipedia)
https://en.wikipedia.org/wiki/Simple_DirectMedia_Layer - Seriál Grafická knihovna OpenGL
https://www.root.cz/serialy/graficka-knihovna-opengl/ - Pyglet event loop
http://pyglet.readthedocs.io/en/latest/programming_guide/eventloop.html - Decorators I: Introduction to Python Decorators
http://www.artima.com/weblogs/viewpost.jsp?thread=240808 - 3D Programming in Python – Part 1
https://greendalecs.wordpress.com/2012/04/21/3d-programming-in-python-part-1/ - A very basic Pyglet tutorial
http://www.natan.termitnjak.net/tutorials/pyglet_basic.html