Hlavní navigace

Knihovna PyOpenGL (2)

18. 3. 2005
Doba čtení: 4 minuty

Sdílet

V dnešním dílu se budeme zabývat rozdíly mezi OpenGL v C a v Pythonu, konkrétně půjde o volání funkcí, výjimky, předávání parametrů funkcím a použití bufferů.

Volání funkcí

Většinu funkcí obsažených v PyOpenGL můžete volat podobně jako v C a můžeme očekávat také podobný výstup. Samozřejmě zde existují rozdíly, které jsou dány rozdíly mezi jazyky C a Python. Jedním z těchto rozdílů je pojetí přístupu k polím. V C například použijeme tento funkční prototyp:

void foo(int count, const int *args);

A v pythonu zase toto volání:

foo(args) -> None

Jak je vidět, v C se předává velikost pole. Důvod spočívá v samotné implementaci polí v jazyce, kdy jsou pole předávána odkazem (čili je předávána pouze jejich adresa) a záleží jen na programátorovi, jak si sám obhospodaří rozsah pole.

Naproti tomu v Pythonu není potřeba předávat funkci velikost pole, protože objekt pole si tuto informaci uchovává. Hodnotu lze z objektu získat metodou pole.__len__() nebo jednodušeji vestavěnou funkcí len(pole).

Výjimky

Chyby v programu jsou předávány pro Python zcela přirozeným způsobem – výjimkami. Funkce glGetError není z důvodů použití výjimek potřebná (nevracela by nic jiného než GL_NO_ERROR). Při konverzi z kódu používajícího tuto funkci musíte zaměnit volání glGetError za řádné zachytávání výjimky. Výjimky:

GL.GLerror
ve všech modulech.
GLU.GLUerror
vyvolávána několika funkcemi v GLU. Funkce v modulu GLU však i nadále používají GL.GLerror.

Předávání parametrů funkcí

Funkcí nastavujících ukazatele na pole existuje mnoho variant (lišících se typem pole). Pro zjednodušení práce programátora byly zavedeny různé zápisy stejné funkce lišící se koncovkami: glXPointer{ub|b|us|­s|ui|i|f|d}. Díky tomuto zápisu funkce přijímá pouze jeden argument (vícerozměrné pole). Koncovky představují typ a velikost jedné buňky pole – ub-unsigned byte, b-byte, us-unsigned short,s-short, ui-unsigned int, i-int, f-float, d-double. Typy lze předat také jako parametr funkce, která však neobsahuje koncovku. Můžete si tedy vybrat to, co vám více vyhovuje.

glColorPointer(size, type, stride, pointer) -> None
glColorPointerub(pointer[][]) -> None
glColorPointerb(pointer[][]) -> None
glColorPointerus(pointer[][]) -> None
glColorPointers(pointer[][]) -> None
glColorPointerui(pointer[][]) -> None
glColorPointeri(pointer[][]) -> None
glColorPointerf(pointer[][]) -> None
glColorPointerd(pointer[][]) -> None

Samozřejmě, že se nemusí jednat pouze o vícerozměrná po­le:

glDrawElements(mode, count, type, indices) -> None
glDrawElementsub(mode, indices[]) -> None
glDrawElementsus(mode, indices[]) -> None
glDrawElementsui(mode, indices[]) -> None

Stejné schéma lze použít i u funkcí kreslících či pracujících s texturami. Např. funkce glDrawPixels přijímá řetězec obsahující data-pixely: glDrawPixels(wid­th, height, format, type, pixels) → None. Tato funkce respektuje parametry nastavené funkcí glPixelStore{i|f}.

Místo dvojice těchto funkcí můžete použít jednu (v různých obměnách), která přejímá jako parametr vícerozměrné pole a nastaví glPixelStore{i|f} automaticky. glDrawPixelsub(for­mat, pixels[][][]) → None – nyní už nemusíme předávat width a height, protože jsou dány rozměry pole a nemusíme předávat typ – ten je GLubyte.

