OpenGL a nadstavbová knihovna GLU (14)

2. 11. 2004
Doba čtení: 9 minut

Sdílet

Dnešní díl seriálu o grafické nadstavbové knihovně OpenGL GLU bude věnován podrobnějšímu popisu práce s NURB plochami. Zaměříme se především na uvedení postupu, pomocí něhož se NURB plocha vytvoří a následně vykreslí. Také si uvedeme důležité vlastnosti NURB ploch spolu s popisem funkcí pro změnu a zjištění těchto vlastností.

Obsah

1. Stručné zopakování postupu vytváření NURB plochy
2. Podrobnější popis funkcí pro specifikaci NURB plochy

2.1 Vytvoření objektu pro NURB plochu
2.2 Nastavení a zjištění atributů NURB ploch
2.3 Registrace callback funkce, která se volá v případě výskytu chyby
2.4 Začátek vytváření NURB plochy
2.5 Zadání řídících bodů a uzlového vektoru
2.6 Konec vytváření NURB plochy
2.7 Zrušení objektu s NURB plochou
3. Vlastnosti NURB ploch a jejich nastavování
4. Stupeň NURB plochy
5. Demonstrační příklady
6. Obsah dalšího pokračování
7. Nové funkce z knihovny GLU popsané v této části
8. Zkomprimovaná verze článku i s přílohami

1. Stručné zopakování postupu vytváření NURB plochy

Podobně jako při práci s NURB křivkami, i při práci s NURB plochami se interně využívají evaluátory z grafické knihovny OpenGL. NURB plocha je tedy před vykreslením v grafickém vykreslovacím řetězci OpenGL rozložena na trojúhelníky a plošné čtyřúhelníky, a teprve tato grafická primitiva jsou do vykreslovacího řetězce poslána, tam jsou rozložena na fragmenty a následně vykreslena.

Vývojář aplikace se však těmito detaily nemusí zabývat (pokud samozřejmě nechce nebo nepotřebuje), jelikož funkce z knihovny GLU poskytují možnost pracovat s NURB plochami na vyšší úrovni – stačí pouze specifikovat souřadnice řídících bodů v prostoru spolu s jejich vahami, stupně ploch a uzlové vektory. Celý postup pro práci s NURB plochami lze stručně shrnout do několika bodů:

  1. Nejprve se musí v operační paměti počítače vytvořit programový objekt, který reprezentuje zvolenou NURB plochu. Vytvoření se provede pomocí funkce gluNewNurbsRen­derer(), která je podrobněji popsána v podkapitole 2.1.
  2. Při vytváření a následném vykreslování NURB plochy se bere do úvahy poměrně velké množství atributů, které se nastavují pomocí funkce gluNurbsProper­ty() popsané v podkapitole 2.2.
  3. Následně je možné provést registraci callback funkce, která se zavolá v případě výskytu nějaké chyby při vytváření či renderování NURB ploch. Pro registraci callback funkce je zapotřebí zavolat funkcigluNurbsCa­llback(). Tato funkce je popsána v podkapitole 2.3.
  4. Počátek vykreslování NURB plochy, tj. zadávání řídících bodů spolu s jejich vahami a uzlovými vektory, se specifikuje zavoláním funkce gluBeginSurfa­ce() – viz podkapitola 2.4.
  5. Specifikace jednotlivých řídících bodů se provádí pomocí funkce gluNurbsSurfa­ce(), která je podrobněji popsána v podkapitole 2.5.
  6. Konec vykreslování NURB plochy se provede zavoláním funkce gluEndSurface() – viz text podkapitoly 2.6.
  7. Po specifikaci všech řídících bodů je možné objekt NURB plochy odstranit z operační paměti počítače zavoláním funkce gluEndSurface(), jež je popsána v podkapitole 2.7.

2. Podrobnější popis funkcí pro specifikaci NURB plochy

