OpenGL a nadstavbová knihovna GLU (5)

24. 8. 2004
Doba čtení: 7 minut

Sdílet

Dnešní pokračování seriálu o nadstavbové grafické knihovně GLU přímo navazuje na přechozí část, ve které jsme si popisovali funkce používané pro specifikaci perspektivní projekce, a také funkci pro přímou projekci bodu z trojrozměrného prostoru do plochy okna zobrazeného na obrazovce. V dnešní části budou podrobněji popsány funkce pro zpětnou projekci bodů a vektorů ze souřadnic na obrazovce do trojrozměrných světových souřadnic.

Obsah

1. Pomocná funkce pro aplikaci zpětné projekce
2. Význam příkazů OpenGL použitých ve funkci myUnProject()
3. Zjednodušená pomocná funkce pro aplikaci zpětné projekce
4. Demonstrační příklady
5. Obsah dalšího pokračování
6. Seznam funkcí OpenGL a GLUT zmíněných v této části
7. Nové funkce z knihovny GLU popsané v této části
8. Zkomprimovaná verze článku i s přílohami
 

1. Pomocná funkce pro aplikaci zpětné projekce

V předchozí části tohoto seriálu jsme si popsali funkci gluUnProject(), pomocí které je možné provádět zpětnou projekci bodů ze souřadnic specifikovaných na obrazovce (screen coordinates) do souřadnic v původním trojrozměrném prostoru, ve kterém se zadávají souřadnice bodů a vektorů, jež vstupují do vykreslovacího řetězce (world coordinates).

Funkce gluUnProject() je však pro použití v reálných aplikacích zbytečně složitá, protože pro svou činnost potřebuje mnoho parametrů. Vzhledem k tomu, že některé parametry lze získat přímo ze stavového stroje OpenGL, je možné zpětnou projekci napsat jednodušeji pomocí vlastní funkce, která jako vstupy získá souřadnice [x, y, z] v prostoru obrazovky, a jako výstup vrátí souřadnice bodu ve světových souřadnicích [xw, yw, zw]:

Tato funkce může vypadat následovně:

// -----------------------------------------------
// Pomocná funkce pro aplikaci zpětné projekce.
// Pro bod zadaný v souřadnicích obrazovky [x,y,z]
// se vypočte odpovídající bod ve světových
// souřadnicích [xw, yw, zw].
// -----------------------------------------------
void myUnProject(
    GLdouble x,              // vstupy
    GLdouble y,
    GLdouble z,
    GLdouble *xw,            // výstupy
    GLdouble *yw,
    GLdouble *zw)
{
    // do těchto pomocných proměnných se uloží
    // souřadnice výsledného bodu
    GLdouble txw, tyw, tzw;

    // pole pro uložení aktuálně
    // nastaveného ViewPortu
    GLint viewPort[4];

    // pole pro uložení aktuálně
    // nastavené matice ModelView
    GLdouble modelViewMatrix[16];

    // pole pro uložení aktuálně
    // nastavené matice Projection
    GLdouble projectionMatrix[16];

    // získat aktuální nastavení
    // ViewPortu
    glGetIntegerv(GL_VIEWPORT, viewPort);

    // získat aktuální nastavení
    // transformační matice ModelView
    glGetDoublev(GL_MODELVIEW_MATRIX,
                 modelViewMatrix);

    // získat aktuální nastavení
    // transformační matice Projection
    glGetDoublev(GL_PROJECTION_MATRIX,
                 projectionMatrix);

    // provést vlastní zpětnou projekci bodu
    gluUnProject(x, y, z, modelViewMatrix,
                 projectionMatrix, viewPort,
                 &txw, &tyw, &tzw);

    // předání parametrů - není nutné,
    // pokud se v předchozí funkci
    // přímo předají ukazatele xw, yw, zw,
    // zde je uvedeno pouze pro přehlednost
    *xw=txw;
    *yw=tyw;
    *zw=tzw;
}

dalším odstavci si popíšeme některé funkce OpenGL, které byly v předchozím příkladu použity a jejichž význam by mohl být z uvedeného kódu nejasný.

2. Význam příkazů OpenGL použitých ve funkci myUnProject()

Ve zdrojovém kódu funkce myUnProject() byly použity některé funkce ze základní grafické knihovny OpenGL, pomocí kterých je možné zjistit vnitřní stav (tj. parametry či atributy) stavového stroje OpenGL. Bližší informace o stavovém stroji OpenGL byly uvedeny v seriálu, který popisoval základy práce s

grafickou knihovnou OpenGL.

