Ve druhé části seriálu o multimediální knihovně Pyglet si ukážeme další možnosti grafické knihovny OpenGL, jejíž funkce je možné z Pygletu velmi snadno volat. Prozatím se zaměříme na demonstrační příklady provádějící vykreslování scény v ploše (2D), příště si ukážeme i tvorbu plnohodnotných prostorových (3D) scén.
Obsah
1. Multimediální knihovna Pyglet: volání funkcí OpenGL
2. Způsob pojmenování funkcí v knihovně OpenGL
3. Vykreslení všech základních geometrických primitiv podporovaných knihovnou OpenGL
4. Nastavení vlastností bodů (GL_POINTS)
5. Nastavení vlastností úseček (GL_LINES)
6. Vykreslení trojúhelníků, nastavení vzorků vykreslování
12. Má význam používat čtyřúhelníky a polygony na moderním GPU?
13. Repositář s demonstračními příklady
1. Multimediální knihovna Pyglet: volání funkcí OpenGL
V úvodní části seriálu o multimediální knihovně Pyglet jsme si ukázali strukturu jednodušších aplikací a taktéž jsme si řekli, jak je možné Pyglet zkombinovat se známou grafickou knihovnou OpenGL. Připomeňme si, že se v Pygletu používají takzvané callback funkce volané ve chvíli, kdy dojde ke vzniku nějaké události, například při požadavku na překreslení okna atd. Nejjednodušší aplikace, která vykreslí RGB trojúhelník s využitím možností poskytovaných knihovnou OpenGL, může vypadat takto:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=640,
height=480,
caption="Pyglet+OpenGL")
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glBegin(GL_TRIANGLES)
glColor3f(1, 0, 0)
glVertex2f(window.width/2, 0)
glColor3f(0, 1, 0)
glVertex2f(0, window.height)
glColor3f(0, 0, 1)
glVertex2f(window.width, window.height)
glEnd()
pyglet.app.run()
Obrázek 1: Demonstrační příklad popsaný v úvodní části tohoto seriálu.
2. Způsob pojmenování funkcí v knihovně OpenGL
Většina funkcí, které jsou v knihovně OpenGL deklarovány, používá poměrně důmyslnou syntaxi, kdy je již ze jména funkce zřejmé, kolik parametrů je při volání funkce použito a jakého jsou typu. To souvisí s tím, že tato knihovna je primárně určena pro jazyk C (a v minulosti taktéž pro FORTRAN). V případě Pythonu budeme mít práci jednodušší, protože se typ navazuje na hodnoty a nikoli na proměnné. Typická OpenGL funkce pro nastavení barvy vrcholu vypadá takto:
glColor3f(0.0f, 1.0f, 0.0f)
Jméno každé funkce z knihovny OpenGL začíná prefixem (předponou) gl. Podobnou vlastnost mají i funkce z knihoven, které na OpenGL více či méně navazují. Například všechny funkce z knihovny GLU začínají prefixem glu a u knihovny GLUT je použit prefix glut (tato knihovna nás však nemusí při použití Pyglet zajímat):
<strong>gl</strong>Color3f(0.0f, 1.0f, 0.0f)
Za prefixem gl následuje tělo jména funkce, které většinou značí vytvářený předmět (například Vertex) nebo vlastnost, která se nastavuje mění (například Color). Tělo jména funkce začíná velkým písmenem, a pokud se skládá z více slov, jsou počáteční písmena slov velká (například ClearColor). Ve funkcích nejsou použita podtržítka a neexistují dvě jména funkcí, která by se lišila pouze ve velikosti písem, protože by to dělalo problémy u programovacích jazyků, které velikosti písma nerozlišují, například Pascalu či již zmíněného FORTRANu:
gl<strong>Color</strong>3f(0.0f, 1.0f, 0.0f)
Za tělem jména funkce většinou následuje číslo, které značí počet parametrů. Z uvedeného příkladu je tedy zřejmé, že funkce bude mít tři parametry. Pokud funkce nemá žádné parametry, žádné číslo se nepíše (tedy ani 0):
glColor<strong>3</strong>f(0.0f, 1.0f, 0.0f)
Na závěr je jedním či dvěma znaky uveden typ parametrů. U většiny funkcí mají všechny parametry stejný typ, takže typ lze specifikovat. Pokud má funkce více parametrů, z nichž každý je odlišného typu, tyto znaky se neuvádí. Některé funkce existující ve více variantách umožňují místo skupiny parametrů stejného typu předat pole. Potom se uvádí typ současně s písmenem v:
glColor3<strong>f</strong>(0.0f, 1.0f, 0.0f)
Základní typy parametrů, s nimiž si většinou vystačíme:
- byte
- sshort
- integer
- fload
- double
3. Vykreslení všech základních geometrických primitiv podporovaných knihovnou OpenGL
Minule jsme si řekli, že v klasické variantě grafické knihovny OpenGL existuje celkem deset základních geometrických tvarů nazývaných grafická primitiva. Tyto tvary můžeme z hlediska jejich vlastností (a počtu rozměrů) rozdělit do tří skupin:
- body: pouze jediný typ primitiva
- úsečky: jednotlivé úsečky, řetězec úseček (polyčáry) a uzavřený tvar tvořený úsečkami
- plošné útvary: trojúhelníky, pás trojúhelníků, trs trojúhelníků, čtyřúhelníky, pás čtyřúhelníků, polygony
Vykreslování všech grafických primitiv začíná funkcí glBegin(jméno_primitiva) a končí funkcí glEnd(). Mezi těmito funkcemi se specifikují jednotlivé vrcholy objektů některou variantou funkce glVertex*(). Taktéž lze specifikovat barvu vrcholů atd. Podívejme se nyní na demonstrační příklad, v němž jsou všechna podporovaná grafická primitiva vykreslena. Povšimněte si především způsobu použití „příkazových závorek“ glBegin() a glEnd():
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
def draw_points():
glColor3f(1.0, 1.0, 1.0); # nastaveni barvy pro kresleni
glBegin(GL_POINTS); # nyni zacneme vykreslovat body
glVertex2i( 50, 50);
glVertex2i(100, 50);
glVertex2i(100, 100);
glVertex2i( 50, 100);
glEnd();
def draw_lines():
glColor3f(1.0, 0.0, 1.0);
glBegin(GL_LINES); # nyni zacneme vykreslovat usecky
glVertex2i(150, 50);
glVertex2i(200, 50);
glVertex2i(200, 100);
glVertex2i(150, 100);
glEnd();
def draw_line_strip():
glColor3f(0.0, 1.0, 1.0);
glBegin(GL_LINE_STRIP); # nyni vykreslime polycaru
glVertex2i(250, 50);
glVertex2i(300, 50);
glVertex2i(300, 100);
glVertex2i(250, 100);
glEnd();
def draw_line_loop():
glColor3f(1.0, 1.0, 0.0);
glBegin(GL_LINE_LOOP); # nyni vykreslime uzavrenou polycaru
glVertex2i(350, 50);
glVertex2i(400, 50);
glVertex2i(400, 100);
glVertex2i(350, 100);
glEnd();
def draw_triangles():
glColor3f(0.0, 0.0, 1.0);
glBegin(GL_TRIANGLES); # vykresleni trojuhelniku
glVertex2i( 50, 150);
glVertex2i(100, 150);
glVertex2i(100, 200);
glVertex2i( 50, 200);
glEnd();
def draw_triangle_strip():
glColor3f(0.0, 1.0, 0.0);
glBegin(GL_TRIANGLE_STRIP); # vykresleni pruhu trojuhelniku
glVertex2i(150, 150);
glVertex2i(150, 200);
glVertex2i(200, 200);
glVertex2i(200, 150);
glEnd();
def draw_triangle_fan():
glColor3f(1.0, 0.0, 0.0);
glBegin(GL_TRIANGLE_FAN); # vykresleni trsu trojuhelniku
glVertex2i(300, 150);
glVertex2i(250, 160);
glVertex2i(270, 190);
glVertex2i(290, 200);
glVertex2i(310, 200);
glVertex2i(330, 190);
glVertex2i(350, 160);
glEnd();
def draw_quads():
glColor3f(1.0, 0.5, 0.5);
glBegin(GL_QUADS); # vykresleni ctyruhelniku
glVertex2i( 50, 250);
glVertex2i(100, 250);
glVertex2i(100, 300);
glVertex2i( 50, 300);
glEnd();
def draw_quad_strip():
glColor3f(0.5, 0.5, 1.0);
glBegin(GL_QUAD_STRIP); # vykresleni pruhu ctyruhleniku
glVertex2i(150, 250);
glVertex2i(150, 300);
glVertex2i(200, 240);
glVertex2i(200, 310);
glVertex2i(250, 260);
glVertex2i(250, 290);
glVertex2i(300, 250);
glVertex2i(300, 300);
glEnd();
def draw_polygon():
glColor3f(0.5, 1.0, 0.5);
glBegin(GL_POLYGON); # vykresleni konvexniho polygonu
glVertex2i(350, 260);
glVertex2i(370, 240);
glVertex2i(390, 240);
glVertex2i(410, 260);
glVertex2i(410, 280);
glVertex2i(390, 300);
glVertex2i(370, 300);
glVertex2i(350, 280);
glEnd();
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
draw_points()
draw_lines()
draw_line_strip()
draw_line_loop()
draw_triangles()
draw_triangle_strip()
draw_triangle_fan()
draw_quads()
draw_quad_strip()
draw_polygon()
pyglet.app.run()
Obrázek 2: Screenshot dnešního prvního demonstračního příkladu: všechna grafická primitiva.
4. Nastavení vlastností bodů (GL_POINTS)
Při vykreslování jednotlivých bodů je možné specifikovat především jejich barvu (což je ostatně logické), funkcí glPointSize() velikost bodu a taktéž přepínat mezi vykreslením bodů bez antialiasingu glDisable(GL_POINT_SMOOTH) a s antialiasingem glEnable(GL_POINT_SMOOTH). V tomto případě mohou být větší body vykresleny jako kolečka; se zakázaným antialiasingem se vždy bude jednat o čtverce. Ostatně podívejme se na příklad, jehož výstup se může na různých GPU odlišovat (nejlépe a taktéž nejpomaleji funguje SW vykreslování):
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
glDisable(GL_POINT_SMOOTH) # zakazani antialiasingu bodu
glPointSize(1.0) # velikost vykreslovanych bodu je jeden pixel
glBegin(GL_POINTS)
for i in xrange(0, 10): # vykresleni prvni rady bodu ruzne barvy
step = i/10.0
glColor3f(step, 0.5, 1.0-step) # zmena barvy uvnitr prikazovych "zavorek" glBegin()/glEnd()
glVertex2f(50.0+300.0*step, 50.0)
glEnd()
glDisable(GL_POINT_SMOOTH) # zakazani antialiasingu bodu
for i in range(0, 10): # vykresleni druhe rady bodu ruzne velikosti a barvy
step = i/10.0
glColor3f(step, 0.5, 1.0-step) # zmena barvy vne prikazovych "zavorek" glBegin()/glEnd()
glPointSize(step*20.0+1.0) # zmena velikosti vykreslovanych bodu
glBegin(GL_POINTS)
glVertex2f(50.0+300.0*step, 100.0)
glEnd()
glEnable(GL_POINT_SMOOTH) # povoleni antialiasingu bodu
for i in range(0, 10): # vykresleni treti rady bodu ruzne velikosti a barvy
step = i/10.0
glColor3f(step, 0.5, 1.0-step) # zmena barvy vne prikazovych "zavorek" glBegin()/glEnd()
glPointSize(step*20.0+1.0) # zmena velikosti vykreslovanych bodu
glBegin(GL_POINTS)
glVertex2f(50.0+300.0*step, 150.0)
glEnd()
pyglet.app.run()
Obrázek 3: Screenshot dnešního druhého demonstračního příkladu. Povšimněte si, že body jsou na mém počítači vždy vykresleny jako čtverce, nikoli jako kolečka.
5. Nastavení vlastností úseček (GL_LINES)
Ve třetím demonstračním příkladu si ukážeme způsob nastavení vlastností úseček, což je grafické primitivum používané (nejenom) při zobrazování drátových modelů (wireframe). Úsečky jsou vykresleny s různou tloušťkou (glLineWidth), s povoleným či zakázaným antialiasingem a taktéž s použitím uživatelsky definovaných vzorků (glLineStipple). Každý vzorek je reprezentován šestnáctibitovou hodnotou, kde nulové bity představují „díry“ a jedničkové bity „čárky“ či „tečky“. Povšimněte si také, že když má každý vrchol úsečky jinou barvu, je při vykreslení provedena lineární transformace barev:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
patterns=[0xff00, 0xf0f0, 0xcccc, 0x5555, 0xfe10, 0x5e32]
glDisable(GL_LINE_SMOOTH) # zakazani antialiasingu usecek
glDisable(GL_LINE_STIPPLE) # zakazani maskovani pixelu na care
glLineWidth(1.0) # tloustka usecky je jeden pixel
glBegin(GL_LINES)
for i in xrange(0, 10): # vykresleni prvni rady usecek ruzne barvy
step = i/10.0
glColor3f(step, 0.0, 1.0-step) # zmena barvy uvnitr prikazovych "zavorek" glBegin()/glEnd()
glVertex2f(50.0+300.0*step, 20.0)
glColor3f(step, 1.0, 1.0-step)
glVertex2f(100.0+300.0*step, 70.0)
glEnd()
for i in xrange(0, 10): # vykresleni druhe rady usecek ruzne tloustky
step = i/10.0
glLineWidth(step*10.0+0.1) # zmena tloustky usecky
glBegin(GL_LINES)
glColor3f(step, 0.0, 1.0-step) # zmena barvy uvnitr prikazovych "zavorek" glBegin()/glEnd()
glVertex2f(50.0+300.0*step, 90.0)
glColor3f(step, 1.0, 1.0-step)
glVertex2f(100.0+300.0*step, 140.0)
glEnd()
glEnable(GL_LINE_SMOOTH) # povoleni antialiasingu usecek
for i in xrange(0, 10): # vykresleni treti rady usecek ruzne tloustky
step= i/10.0
glLineWidth(step*10.0+0.1) # zmena tloustky usecky
glBegin(GL_LINES)
glColor3f(step, 0.0, 1.0-step) # zmena barvy uvnitr prikazovych "zavorek" glBegin()/glEnd()
glVertex2f(50.0+300.0*step, 160.0)
glColor3f(step, 1.0, 1.0-step)
glVertex2f(100.0+300.0*step, 210.0)
glEnd()
glDisable(GL_LINE_SMOOTH) # zakazani antialiasingu usecek
glEnable(GL_LINE_STIPPLE) # povoleni maskovani pixelu na care
glLineWidth(1.0) # tloustka usecky je jeden pixel
glColor3f(1.0, 1.0, 1.0) # zmena barvy vne prikazovych "zavorek" glBegin()/glEnd()
for i in xrange(0, 6):
glLineStipple(2, patterns[i]) # nastaveni masky pri kresleni usecek
glBegin(GL_LINES)
glVertex2i(50, 250+i*20) # vykresleni usecky
glVertex2i(350, 250+i*20)
glEnd()
pyglet.app.run()
Obrázek 4: Screenshot dnešního třetího demonstračního příkladu: různé varianty úseček.
6. Vykreslení trojúhelníků, nastavení vzorků vykreslování
Způsob vykreslování izolovaných trojúhelníků jsme si již ukázali minule, ovšem u trojúhelníků je možné nastavovat i další vlastnosti. Zejména se to týká nastavení vykreslovacího vzorku, přičemž vzorek je reprezentován bitovou mapou o velikosti 32×32 pixelů, což znamená, že celá mapa je uložena ve 128 bajtech (32×32/8). V Pythonu můžeme vzorek reprezentovat seznamem, který se posléze převede na pole bajtů. Seznam 128 bajtových hodnot může vypadat následovně:
pattern1=[
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08]
Převod na pole bajtů zajistí následující výraz:
pattern1_gl = (GLubyte * len(pattern1))(*pattern1)
Teprve takto vytvořené pole je možné předat do funkce glPolygonStipple():
glPolygonStipple(pattern1_gl)
7. Trojúhelníky
Podívejme se nyní na demonstrační příklad, v němž se vykreslí jednotlivé trojúhelníky představované grafickým primitivem GL_TRIANGLES. Každý trojúhelník je vykreslen odlišným stylem:
Příkaz | Vliv na vykreslování |
---|---|
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) | vykreslí se vyplněné trojúhelníky |
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) | vykreslí se pouze hrany trojúhelníků |
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT) | vykreslí se pouze vrcholy trojúhelníků |
glPolygonStipple(pattern) | pro vyplnění se použije předaný vzorek 32×32 pixelů |
Úplný zdrojový kód tohoto demonstračního příkladu vypadá následovně:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
def draw_triangle(x, y):
glBegin(GL_TRIANGLES)
glColor3f(1.0, 0.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x, y)
glColor3f(0.0, 1.0, 0.0)
glVertex2i(x+100, y)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+50, y+80)
glEnd()
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
pattern1=[ # prvni vyplnovy vzorek
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08]
pattern1_gl = (GLubyte * len(pattern1))(*pattern1)
pattern2=[ # druhy vyplnovy vzorek
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa]
pattern2_gl = (GLubyte * len(pattern2))(*pattern2)
glClearColor(0.0, 0.0, 0.0, 0.0) # nastaveni mazaci barvy na cernou
glClear(GL_COLOR_BUFFER_BIT) # vymazani bitovych rovin barvoveho bufferu
glPointSize(5.0) # velikost bodu je rovna peti pixelum
glLineWidth(2.0) # tloustka usecek je rovna dvema pixelum
glEnable(GL_POINT_SMOOTH) # povoleni antialiasingu bodu
glEnable(GL_LINE_SMOOTH) # povoleni antialiasingu usecek
glDisable(GL_POLYGON_STIPPLE) # zakazat vzorek
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych trojuhelniku
draw_triangle(50, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) # vykreslovani pouze hran trojuhelniku
draw_triangle(180, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT) # vykreslovani pouze vrcholu trojuhelniku
draw_triangle(310, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych trojuhelniku
glEnable(GL_POLYGON_STIPPLE) # povolit vzorek
glPolygonStipple(pattern1_gl) # zadat prvni vzorek
draw_triangle(110, 190)
glPolygonStipple(pattern2_gl) # zadat druhy vzorek
draw_triangle(240, 190)
pyglet.app.run()
Obrázek 5: Screenshot dnešního čtvrtého demonstračního příkladu: různé varianty trojúhelníků.
8. Pás trojúhelníků
Často používané primitivum představuje souvislý pás složený z trojúhelníků. Použít ho lze například při vykreslování stěn složitějších těles. Toto primitivum je univerzálnější než trs trojúhelníků. Zadávání začíná příkazem glBegin(GL_TRIANGLE_STRIP), po němž následují jednotlivé vrcholy. První tři vrcholy definují první trojúhelník. Každý další vrchol definuje další trojúhelník, jenž má s předchozím trojúhelníkem společnou hranu. Použitím tohoto primitiva lze ušetřit až dvě třetiny volání funkce glVertex*():
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
def draw_triangle_strip(x, y):
glBegin(GL_TRIANGLE_STRIP)
glColor3f(1.0, 0.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x, y)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+50, y+80)
glColor3f(0.0, 1.0, 0.0)
glVertex2i(x+100, y)
glColor3f(1.0, 1.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x+150, y+80)
glColor3f(0.0, 1.0, 1.0)
glVertex2i(x+200, y)
glColor3f(1.0, 0.0, 1.0)
glVertex2i(x+250, y+80)
glColor3f(1.0, 1.0, 1.0)
glVertex2i(x+300, y)
glEnd()
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
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
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
draw_triangle_strip(50, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
draw_triangle_strip(50, 200)
pyglet.app.run()
Obrázek 6: Screenshot dnešního pátého demonstračního příkladu: pás trojúhelníků.
9. Trs trojúhelníků
Toto primitivum je určeno pro snížení datového toku při zadávání vrcholů trojúhelníků. Používá se například při vykreslování vrchlíků koule. Zadávání začíná příkazem glBegin(GL_TRIANGLE_FAN), po němž následují jednotlivé vrcholy. První tři vrcholy definují první trojúhelník. Každý další vrchol definuje další trojúhelník, protože ostatní dva vrcholy trojúhelníku jsou shodné vždy s prvním zadaným vrcholem a s předposledním vrcholem. Tvoří se tak jakýsi deštník, v němž mají všechny trojúhelníky společný jeden vrchol. Použitím tohoto primitiva lze ušetřit až dvě třetiny volání funkce glVertex*():
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
def draw_triangle_fan(x, y):
glBegin(GL_TRIANGLE_FAN)
glColor3f(1.0, 0.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x+175, y)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+50, y+80)
glColor3f(0.0, 1.0, 0.0)
glVertex2i(x+100, y+80)
glColor3f(1.0, 1.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x+150, y+80)
glColor3f(0.0, 1.0, 1.0)
glVertex2i(x+200, y+80)
glColor3f(1.0, 0.0, 1.0)
glVertex2i(x+250, y+80)
glColor3f(1.0, 1.0, 1.0)
glVertex2i(x+300, y+80)
glEnd()
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
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
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL)
draw_triangle_fan(50, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE)
draw_triangle_fan(50, 200)
pyglet.app.run()
Obrázek 7: Screenshot dnešního pátého demonstračního příkladu: trs trojúhelníků.
10. Čtyřúhelníky
Toto primitivum umožňuje zadávat rovinné konvexní čtyřúhelníky. Zadávání začíná příkazem glBegin(GL_QUADS), po němž následují jednotlivé vrcholy. Použití je podobné jako u trojúhelníků, ale musíme zaručit, že vrcholy čtyřúhelníku budou ležet v jedné rovině a čtyřúhelník bude konvexní (to je u trojúhelníku zaručeno vždy). Pokud tyto podmínky nebudou splněny, nemusí být (a pravděpodobně ani nebude) vykreslení korektní, protože interpolátory v grafickém akcelerátoru většinou nedovedou detekovat hranici, kde se má vykreslování přerušit:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=350,
caption="Pyglet+OpenGL")
def draw_quad(x, y):
glBegin(GL_QUADS)
glColor3f(1.0, 0.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x, y)
glColor3f(0.0, 1.0, 0.0)
glVertex2i(x+100, y)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+100, y+100)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x, y+100)
glEnd()
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
pattern1=[ # prvni vyplnovy vzorek
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08]
pattern1_gl = (GLubyte * len(pattern1))(*pattern1)
pattern2=[ # druhy vyplnovy vzorek
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa]
pattern2_gl = (GLubyte * len(pattern2))(*pattern2)
glClearColor(0.0, 0.0, 0.0, 0.0) # nastaveni mazaci barvy na cernou
glClear(GL_COLOR_BUFFER_BIT) # vymazani bitovych rovin barvoveho bufferu
glPointSize(5.0) # velikost bodu je rovna peti pixelum
glLineWidth(2.0) # tloustka usecek je rovna dvema pixelum
glEnable(GL_POINT_SMOOTH) # povoleni antialiasingu bodu
glEnable(GL_LINE_SMOOTH) # povoleni antialiasingu usecek
glDisable(GL_POLYGON_STIPPLE) # zakazat vzorek
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych ctyruhelniku
draw_quad(50, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) # vykreslovani pouze hran ctyruhelniku
draw_quad(180, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_POINT) # vykreslovani pouze vrcholu ctyruhelniku
draw_quad(310, 50)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych ctyruhelniku
glEnable(GL_POLYGON_STIPPLE) # povolit vzorek
glPolygonStipple(pattern1_gl) # zadat prvni vzorek
draw_quad(110, 190)
glPolygonStipple(pattern2_gl) # zadat druhy vzorek
draw_quad(240, 190)
pyglet.app.run()
Obrázek 8: Screenshot dnešního šestého demonstračního příkladu.
11. Polygony
Jedná se o nejsložitější primitivum, pomocí něhož lze kreslit polygony zadané větším počtem vrcholů. Musíme však zaručit, že všechny vrcholy budou ležet v jedné rovině a výsledný polygon bude konvexní. V mnoha případech je tato podmínka těžko splnitelná, a proto se polygon rozděluje na jednotlivé trojúhelníky (tesselace). Zadávání polygonu začíná příkazem glBegin(GL_POLYGON), po němž následují souřadnice jednotlivých vrcholů:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=450,
caption="Pyglet+OpenGL")
def draw_polygon(x, y):
glBegin(GL_POLYGON)
glColor3f(1.0, 0.0, 0.0) # kazdy vertex bude vykresleny jinou barvou
glVertex2i(x, y)
glColor3f(0.0, 1.0, 0.0)
glVertex2i(x+50, y+85)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+150, y+85)
glColor3f(0.0, 0.0, 1.0)
glVertex2i(x+200, y)
glColor3f(1.0, 0.0, 1.0)
glVertex2i(x+150, y-85)
glColor3f(1.0, 0.0, 1.0)
glVertex2i(x+50, y-85)
glEnd()
@window.event
def on_draw():
glClear(GL_COLOR_BUFFER_BIT)
glLoadIdentity()
pattern1=[ # prvni vyplnovy vzorek
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x03, 0x80, 0x01, 0xC0, 0x06, 0xC0, 0x03, 0x60,
0x04, 0x60, 0x06, 0x20, 0x04, 0x30, 0x0C, 0x20,
0x04, 0x18, 0x18, 0x20, 0x04, 0x0C, 0x30, 0x20,
0x04, 0x06, 0x60, 0x20, 0x44, 0x03, 0xC0, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x44, 0x01, 0x80, 0x22, 0x44, 0x01, 0x80, 0x22,
0x66, 0x01, 0x80, 0x66, 0x33, 0x01, 0x80, 0xCC,
0x19, 0x81, 0x81, 0x98, 0x0C, 0xC1, 0x83, 0x30,
0x07, 0xe1, 0x87, 0xe0, 0x03, 0x3f, 0xfc, 0xc0,
0x03, 0x31, 0x8c, 0xc0, 0x03, 0x33, 0xcc, 0xc0,
0x06, 0x64, 0x26, 0x60, 0x0c, 0xcc, 0x33, 0x30,
0x18, 0xcc, 0x33, 0x18, 0x10, 0xc4, 0x23, 0x08,
0x10, 0x63, 0xC6, 0x08, 0x10, 0x30, 0x0c, 0x08,
0x10, 0x18, 0x18, 0x08, 0x10, 0x00, 0x00, 0x08]
pattern1_gl = (GLubyte * len(pattern1))(*pattern1)
pattern2=[ # druhy vyplnovy vzorek
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa,
0x55, 0x55, 0x55, 0x55, 0xaa, 0xaa, 0xaa, 0xaa]
pattern2_gl = (GLubyte * len(pattern2))(*pattern2)
glClearColor(0.0, 0.0, 0.0, 0.0) # nastaveni mazaci barvy na cernou
glClear(GL_COLOR_BUFFER_BIT) # vymazani bitovych rovin barvoveho bufferu
glPointSize(5.0) # velikost bodu je rovna peti pixelum
glLineWidth(2.0) # tloustka usecek je rovna dvema pixelum
glEnable(GL_POINT_SMOOTH) # povoleni antialiasingu bodu
glEnable(GL_LINE_SMOOTH) # povoleni antialiasingu usecek
glDisable(GL_POLYGON_STIPPLE) # zakazat vzorek
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych ctyruhelniku
draw_polygon(10, 110)
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) # vykreslovani pouze hran ctyruhelniku
draw_polygon(220, 110)
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL) # vykreslovani vyplnenych ctyruhelniku
glEnable(GL_POLYGON_STIPPLE) # povolit vzorek
glPolygonStipple(pattern1_gl) # zadat prvni vzorek
draw_polygon(10, 320)
glPolygonStipple(pattern2_gl) # zadat druhy vzorek
draw_polygon(220, 320)
pyglet.app.run()
Obrázek 9: Screenshot dnešního sedmého demonstračního příkladu.
12. Má význam používat čtyřúhelníky a polygony na moderním GPU?
Na moderních GPU je použití čtyřúhelníků a polygonů poněkud problematické, protože tyto grafické akcelerátory jsou většinou upraveny pro rychlé vykreslování trojúhelníků se všemi výhodami a nevýhodami, které toto řešení přináší (trojúhelník je vždy konvexní, všechny jeho vrcholy leží v jedné rovině atd.). Standardní OpenGL sice stále podporuje i práci se čtyřúhelníky a obecnými konvexními polygony, ovšem většinou na úkor rychlosti. Tato grafická primitiva totiž jsou ještě před posláním do grafického akcelerátoru rozdělena (teselována) na trojúhelníky, což je samozřejmě časově náročnější, než pouhý přesun vrcholů trojúhelníků do GPU. Z tohoto důvodu je lepší, aby byly všechny vykreslované 3D modely již předem převedeny na trojúhelníkové sítě, samozřejmě pokud je to možné (většina modelovacích SW tento převod provede automaticky).
13. Repositář s demonstračními příklady
Všech osm dnes popsaných demonstračních příkladů bylo uloženo 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 OpenGL a GLU (což se většinou provede automaticky v rámci instalace Pygletu):
14. 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