V této části článku budou podrobněji vysvětleny funkce z nadstavbové knihovny GLU, které je možné nebo nutné volat při specifikaci a následném vykreslení NURB ploch. Některé funkce mají při práci s NURB plochami stejný význam jako v případě NURB křivek, nicméně vzhledem k ucelenosti tohoto článku uvedu tyto funkce znovu i s jejich úplným popisem.

2.1 Vytvoření objektu pro NURB plochu

Prakticky vždy první operací, kterou je nutné při práci s NURB plochami provést, je vytvoření programového objektu, který reprezentuje NURB plochu v operační paměti počítače. Tento objekt se vytváří pomocí funkcegluNewNur­bsRenderer(), jedná se tedy o stejnou funkci, jaká byla použita při práci s NURB křivkami. Hlavička této funkce vypadá následovně:

GLUnurbs * gluNewNurbsRenderer(
    void
);

Jak je z hlavičky této funkce patrné, nevyžaduje se žádný parametr. Funkce vrací buď ukazatel na paměťovou strukturu popisující vzniklý objekt, nebo, v případě chyby, hodnotu 0 resp. NULL.

2.2 Nastavení a zjištění atributů NURB ploch

NURB plochy se mohou vytvářet a vykreslovat s různými vlastnostmi, které se nastavují pomocí funkce gluNurbsProper­ty(). Tato funkce má následující hlavičku:

void gluNurbsProperty(
    GLUnurbs  *nurb,
    GLenum     property,
    GLfloat    value
);

V prvním parametru nurb musí být předán ukazatel na objekt (interní paměťovou strukturu) NURB křivky či plochy. Ve druhém parametruproperty je pomocí symbolické konstanty specifikován atribut, který se má změnit. Hodnota tohoto atributu se předává v posledním parametru value. Při inicializaci objektu s NURB plochou jsou její atributy nastaveny tak, aby se vykreslila „rozumně“, tj. s poměrně malou chybou, ale se zachováním vysoké rychlosti vykreslování. Z toho je patrné, že se knihovna GLU chová k NURB křivkám a plochám prakticky stejně.

Zjištění nastavených atributů NURB plochy lze provést pomocí funkce gluGetNurbsPro­perty() s následující hlavičkou:

void gluGetNurbsProperty(
    GLUnurbs * nurb,
    GLenum     property,
    GLfloat  * data
);

Význam jednotlivých parametrů je u této funkce prakticky shodný jako u funkce gluNurbsProper­ty() s tím rozdílem, že poslední parametr data obsahuje ukazatel na proměnnou, do které se vybraný atribut uloží. Všechny atributy jsou typu GLfloat, i když v některých případech mají význam pouze celočíselné hodnoty.

2.3 Registrace callback funkce, která se volá v případě výskytu chyby

Podobně jako při práci s kvadrikami a NURB křivkami, i při specifikaci či vykreslování NURBS ploch mohou vzniknout různé neošetřené stavy a chyby. Z tohoto důvodu poskytuje nadstavbová grafická knihovna GLU možnost registrace callback funkcí, které jsou v případě vzniku nějaké chyby vyvolány. Aplikace by měla na vzniklé chyby nějakým způsobem reagovat, protože se může stát, že se při výskytu chyby nemusí vykreslit celá NURB plocha, nebo se může vykreslit nesmyslný tvar (zejména při chybně zadaném uzlovém vektoru či vahách krajních řídících bodů – konkrétní chyby si můžete otestovat vhodnou modifikací demonstračních příkladů).

Registrace callback funkce se provádí zavoláním funkce gluNurbsCallbac­k(), která má následující hlavičku:

void gluNurbsCallback(
    GLUnurbs * nurb,
    GLenum     which,
    GLUfuncptr callBackFunc
);