Buffery

Pro použití selektivního bufferu můžete v OpenGL zapsat:

GLuint buffer[SIZE];
glSelectBuffer(SIZE, buffer);
glRenderMode(GL_SELECT);
/* něco nakreslí */
GLint count = glRenderMode(GL_RENDER);
/* analyzuje selekční buffer */

V Pythonu bude však kód vypadat jinak:

glSelectBuffer(SIZE) # alokuje selekční buffer o SIZE elementech
glRenderMode(GL_SELECT)
# něco nakreslí
buffer = glRenderMode(GL_RENDER)
for hit_record in buffer:
    min_depth, max_depth, names = hit_record
    # pracuje se záznamem

Co se týče feedback bufferů, používá se stejný princip, přičemž každý prvek bufferu je n-tice (token, value).

Rozšíření OpenGL

Knihovna obsahuje podporu pro mnoho rozšíření základních modulů GL, GLU a WGL. Tato rozšíření mohou být implementována jako podmoduly jednotlivých balíčků. Např. rozšíření GL_ARB_multitexture může být uloženo jako GL.ARB.multitex­ture. Nezáleží na tom, zda je příslušné rozšíření implementováno jako uživatelská knihovna, nebo ne, vždy musí příslušný modul v balíčkovém stromě existovat. Modul bude také existovat, pokud nebude definovat nové tokeny nebo funkce. Pokud není ono rozšíření podporováno aktuálním kontextem, bude vyvolána výjimka GLerror s kódem GL_INVALID_OPE­RATION.

Každá implementace OpenGL má vlastní metodu pro linkování rozšiřujících knihoven. Může se jednat o dynamické linkování, statické linkování nebo o jakousi dynamicko-statickou kombinaci. Bez ohledu na tuto implementaci je nutné před použítím rozšíření zjistit, zda je podporováno aktuálním kontextem. Můžete to většinou provést voláním funkce glGetString(GL_EX­TENSIONS) a hledáním jména rozšíření ve vráceném řetězci.

Rožšíření jsou potenciálně závislá na kontextu, což znamená, že rozšíření podporované jedním kontextem nemusí být funkční v jiném. To znamená, že pro každý kontext musíte kontrolovat funkcionalitu rozšíření. Pro každé rozšíření musíte tedy provést tyto tři kroky:

CS24_early

  1. ověření, že je rozšíření podporováno aktuálním kontextem
  2. načtení všech adres procedur, které mají být použity
  3. vrácení úspěchu či neúspěchu předcházejících kroků

Funkce v následujícím příkladě přejímá jako parametr jméno rozšíření a vrací jméno inicializační funkce. Je použito pojmenovávací schéma OpenGL.

def init_name(extension_name):
    parts = string.split(extension_name, '_')
    return string.join([string.lower(parts[0]), 'Init'] +
                       map(string.capitalize, parts[2:]) +
                       [parts[1]], '')
>>> init_name('GL_ARB_multitexture')
'glInitMultitextureARB'
>>> init_name('GLU_SGI_filter4_parameters')
'gluInitFilter4ParametersSGI'

Jako další příklad je zde program používající knihovnu GLUT, který ke svému běhu potřebuje rozšíření GL_ARB_multitex­ture:

from OpenGL.GLUT import *
from OpenGL.GL.ARB.multitexture import *
import sys

# inicializuje GLUT
argv = glutInit(sys.argv)

# vytvoří okno, je potřeba před glInitMultitextureARB
glutCreateWindow('foo')

# zjistí, zda je GL_ARB_multitexture podporováno
if not glInitMultitextureARB():
    # chyba - není podpora
    print "Help, I'm lost without GL_ARB_multitexture!"
    sys.exit(1)
# pracuje s GL_ARB_multitexture

Byl pro vás článek přínosný?