OpenGL a nadstavbová knihovna GLU IV

Pavel Tišnovský



Úvodník

V dnešním pokračování seriálu o nadstavbové grafické knihovně GLU se budeme zabývat funkcemi, pomocí nichž je možné provádět projekci a zpětnou projekci bodů či vektorů z trojrozměrného světového prostoru do okna aplikace. Také si popíšeme funkci, pomocí níž je možné jednoduše nastavit perspektivní transformaci.

Obsah

1. Specifikace perspektivní transformace
2. Vlastní implementace funkce gluPerspective()
3. Projekce bodu či vektoru do okna na obrazovce
4. Zpětná projekce ze souřadnic okna do prostoru
5. Demonstrační příklady
6. Obsah dalšího pokračování
7. Seznam funkcí OpenGL a GLUT zmíněných v této části
8. Nové funkce z knihovny GLU popsané v této části
9. Zkomprimovaná verze článku i s přílohami

1. Specifikace perspektivní transformace

V předchozí části tohoto seriálu jsme si popsali funkci, pomocí které je možné nepřímo zadat projekční matici, která se používá pro specifikaci ortogonálního pohledu na zobrazovanou scénu. Dnes si popíšeme funkci pro jednoduchou specifikaci takzvané perspektivní transformace. Tento typ transformace je možné použít prakticky ve všech aplikacích trojrozměrné počítačové grafiky, protože vcelku věrně napodobuje fungování optického systému lidského oka a jejím výsledkem jsou poměrně realisticky vyhlížející trojrozměrné objekty i celé scény.

Funkce pro jednoduché nastavení perspektivní kamery se v nadstavbové knihovně GLU nazývá (jak jinak) gluPerspective() a má následující hlavičku:


void gluPerspective(
    GLdouble fovy,
    GLdouble aspect,
    GLdouble zNear,
    GLdouble zFar
);

Význam parametrů této funkce:

  1. První parametr fovy určuje takzvaný zorný úhel (Field Of View) kamery. Tento úhel je zadán v rovině x-z, která prochází počátkem, tj. bodem O=[0, 0, 0]. Běžně používané zorné úhly se pohybují v rozsahu 30-80 stupňů, je však možné zadat i úhly menší (teleobjektiv, dalekohled) nebo větší (takzvané "rybí oko").
  2. Druhý parametr aspect udává poměr mezi šířkou a výškou zadávaného jehlanu, který omezuje zobrazovaný prostor. Tento parametr lze použít pro roztažení popř. smrsknutí obrázku ve směru jedné ze souřadných os. Hodnotu tohoto parametru můžeme získat podílem šířky a výšky podstavy jehlanu. Většinou se tato hodnota zjišťuje z rozměrů okna pro kreslení (v callback funkci onResize()).
  3. Třetím parametrem zNear se zadává vzdálenost od kamery k bližší ořezávací rovině kolmé na směr promítání. Vzdálenost je tedy zadána ve směru osy z (před prvním natočením či posunutím souřadného systému). Význam tohoto parametru je stejný jako u funkce glFrustum() z knihovny OpenGL.
  4. Posledním parametrem zFar se zadává vzdálenost od kamery k vzdálenější ořezávací rovině kolmé na směr promítání. Stejně jako u předchozího parametru, i tato vzdálenost je zadána ve směru osy z. Význam tohoto parametru je opět totožný s parametrem funkce glFrustum().
Význam parametrů funkce gluPerspective()

Obrázek 1: Význam parametrů funkce gluPerspective()

2. Vlastní implementace funkce gluPerspective()

Funkci gluPerspective() lze naprogramovat i s využitím základních funkcí grafické knihovny OpenGL. Příklad implementace této funkce byl převzat ze stránek http://nehe.gamedev.org, který jsem přeložil do češtiny (poznámky) a poněkud zjednodušil:


// ----------------------------------------------
// Vlastní implementace funkce gluPerspective()
// bez použití nadstavbové knihovny GLU.
// ----------------------------------------------
void myGluPerspective(
    GLdouble fovy,
    GLdouble aspect,
    GLdouble zNear,
    GLdouble zFar)
{
    // známá konstanta PI je použita pro převody
    // úhlů mezi stupni a radiány
    const GLdouble pi=3.1415927;

    // poloviční vzdálenost x-ové a y-ové
    // ořezávací roviny
    GLdouble fw;
    GLdouble fh;
    
    // výpočet vzdálenosti od 0 (počátku souřadnic)
    // do y-ové ořezávací roviny
    fh=tan(fovy/180*pi)*zNear/2;
    // (funkce tan() pracuje s radiány)
    
    // výpočet vzdálenosti od 0 (počátku souřadnic)
    // do x-ové ořezávací roviny
    fw=fh*aspect;

    // nakonec zavoláme funkci glFrustum(), která
    // nastavení transformační matice provede za nás
    glFrustum(-fw, fw, -fh, fh, zNear, zFar);
}