V prvním parametru nurb se opět předává ukazatel na objekt NURB křivky či plochy, jež v operační paměti počítače vznikl vyvoláním příkazugluNew­NurbsRenderer(). Ve druhém parametru which musí být předána konstanta GLU_ERROR Třetí parametr callBackFunc obsahuje ukazatel na uživatelem vytvořenou callback funkci, jež nevrací žádnou hodnotu (tj. je typu void) a má jeden parametr typu GLenum, ve kterém se předává kód vzniklé chyby. Callback funkci lze jednoduše odregistrovat tak, že se v posledním parametru předá hodnota NULL nebo pouze 0 (nula).

2.4 Začátek vytváření NURB plochy

Začátek vykreslování NURB plochy se specifikuje zavoláním funkce gluBeginSurface() s hlavičkou:

void gluBeginSurface(
    GLUnurbs * nurb
);

a jediným parametrem nurb, ve kterém je předán ukazatel na objekt NURB plochy. Tato funkce neočekává žádné další parametry, měla by však být uvedena před funkcí gluEndSurface() a gluNurbsSurfa­ce(), v opačném případě se generuje chyba.

2.5 Zadání řídících bodů a uzlového vektoru

řídící body NURB plochy jsou spolu se složkami uzlového vektoru předány pomocí funkce gluNurbsSurfa­ce(), která má následující hlavičku:

void gluNurbsSurface(
    GLUnurbs  *nurb,
    GLint      sKnotCount,
    GLfloat   *sKnots,
    GLint      tKnotCount,
    GLfloat   *tKnots,
    GLint      sStride,
    GLint      tStride,
    GLfloat   *control,
    GLint      sOrder,
    GLint      tOrder,
    GLenum     type
);

První parametr nurm musí obsahovat ukazatel na objekt NURB plochy v operační paměti počítače.

V dalších dvou parametrech je uložena délka uzlového vektoru a ukazatel na pole hodnot uzlového vektoru ve směru růstu parametru s. Následující dva parametry obsahují podobné informace, nyní ale pro uzlový vektor vytvořený ve směru růstu parametru t.

V parametru sStride je specifikována vzdálenost (počet hodnot typu GLfloat) mezi dvěma souřadnicemi řídících bodů ve směru růstu parametru s. V následujícím parametru tStride je uvedena obdobná informace, která se týká řídících bodů ve směru růstu parametru t. V demonstračních příkladech je ukázáno, jak lze všechny řídící body uložit do jednoho pole a jak se v závislosti na tvaru tohoto pole nastavují oba zmíněné parametry.

Přes parametr control je předán ukazatel na pole, které obsahuje souřadnice řídících bodů, kde každá souřadnice musí být typuGLfloat.

Následující dva parametry sOrder a tOrder obsahují stupeň NURB plochy, který je možné nastavovat nezávisle pro směr růstu parametru s i parametru t.

Poslední parametr type obsahuje typ předávaných řídících bodů. Význam hodnoty tohoto parametru je stejný jako v případě NURB křivek. Jediné rozšíření NURB ploch oproti křivkám je možné použít při trimmingu, který si popíšeme v některém z dalších pokračování tohoto seriálu.

2.6 Konec vytváření NURB plochy

Konec vytváření NURB plochy se specifikuje jednoduše – zavolá se funkce gluEndSurface(), která má hlavičku:

void gluEndSurface(
    GLUnurbs  *nurb
);

Jediný parametr této funkce má stejný význam jako u funkce gluBeginSurfa­ce().

2.7 Zrušení objektu s NURB plochou

Zrušení NURB plochy je stejně jednoduché jako její vytvoření. Stačí pouze zavolat funkci gluDeleteNurbsRen­derer(), která má hlavičku:

void gluDeleteNurbsRenderer(
    GLUnurbs * nurb
);

Tato funkce opět požaduje pouze jeden parametr, v němž je uložen ukazatel na NURBS objekt, který se má zrušit. Po zavolání této funkce je NURB plocha (a s ní pevně spojený renderovací objekt) zrušena, tj. jsou uvolněny všechny interní paměťové struktury, které se k tomuto objektu vztahují. Ukazatel na zrušený objekt se již nesmí dále v běžící aplikaci použít, jinak by byla generována chyba (která ovšem nemůže být odchycena callback funkcí, protože objekt s touto funkcí je již zrušen).

