V osmé části seriálu o multimediální knihovně Pyglet si ukážeme, jak lze nakonfigurovat texturovací jednotku při nanášení textur na stěny těles. Kromě přímého nanášení jednotlivých texelů je totiž možné textury takzvaně modulovat, míchat barvy texelů s další vybranou barvou apod. Modulace textur se poměrně často používá v praxi, protože umožňuje, aby se z jediného rastrového obrázku s texturou vytvořilo hned několik odlišných variant textur, a to bez větších nároků na kapacitu paměti použité grafickým procesorem pro uložení bitmap/pixmap s texturami.
Obsah
2. Textury v trojrozměrných scénách
3. První demonstrační příklad – textury na stěnách 3D tělesa
4. Nastavení blendingu a modulace textur
5. Režimy výpočtu barev stěn při nanášení textur
6. Druhý demonstrační příklad – nanesení textury na různobarevné čtverce
8. Třetí demonstrační příklad – zákaz modulace textur
9. Nanášení texelů bez dalších úprav popř. použití alfa kanálu
10. Čtvrtý demonstrační příklad: použití alfa kanálu textury
12. Předání parametrů funkcím glTexEnviv a glTexEnvfv
13. Pátý demonstrační příklad – blending na úrovni textur
14. Funkce knihovny OpenGL a pomocné knihovny GLU, které byly použity ve zdrojových kódech
15. Repositář s demonstračními příklady
1. Multimediální knihovna Pyglet: konfigurace texturovací jednotky při nanášení textur na stěny těles
V dnešní části seriálu o multimediální knihovně Pyglet si nejdříve ukážeme, jakým způsobem se textury používají v trojrozměrných scénách a posléze se budeme zabývat některými možnostmi nastavení texturovací jednotky, která je poměrně dobře konfigurovatelná již na úrovni knihoven OpenGL+Pyglet, tj. bez nutnosti explicitního naprogramování grafického akcelerátoru. Na obrázku pod tímto odstavcem je velmi zjednodušené schéma renderovací pipeline ukazující, kde se nachází paměť pro textury a taktéž to, že do této paměti je možné (pochopitelně) i zapisovat.
Obrázek 1: Zjednodušené schéma renderovací pipeline.
2. Textury v trojrozměrných scénách
Zkusme si nyní upravit příklad, v němž je zobrazena scéna s jednoduchým trojrozměrným tělesem – domečkem. Samozřejmě je použito perspektivní promítání ří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
Rotace tělesa je uložena v těchto proměnných:
r1 = 0.0
r2 = 0.0
Změna proměnných r1 a r2 pomocí kurzorových šipek:
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
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)
Obrázek 2: První demonstrační příklad – texturování je nyní vypnuto.
Zapnutí a vypnutí funkce paměti hloubky (z-bufferu) a texturovací jednotky klávesami Z a T:
global depthBufferEnabled
global texturesEnabled
if keys[key.Z]:
depthBufferEnabled = not depthBufferEnabled
if keys[key.T]:
texturesEnabled = not texturesEnabled
clear_buffers(depthBufferEnabled)
set_depth_buffer(depthBufferEnabled)
Samotné nastavení renderingu je zajištěno velmi jednoduše:
if depthBufferEnabled:
glEnable(GL_DEPTH_TEST)
else:
glDisable(GL_DEPTH_TEST)
if texturesEnabled:
glEnable(GL_TEXTURE_2D)
else:
glDisable(GL_TEXTURE_2D)
Obrázek 3: První demonstrační příklad – texturování je nyní zapnuto.
Při specifikaci stěn těles je nutné pro každý vrchol (vertex) uvádět i souřadnice v prostoru textury:
def draw_walls():
glBegin(GL_QUADS) # vykresleni otevrene krychle - sten domecku
glColor3f(0.0, 0.0, 1.0) # modra barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, -5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 0.0) # zelena barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 0.0, 0.0) # cervena barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0) # zluta barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, 5.0, -5.0)
glEnd()
def draw_roof():
glBegin(GL_TRIANGLES) # vykresleni strechy domecku z trojuhelniku
glColor3f(0.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 0.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 0.0, 0.0)
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glEnd()
3. První demonstrační příklad – textury na stěnách 3D tělesa
Vlastnosti prvního příkladu jsme si již popsali v předchozí kapitole, takže si nyní uveďme jeho zdrojový kód:
#!/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
depthBufferEnabled = False # povoleni ci zakaz Z-bufferu
texturesEnabled = False # povoleni ci zakaz textur
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()
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_FILL) # nastaveni rezimu vykresleni modelu
glPolygonMode(GL_BACK, GL_FILL)
glDisable(GL_CULL_FACE) # zadne hrany ani steny se nebudou odstranovat
glDepthFunc(GL_LESS) # funkce pro testovani fragmentu
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_walls():
glBegin(GL_QUADS) # vykresleni otevrene krychle - sten domecku
glColor3f(0.0, 0.0, 1.0) # modra barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, -5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, -5.0, -5.0)
glColor3f(0.0, 1.0, 0.0) # zelena barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, 5.0, -5.0)
glColor3f(1.0, 0.0, 0.0) # cervena barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f(-5.0, 5.0, -5.0)
glColor3f(1.0, 1.0, 0.0) # zluta barva steny
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, -5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, -5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.0, 0.7)
glVertex3f( 5.0, 5.0, -5.0)
glEnd()
def draw_roof():
glBegin(GL_TRIANGLES) # vykresleni strechy domecku z trojuhelniku
glColor3f(0.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 0.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(1.0, 1.0, 1.0)
glTexCoord2f(0.0, 0.0)
glVertex3f( 5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glColor3f(0.0, 0.0, 0.0)
glTexCoord2f(0.0, 0.0)
glVertex3f(-5.0, 5.0, 5.0)
glTexCoord2f(0.7, 0.0)
glVertex3f(-5.0, 5.0, -5.0)
glTexCoord2f(0.7, 0.7)
glVertex3f( 0.0, 11.0, 0.0)
glEnd()
def set_depth_buffer(depthBufferEnabled):
if depthBufferEnabled:
glEnable(GL_DEPTH_TEST)
else:
glDisable(GL_DEPTH_TEST)
def clear_buffers(depthBufferEnabled):
if depthBufferEnabled:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) # vymazani i Z/W bufferu
else:
glClear(GL_COLOR_BUFFER_BIT) # vymazani vsech bitovych rovin barvoveho bufferu
@window.event
def on_draw():
global r1, r2
global depthBufferEnabled
global texturesEnabled
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
if keys[key.Z]:
depthBufferEnabled = not depthBufferEnabled
if keys[key.T]:
texturesEnabled = not texturesEnabled
clear_buffers(depthBufferEnabled)
set_depth_buffer(depthBufferEnabled)
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)
if texturesEnabled:
glEnable(GL_TEXTURE_2D)
else:
glDisable(GL_TEXTURE_2D)
draw_walls()
draw_roof()
pyglet.app.run()
4. Nastavení blendingu a modulace textur
Grafická knihovna OpenGL (a tím pádem i knihovna Pyglet, i když samozřejmě jen nepřímo) podporuje techniku nazvanou modulace textur. S využitím modulace textur je možné kombinovat texturu, jejíž texely jsou na stěny objektu nanášeny, s barvou, která je buď přímo zadána jako vlastnost vykreslované plošky (viz nám již známý příkaz glColor*(), resp. přesněji řečeno jeho různé variace) nebo je vypočtena s využitím takzvaného osvětlovacího modelu. Lze samozřejmě provádět i kombinaci textury s barevným přechodem a další podobné triky.
Nastavení modulace textur se v OpenGL provádí pomocí funkcí:
void glTexEnvf(
GLenum target,
GLenum pname,
GLfloat param
);
void glTexEnvi(
GLenum target,
GLenum pname,
GLint param
);
popřípadě funkcemi, jejichž posledním parametrem je ukazatel na pole hodnot:
void glTexEnvfv(
GLenum target,
GLenum pname,
const GLfloat *params
);
void glTexEnviv(
GLenum target,
GLenum pname,
const GLint *params
);
V knihovně Pyglet se nemusíme zabývat přesnými typy parametrů, takže se hlavičky funkcí zjednoduší na:
def glTexEnvf(
target,
pname,
param
)
def glTexEnvi(
target,
pname,
param
)
def glTexEnvfv(
target,
pname,
params
)
def glTexEnviv(
target,
pname,
params
)
Význam jednotlivých argumentů těchto funkcí je následující:
- Při volání všech uvedených funkcí se do prvního argumentu target zadává cílový objekt (nebo typ objektu) v kontextu knihovny OpenGL, jehož parametry měníme. V případě textur se do tohoto argumentu vždy zadává symbolická konstanta GL_TEXTURE_ENV.
- Druhý argument pname může nabývat několika hodnot, podle toho, jaké konkrétní parametry texturování měníme. První povolenou hodnotou je GL_TEXTURE_ENV_MODE, kdy v dalším parametru param specifikujeme mód výpočtu textur. Druhou hodnotou tohoto argumentu je symbolická konstanta GL_TEXTURE_ENV_COLOR, kdy je dalším parametrem params zadávána barva použitá při blendingu (míchání) textur. Obě možnosti si konkrétně vysvětlíme v navazujících kapitolách. Další možné hodnoty tohoto parametru jsou GL_COMBINE_RGB, GL_COMBINE_ALPHA, GL_RGB_SCALE, GL_ALPHA_SCALE, GL_SRC0_RGB, GL_SRC1_RGB, GL_SRC2_RGB, GL_SRC0_ALPHA, GL_SRC1_ALPHA a GL_SRC2_ALPHA.
- V posledním argumentu param resp. params jsou buď zadány režimy výpočtu textur, nebo barva použitá při takzvaném blendingu textur.
5. Režimy výpočtu barev stěn při nanášení textur
Pomocí funkcí glTexEnvi() a glTexEnvf(), které jsou popsané v předchozí kapitole, je možné nastavit režim výpočtu textur, tj. způsob, jakým bude barva jednotlivých texelů textury kombinována s jinou barvou, získanou například pomocí osvětlovacího modelu. Nastavování se provede konkrétně tímto příkazem:
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, mode);
přičemž poslední argument mode může nabývat tří hodnot:
- GL_DECAL – jednotlivé texely budou nanášeny na vykreslovaný povrch bez dalších úprav. Tímto příkazem tedy zakazujeme jak blending, tak i modulaci textur. Více informací o tomto režimu je uvedeno v dalším textu.
- GL_BLEND – vykreslované texely jsou míchány s předem zadanou barvou.
- GL_MODULATE – vykreslované texely čtené z rastrové textury jsou modulovány podkladovou barvou.
Důležité je vědět, že při inicializaci aplikace, která pro vykreslování používá grafickou knihovnu OpenGL, je standardně nastaveno modulování textur (tj. GL_MODULATE) a míchací barva (GL_TEXTURE_ENV_COLOR) pro blending je černá s nulovou alfa složkou, tj. (0, 0, 0, 0).
Při běhu aplikace je možné provést dotaz na aktuálně nastavený režim výpočtu textur. K tomuto účelu se používají funkce:
void glGetTexEnvfv(
GLenum target,
GLenum pname,
GLfloat * params
);
void glGetTexEnviv(
GLenum target,
GLenum pname,
GLint * params
);
Význam jednotlivých parametrů je stejný jako při zadávání režimu výpočtu, pouze s tím rozdílem, že poslední argumenty jsou určeny pro přepis, tj. je v nich vrácena nastavená hodnota.
6. Druhý demonstrační příklad – nanesení textury na různobarevné čtverce
V dnešním druhém demonstračním příkladu je vykresleno pět čtverců s použitím této funkce:
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()
Každý čtverec má nastavenou jinou podkladovou barvu a je na něj nanesena textura:
glEnable(GL_TEXTURE_2D)
# prostredni ctverec
glColor3f(1.0, 1.0, 1.0)
draw_quad(165, 165)
# vlevo chybi cervena slozka
glColor3f(0.0, 1.0, 1.0)
draw_quad(10, 165)
# vpravo chybi modra slozka
glColor3f(1.0, 1.0, 0.0)
draw_quad(320, 165)
# nahore chybi zelena slozka
glColor3f(1.0, 0.0, 1.0)
draw_quad(165, 320)
# dole chybi zelena a modra slozka
glColor3f(1.0, 0.0, 0.0)
draw_quad(165, 10)
Textura je sice vždy tvořena stejným obrázkem, ovšem díky použití alfa kanálu a implicitní operaci GL_MODULATE jsou texely zkombinovány s podkladovou barvou. Výsledkem bude tato scéna:
Obrázek 4: Screenshot dnešního druhého demonstračního příkladu.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=450,
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)
# prostredni ctverec
glColor3f(1.0, 1.0, 1.0)
draw_quad(165, 165)
# vlevo chybi cervena slozka
glColor3f(0.0, 1.0, 1.0)
draw_quad(10, 165)
# vpravo chybi modra slozka
glColor3f(1.0, 1.0, 0.0)
draw_quad(320, 165)
# nahore chybi zelena slozka
glColor3f(1.0, 0.0, 1.0)
draw_quad(165, 320)
# dole chybi zelena a modra slozka
glColor3f(1.0, 0.0, 0.0)
draw_quad(165, 10)
# spusteni aplikace
pyglet.app.run()
7. Zákaz modulace textur
Jestliže v aplikaci používající knihovny OpenGL a Pyglet zavoláme následující příkaz:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
bude při nanášení texelů na povrch těles původní barva odstraněna a uvidíme tedy pouze texturu, nikoli případné barevné pozadí. To platí i ve chvíli, kdy textura obsahuje poloprůhledné pixely (režim RGBA) – průhlednost je totiž zcela ignorována. Výsledkem aplikace tohoto režimu v našem demonstračním příkladu bude následující obrázek, v němž není ani stopa po původní barvě čtverců:
Obrázek 5: Screenshot dnešního třetího demonstračního příkladu.
8. Třetí demonstrační příklad – zákaz modulace textur
Čtvrtý screenshot, který jste mohli vidět v předchozí kapitole, byl získán po nastavení:
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
Úplný zdrojový kód tohoto příkladu vypadá následovně:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=450,
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)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE)
# prostredni ctverec
glColor3f(1.0, 1.0, 1.0)
draw_quad(165, 165)
# vlevo chybi cervena slozka
glColor3f(0.0, 1.0, 1.0)
draw_quad(10, 165)
# vpravo chybi modra slozka
glColor3f(1.0, 1.0, 0.0)
draw_quad(320, 165)
# nahore chybi zelena slozka
glColor3f(1.0, 0.0, 1.0)
draw_quad(165, 320)
# dole chybi zelena a modra slozka
glColor3f(1.0, 0.0, 0.0)
draw_quad(165, 10)
# spusteni aplikace
pyglet.app.run()
9. Nanášení texelů bez dalších úprav popř. použití alfa kanálu textury
V případě, že je texturování nastaveno do režimu GL_DECAL, jsou barvy texelů pro některé typy textur mapovány do jednotlivých fragmentů bez dalších úprav barvových složek. Jestliže je však formát textury nastaven tak, že neobsahuje přímé barvy (což je dnes zcela běžné), jsou tyto barvy vypočteny buď na základě zadané intenzity či luminance (tj. textury ve stupních šedi), nebo se použije původní barva fragmentu (formát GL_ALPHA). Pokud je textura uložena v barvovém modelu RGBA, je provedeno i míchání barev na základě alfa složky texelů. Naše zkušební textura se zeměkoulí používá barvový model RGBA, takže se v demonstračním příkladu použije právě tato možnost.
Všechny popsané možnosti jsou vyjádřeny v následující tabulce:
Počet barvových komponent |
Typ komponent |
Výpočet barvy |
1 | GL_ALPHA | C=Cf A=At |
1 | GL_LUMINANCE | C=Lt A=Af |
1 | GL_INTENSITY | C=It A=It |
2 | GL_LUMINANCE_ALPHA | C=Lt A=At |
3 | GL_RGB | C=Ct A=Af |
4 | GL_RGBA | C=(1-At)Cf+AtCt A=At |
Poznámka: dnes se prakticky setkáme jen s pátým a se šestým formátem.
Význam jednotlivých symbolů:
Symbol | Význam |
---|---|
C | výsledná barva, jedná se o vektor C=(R, G, B) |
Cf | barva fragmentu |
Ct | barva texelu |
A | vypočtená alfa složka |
Af | alfa složka fragmentu |
At | alfa složka texelu |
Lt | luminance texelu |
10. Čtvrtý demonstrační příklad: použití alfa kanálu textury
V dalším příkladu je texturovací jednotka nastavena do režimu GL_DECAL
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
Výsledkem by měla být tato scéna, protože naše textura je uložena v barvovém modelu RGBA a v režimu GL_DECAL je za těchto okolností provedeno míchání barev na základě alfa složky texelů:
Obrázek 6: Screenshot dnešního čtvrtého demonstračního příkladu.
Následuje výpis zdrojového kódu demonstračního příkladu:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=450,
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)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL)
# prostredni ctverec
glColor3f(1.0, 1.0, 1.0)
draw_quad(165, 165)
# vlevo chybi cervena slozka
glColor3f(0.0, 1.0, 1.0)
draw_quad(10, 165)
# vpravo chybi modra slozka
glColor3f(1.0, 1.0, 0.0)
draw_quad(320, 165)
# nahore chybi zelena slozka
glColor3f(1.0, 0.0, 1.0)
draw_quad(165, 320)
# dole chybi zelena a modra slozka
glColor3f(1.0, 0.0, 0.0)
draw_quad(165, 10)
# spusteni aplikace
pyglet.app.run()
11. Blending na úrovni textur
V případě, že je povolen blending textur, tj. když je zavolán příkaz:
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND)
musí se specifikovat barva, se kterou se bude textura míchat. Pro zadání této barvy je možné použít funkce:
glTexEnviv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color)
resp.:
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color)
kde color je v jazyku C ukazatel na čtyři hodnoty typu GLint nebo GLfloat, které reprezentují barvu v barvovém modelu RGBA. Pokud je blending vypnutý, nemá nastavená barva na vykreslovanou texturu samozřejmě žádný vliv.
Pokud je barva zadaná jako čtyři složky typu GLfloat, jsou tyto složky oříznuty do intervalu –1..1. Pokud se naopak použijí složky typu GLint, je interně proveden převod na GLfloat tak, aby byla nejvyšší hodnota typu GLint převedena na hodnotu 1.0 a nejnižší hodnota na –1.0.
Připomeňme si, že při zadávání rastrových dat textury pomocí funkce glTexImage() je v této funkci specifikováno, které složky barvy jsou v textuře použity. V případě, že je zadána pouze jedna složka, je považována za světlost (L – Luminance). Pokud jsou zadány složky dvě, jsou považovány za světlost následovanou průhledností alfa. Tři zadané složky představují barvový model RGB bez alfa kanálu, složky čtyři potom úplný model RGBA.
V následující tabulce je ukázáno, jak se vypočte vykreslovaná barva v režimu GL_BLEND v závislosti na počtu a typu barevných komponent:
Počet barvových komponent |
Typ komponent |
Výpočet barvy |
1 | GL_ALPHA | není definováno |
1 | GL_LUMINANCE | C=(1-Lt)Cf+LtCc A=At |
1 | GL_INTENSITY | C=(1-It)Cf+ItCc A=At |
2 | GL_LUMINANCE_ALPHA | C=(1-Lt)Cf+LtCc A=AfAt |
3 | GL_RGB | není definováno |
4 | GL_RGBA | není definováno |
Význam jednotlivých symbolů:
Symbol | Význam |
---|---|
C | výsledná barva, jedná se o vektor C=(R, G, B) |
Cf | barva fragmentu |
Ct | barva texelu |
Cc | barva nastavená pomocí funkce glTexEnv*v() |
A | vypočtená alfa složka |
Af | alfa složka fragmentu |
At | alfa složka texelu |
Lt | luminance texelu |
12. Předání parametrů funkcím glTexEnviv a glTexEnvfv
Podívejme se nyní na způsob předání parametrů funkcím glTexEnviv a glTexEnvfv. V programovacím jazyku C je předání jednoduché, protože se použije ukazatel na konkrétní pole s prvky. Při volání wrapperů těchto funkcí v Pythonu musíme použít modul ctypes. Nejprve vytvoříme běžný seznam, zde konkrétně obsahující barvu, tj. trojici hodnot RGB:
color = [0.5, 0.5, 0.5]
Následně musíme tento seznam převést na pole, zde konkrétně pole prvků typu GLfloat. Následující příkaz se skládá ze tří částí – vytvoření pole o třech prvcích typu GLfloat (výraz v první závorce, jedná se o idiomatický způsob zápisu), předání seznamu ve formě n-tice (výraz ve druhé závorce s hvězdičkou na začátku) a přiřazení výsledku (což je v tomto případě objekt vytvořený z třídy PyCArrayType) do proměnné color_gl:
color_gl = (GLfloat * len(color))(*color)
Následně je již možné zavolat kýženou funkci, zde konkrétně glTexEnvfv:
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color_gl)
Poznámka: samozřejmě je možné všechny tři příkazy vykonat přímo ve volání funkce glTexEnvfv.
13. Pátý demonstrační příklad – blending na úrovni textur
Výše popsané předání parametrů funkci glTexEnvfv() je použito v pátém a dnes i posledním demonstračním příkladu, jehož úplný zdrojový kód vypadá následovně:
#!/usr/bin/env python
import pyglet
from pyglet.gl import *
window = pyglet.window.Window(width=450,
height=450,
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)
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND)
color = [0.5, 0.5, 0.5]
color_gl = (GLfloat * len(color))(*color)
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, color_gl)
# prostredni ctverec
glColor3f(1.0, 1.0, 1.0)
draw_quad(165, 165)
# vlevo chybi cervena slozka
glColor3f(0.0, 1.0, 1.0)
draw_quad(10, 165)
# vpravo chybi modra slozka
glColor3f(1.0, 1.0, 0.0)
draw_quad(320, 165)
# nahore chybi zelena slozka
glColor3f(1.0, 0.0, 1.0)
draw_quad(165, 320)
# dole chybi zelena a modra slozka
glColor3f(1.0, 0.0, 0.0)
draw_quad(165, 10)
# spusteni aplikace
pyglet.app.run()
Obrázek 7: Screenshot dnešního pátého demonstračního příkladu.
14. Funkce knihovny OpenGL a pomocné knihovny GLU, které byly použity ve zdrojových kódech
V demonstračních příkladech, které byly uvedeny v dnešním článku, byly použity následující funkce převzaté z knihoven OpenGL a GLU:
- glLoadIdentity
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadIdentity.xml - glRotate
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml - glMatrixMode
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMatrixMode.xml - glViewport
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glViewport.xml - glColor3f
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glColor.xml - glClearColor
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glClearColor.xml - glClear
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glClear.xml - glTexEnvi
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glTexEnvf
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glTexEnviv
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glTexEnvfv
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glBegin
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBegin.xml - glEnd
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBegin.xml - glBindTexture
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBindTexture.xml - glTexParameteri
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexParameter.xml - glEnable
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glEnable.xml - glDisable
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glEnable.xml - glDepthFunc
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glDepthFunc.xml - glVertex3f
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glVertex.xml - glTexCoord2f
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexCoord.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. 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 |
---|---|
41_texture_on_3d.py | https://github.com/tisnik/presentations/blob/master/pyglet/41_texture_on_3d.py |
42_textures_and_colors_modulate.py | https://github.com/tisnik/presentations/blob/master/pyglet/42_textures_and_colors_modulate.py |
43_textures_and_colors_replace.py | https://github.com/tisnik/presentations/blob/master/pyglet/43_textures_and_colors_replace.py |
44_textures_and_colors_decal.py | https://github.com/tisnik/presentations/blob/master/pyglet/44_textures_and_colors_decal.py |
45_textures_and_colors_blend.py | https://github.com/tisnik/presentations/blob/master/pyglet/45_textures_and_colors_blend.py |
Poznámka: všechny demonstrační příklady navíc vyžadují v aktuálním adresáři přítomnost tohoto obrázku, z něhož se textura vytváří.
16. Odkazy na Internetu
- glTexEnv - příkaz OpenGL
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml - glGetTexEnv - příkaz OpenGL
https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glGetTexEnv.xml - 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 - Alpha blending
https://en.wikipedia.org/wiki/Alpha_compositing#Alpha_blending