Poloviční vzdálenost od rovin jsme počítali proto, že funkce glFrustum() vyžaduje vzdálenosti zadané od počátku souřadnic, a ne vzájemné vzdálenosti ořezávacích rovin.

Použití perspektivní transformace je ukázáno v prvním a druhém demonstračním příkladu.

3. Projekce bodu či vektoru do okna

Pomocí funkce gluProject() lze provést přímou projekci bodu či vektoru zadaného ve světových souřadnicích (world coordinates) do souřadnic okna (screen coordinates). Tak lze velmi jednoduchým způsobem zjistit, do kterého pixelu na obrazovce se promítne právě zpracovávaný vrchol.

Předností této funkce je, že se neprovádí žádné vykreslování, tj. obsah všech bufferů ve framebufferu zůstane nezměněn, i když zadaný bod či vektor je zpracováván v grafickém vykreslovacím řetězci.

Funkce gluProject() má hlavičku:


GLint gluProject(
    GLdouble objX,
    GLdouble objY,
    GLdouble objZ,
    const GLdouble *modelViewMatrix,
    const GLdouble *projectionMatrix,
    const GLint    *viewPort,
    GLdouble* winX,
    GLdouble* winY,
    GLdouble* winZ
);

Kde jednotlivé parametry mají následující význam:

Pokud přímá projekce pomocí funkce gluProject() proběhne v pořádku, vrátí se hodnota GL_TRUE, v opačném případě se vrátí hodnota GL_FALSE.

4. Zpětná projekce ze souřadnic okna do prostoru

Pomocí funkce gluUnProject() je možné provést zpětnou projekci souřadnic z okna (screen coordinates) do světových souřadnic (world coordinates). Tuto funkci je možné použít například při získání 3D souřadnic kurzoru myši - stačí načíst pozici kurzoru myši na obrazovce, přiřadit mu z-ovou souřadnici a pomocí zpětné projekce zjistit souřadnice kurzoru v prostoru, ve kterém se specifikují souřadnice vrcholů těles.

Po této operaci je možné jednoduše pomocí myši například hýbat s objekty, nebo měnit jejich vrcholy či řídicí body (u Bézierových ploch a ploch typu NURBS).

Funkce gluUnProject() má následující hlavičku:


GLint gluUnProject(
    GLdouble winX,
    GLdouble winY,
    GLdouble winZ,
    const GLdouble *modelViewMatrix,
    const GLdouble *projectionMatrix,
    const GLint    *viewPort,
    GLdouble* objX,
    GLdouble* objY,
    GLdouble* objZ
);

Jednotlivé parametry této funkce mají následující význam:

Pokud zpětná projekce pomocí funkce gluUnProject() proběhne v pořádku, vrátí se hodnota GL_TRUE, v opačném případě se vrátí hodnota GL_FALSE.

5. Demonstrační příklady

První demonstrační příklad je velmi jednoduchý. Po spuštění se nejprve nastaví transformační matice Projection a ModelView. Projekční matice je nastavena pomocí výše popsané funkce gluPerspective(). Poté se vykreslí trojrozměrný objekt - čajová konvička, přičemž je možné myší měnit parametry kamery, tj. obsah projekční matice.

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 2: Screenshot prvního demonstračního příkladu s pozměněným poměrem šířka/výška pohledu

Druhý demonstrační příklad navazuje na příklad první s tím rozdílem, že se pro nastavení projekční matice Projection nepoužívá funkce gluPerspective(), ale vlastní implementace pomocí funkce myGluPerspective(), jejíž zdrojový kód byl ukázán ve druhé kapitole této části.

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 3: Screenshot druhého demonstračního příkladu s pozměněným poměrem šířka/výška pohledu

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

V dalším pokračování tohoto seriálu si ukážeme, jakým způsobem je možné zjednodušit výše zmíněnou funkci gluProject(), která pro běžné použití vyžaduje příliš mnoho parametrů. Také si ukážeme použití zpětné transformace bodů ze souřadnic okna do 3D prostoru pomocí funkce gluUnProject() tak, aby nebylo nutné zadávat z-ovou souřadnici pixelu. Dopředu prozradím pouze to, že se při získávání chybějící z-ové souřadnice budou číst informace z framebufferu.

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

glFrustum()
glViewport()

8. Nové funkce GLU popsané v této části

gluPerspective()
gluProject()

9. 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: Pavel Tišnovský   2004