Mezi funkce, které byly použity v předchozím kódu, patří:

  1. glGetIntegerv(GL_VI­EWPORT, viewPort) – pomocí této funkce je možné získat aktuální nastavení ViewPortu, pomocí kterého se provádí mapování a ořezání souřadnic pro zobrazení do okna aplikace. Po zavolání této funkce je naplněno pole viewPort, které obsahuje čtyři hodnoty typu GLint. Tyto hodnoty odpovídají souřadnicím [x, y] levého horního rohu obrazovky a pravého dolního rohu. Způsob nastavení těchto hodnot známe – používá se známá funkce glViewPort(), jejíž použití je patrné z demonstračních příkladů uvedených v této i v předchozích částech.
  2. glGetDoublev(GL_MO­DELVIEW_MATRIX, modelViewMatrix) – pomocí této funkce se získává nastavení prvků transformační matice ModelView. Tato matice obsahuje, stejně jako všechny ostatní matice, které slouží ke specifikaci transformace v homogenních souřadnicích [x, y, z, w], šestnáct prvků uspořádaných do čtyř řádků a čtyř sloupců. To znamená, že pole pro uložení hodnot prvků transformační matice musí mít kapacitu pro minimálně šestnáct položek typu GLdouble (který většinou odpovídá vestavěnému typu double použitého překladače).
  3. glGetDoublev(GL_PRO­JECTION_MATRIX, projectionMatrix) – tato funkce, podobně jako funkce předchozí, slouží k získání hodnot prvků transformační matice. U této funkce se však zjišťuje nastavení projekční transformační matice nazývané Projection. Vrácené hodnoty musí být uloženy do paměťové oblasti, která pojme alespoň šestnáct hodnot typuGLdouble resp. double (při překladu zkontrolujte, zda sizeof(GLdouble)==si­zeof(double)!).

Přednosti výše uvedené funkce myUnProject() jsou zřejmé: v aplikaci není potřeba složitě alokovat pomocná pole a získávat prvky nastavených matic, vše se může provést automaticky a skrytě v jedné proceduře. Ve zjednodušování funkce pro zpětnou projekci však můžeme dále pokračovat, jak je ostatně naznačeno v následujícím odstavci.

3. Zjednodušená pomocná funkce pro aplikaci zpětné projekce

Výše uvedenou pomocnou funkci myUnProject() pro aplikaci zpětné projekce lze dále upravit tak, aby se nemusela složitě zjišťovat a zadávat z-ová souřadnice, jejíž hodnota nemá v ploše obrazovky význam, a proto ji nelze přímo zjistit (zjistit lze pouze x-ovou a y-ovou souřadnici, například z pozice kurzoru myši). Pro přečtení z-ové souřadnice bodu je však možné přečíst hloubku fragmentu na určeném místě na obrazovce.

Tato hloubka je, spolu s dalšími informacemi o fragmentech, uložená ve framebufferu, konkrétně v paměti hloubky (Z-bufferu, depth bufferu). Pro její přečtení se použije funkce pro čtení pixmapy z framebufferu, přičemž velikost této pixmapy se rovná pouze jednomu pixelu a souřadnice odpovídá zadaným souřadnicím x a y, tj.:

glReadPixels(x, y, 1, 1, GL_DEPTH_COMPONENT, GL_FLOAT, &z); 

Funkci glReadPixels() už známe, pro osvěžení pouze připomenu význam jednotlivých parametrů:

  1. V prvních dvou parametrech je předána souřadnice počátečního pixelu v pixmapě. Tato souřadnice v našem případě odpovídá souřadnicím bodu v ploše obrazovky, proto se zde předají hodnoty parametrů x a y.
  2. Ve třetím a čtvrtém parametru je specifikován rozměr pixmapy, kterou chceme z framebufferu přečíst. Vzhledem k tomu, že nás zajímá pouze hodnota jednoho pixelu, je horizontální i vertikální rozměr čtené pixmapy nastaven na jedničku, tj. w=h=1.
  3. Pátým parametrem je specifikováno, že se má pixmapa (resp. pouze jeden pixel – viz výše) přečíst z paměti hloubky. Pixmapu lze také přečíst z některého barvového bufferu, paměti šablony nebo z akumulačního bufferu, tyto funkce však v našem případě nevyužijeme.
  4. V šestém parametru je zadán datový typ jednotlivých pixelů, které budou z framebufferu přečteny a případně překonvertovány. Pro předání hodnot do funkcí gluProject() a gluUnProject() se používají datové typy GLdouble resp. GLfloat, proto je v šestém parametru funkceglReadPi­xels() specifikován symbolický identifikátor typu GLfloat.
  5. Poslední parametr by měl obsahovat ukazatel na pole, do kterého se přečtená pixmapa uloží. Vzhledem k tomu, že z framebufferu čteme hodnotu pouze jednoho pixelu, nemusí se pole vytvářet, stačí předat ukazatel na proměnnou typu GLfloat.