3. Vlastnosti NURB ploch a jejich nastavování

U NURB ploch lze nastavit poměrně značné množství vlastností. Pro vykreslování plochy je důležitý především výběr grafických primitiv, na která bude plocha před vykreslením rozdělena. Jsou implementovány dvě možnosti: plocha je buď vykreslena pomocí lomených čar (polyčar), nebo pomocí vyplněných plošných polygonů.

Další vlastností NURB ploch je specifikace způsobu rozdělení plochy na grafická primitiva. Buď se mohou spočítat délky hran vykreslovaných polygonů či délky čar, které nesmí překročit stanovenou hodnotu. Druhým způsobem je výpočet vzdálenosti bodů na polygonu od plochy. Opět se nesmí překročit předem stanovená hodnota. Třetí možností je specifikace počtu vytvořených bodů ve směru růstu parametru s nebo t. Výběr vhodné metody pro určení dalšího dělení plochy má samozřejmě vliv na kvalitu a rychlost vykreslování.

Mezi pokročilejší vlastnosti patří způsob práce s transformačními maticemi při výpočtu pozic řídících bodů NURB plochy. Buď jsou použity aktuálně nastavené transformační matice, nebo je možné pro každou NURB plochu specifikovat samostatné a nezávislé transformační matice. Toho lze využít například při specifikaci vah jednotlivých řídících bodů.

4. Stupeň NURB plochy

Jak jsem již napsal v popisu funkce gluNurbsSurfa­ce(), je možné u NURB plochy zadávat její stupeň nezávisle pro směr růstu parametru s i parametru t. To má svůj význam například při vytváření kvadrik či podobných těles. Zde je možné například v jednom směru vytvářet „kubickou NURB“ a ve směru druhém „lineární NURB“, tj. sOrder=4, tOrder=2, protože zadaná hodnota je o jednotku větší než požadovaný stupeň křivky.

Při ručním zadávání NURB ploch jsou většinou oba stupně shodné, ve velkém množství aplikací se používají bikvadratické či bikubické NURB plochy, jejichž stupeň je v obou směrech roven dvěma resp. třem.

Ukázka vykreslení NURB plochy, u které se v jednotlivých směrech v parametrickém prostoru liší její stupně, je uvedena ve druhém demonstračním příkladu.

5. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí osvětlená NURB plocha, která je specifikována pomocí šestnácti řídících bodů uspořádaných do mřížky 4×4 body. Při rozdělování NURB plochy na jednotlivé plošky je nastavena velmi nízká tolerance, proto může rendering trvat i na výkonných počítačích poměrně dlouhou dobu.

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

Po překladu a spuštění druhého demonstračního příkladu se zobrazí osvětlená NURB plocha, jež je specifikovaná pomocí dvanácti řídících bodů. Ve směru jednoho parametru se jedná o plochu stupně tři, ve směru parametru druhého o plochu stupně dva. Pro ilustraci jsou znázorněny i řídící body NURB plochy.

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

Třetí demonstrační příklad ukazuje možnost práce s uzlovými vektory, které mají pro oba dva směry v parametrickém prostoru nastaveny jiné hodnoty svých složek.

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

Screenshot třetího demonstračního příkladu
Obrázek 3: Screenshot třetího demonstračního příkladu

ict ve školství 24

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

V dalším pokračování tohoto seriálu uvedu příklady cílené změny uzlového vektoru a jejich dopad na celkový tvar NURB křivky.

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

gluNewNurbsRen­derer()
gluDeleteNurbsRen­derer()
gluNurbsProperty()
gluNurbsCallback()
gluNurbsSurface()
gluBeginSurface()
gluEndSurface()
gluGetNurbsPro­perty()
 

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.