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

1. Multimediální knihovna Pyglet: konfigurace texturovací jednotky při nanášení textur na stěny těles

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

7. Zákaz modulace textur

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

11. Blending na úrovni textur

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

16. Odkazy na Internetu

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_TEX­TURE_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_TEX­TURE_ENV, GL_TEXTURE_ENV_COLOR, color)

resp.:

glTexEnvfv(GL_TEX­TURE_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 (LLuminance). 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:

  1. glLoadIdentity
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glLoadIdentity.xml
  2. glRotate
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glRotate.xml
  3. glMatrixMode
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glMatrixMode.xml
  4. glViewport
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glViewport.xml
  5. glColor3f
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glColor.xml
  6. glClearColor
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glClearColor.xml
  7. glClear
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glClear.xml
  8. glTexEnvi
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml
  9. glTexEnvf
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml
  10. glTexEnviv
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml
  11. glTexEnvfv
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexEnv.xml
  12. glBegin
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBegin.xml
  13. glEnd
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBegin.xml
  14. glBindTexture
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glBindTexture.xml
  15. glTexParameteri
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexParameter.xml
  16. glEnable
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glEnable.xml
  17. glDisable
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glEnable.xml
  18. glDepthFunc
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glDepthFunc.xml
  19. glVertex3f
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glVertex.xml
  20. glTexCoord2f
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/glTexCoord.xml
  21. gluLookAt
    https://www.khronos.org/registry/OpenGL-Refpages/gl2.1/xhtml/gluLookAt.xml
  22. 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

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