Zjednodušená verze funkce myUnProject() bude mít tvar:

// -----------------------------------------------
// Upravená pomocná funkce pro aplikaci zpětné
// projekce. U této funkce není zapotřebí speci-
// fikovat z-ovou souřadnici v prostoru obrazovky,
// protože tato souřadnice je zjištěna z hloubky
// uložené ve framebufferu.
// -----------------------------------------------
void myUnProject2(
    GLdouble x,
    GLdouble y,
    GLdouble *xw,
    GLdouble *yw,
    GLdouble *zw)
{
    // do těchto proměnných se uloží výsledný bod
    GLdouble txw, tyw, tzw;

    // proměnná, do které se z framebufferu
    // načte z-ová souřadnice pixelu
    GLfloat z;

    // pole pro uložení aktuálně
    // nastaveného ViewPortu
    GLint viewPort[4];

    // pole pro uložení aktuálně
    // nastavené matice ModelView
    GLdouble modelViewMatrix[16];

    // pole pro uložení aktuálně
    // nastavené matice Projection
    GLdouble projectionMatrix[16];

    // získat nastavení ViewPortu
    glGetIntegerv(GL_VIEWPORT, viewPort);

    // získat nastavení
    // transformační matice ModelView
    glGetDoublev(GL_MODELVIEW_MATRIX,
                 modelViewMatrix);

    // získat nastavení
    // transformační matice Projection
    glGetDoublev(GL_PROJECTION_MATRIX,
                 projectionMatrix);

    // přečíst hloubku pixelu přímo z framebufferu
    // z paměti hloubky (Z-bufferu, depth bufferu)
    glReadPixels((GLint)x, (GLint)y, // pozice pixmapy
             1, 1,               // rozměry pixmapy
             GL_DEPTH_COMPONENT, // atribut fragmentu
             GL_FLOAT,           // datový formát
             &z          // výsledná "pixmapa"
    );

    // provést vlastní zpětnou projekci
    gluUnProject(x, y, (GLdouble)z,
                 modelViewMatrix, projectionMatrix,
                 viewPort,
                 &txw, &tyw, &tzw);

    // předání parametrů - není nutné,
    // pokud se v předchozí funkci
    // přímo předají ukazatele xw, yw, zw,
    // zde je uvedeno pouze pro přehlednost
    *xw=txw;
    *yw=tyw;
    *zw=tzw;
}

4. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí známý trojrozměrný model čajové konvičky. Pomocí levého tlačítka myši je možné modelem rotovat, pravým tlačítkem se mění vzdálenost tělesa od pozorovatele.

Při pasivním pohybu myší (tj. bez stisknutého tlačítka) se odečítají souřadnice kurzoru na obrazovce a pomocí funkce myUnProject() se provádí přepočet těchto souřadnic do světového prostoru, přičemž je z-ová souřadnice v prostoru obrazovky nastavena na nulu (což není obecně korektní). Odečtené i vypočtené souřadnice jsou zobrazeny v titulkovém pruhu okna pomocí funkce glutSetWindow­Title().

Zdrojový kód prvního demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.

Screenshot prvního demonstračního příkladu
Obrázek 1: Screenshot prvního demonstračního příkladu

Druhý demonstrační příklad navazuje na příklad první s tím, že se souřadnice ve světovém prostoru počítají ve funkci myUnProject2() korektně, tj. z-ová souřadnice na obrazovce je získána čtením z framebufferu. Zkuste kurzorem myši přejet po hranici mezi černým pozadím ve scéně a zobrazeným objektem. Vzdálenost pozadí je nastavena na poměrně velkou hodnotu (zadní ořezávací rovina), kdežto body 3D objektu mají z-ovou hodnotu, která se pohybuje okolo počátku v rozsahu –2..2 jednotky.

Zdrojový kód druhého demonstračního příkladu je dostupný zde, jeho HTML verze se zvýrazněním syntaxe zde.

Screenshot druhého demonstračního příkladu
Obrázek 2: Screenshot druhého demonstračního příkladu

ict ve školství 24

5. Obsah dalšího pokračování

V dalším pokračování seriálu o nadstavbové knihovně GLU si popíšeme funkce, které je možné využít pro vytvoření a následné zobrazení kvadrik, tj. těles ve tvaru koulí, válců a disků.

6. Seznam funkcí OpenGL a GLUT zmíněných v této části

glGetDoublev()
glGetIntegerv()
glReadPixels()
glViewPort()
 

7. Nové funkce z knihovny GLU popsané v této části

gluProject()
gluUnProject()

8. Zkomprimovaná verze článku i s přílohami

Zkomprimovaná verze tohoto článku i s přílohami a demonstračními příklady je uložena zde.

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.