OpenGL a nadstavbová knihovna GLU (11)

12. 10. 2004
Doba čtení: 7 minut

Sdílet

Mezi funkce, které knihovna GLU programátorům nabízí, patří i funkce pro práci s NURB křivkami a plochami. Tím se otevírají široké možnosti použití hotových modelů 3D těles, které se mohou vytvořit v mnoha aplikacích typu CAD/CAM. Stěnu tělesa popsanou pomocí NURBS tak není nutné nejprve rozložit na trojúhelníky.

Obsah

1. Postup při práci s NURB křivkami
2. Vytvoření objektu pro NURB křivky
3. Nastavení a zjištění atributů NURB křivek
4. Registrace callback funkce, která se volá v případě chyby
5. Začátek vytváření NURB křivky
6. Zadání řídících bodů a knot vektoru NURB křivky
7. Konec vytváření NURB křivky
8. Zrušení objektu pro NURB křivky
9. Demonstrační příklady
10. Obsah dalšího pokračování
11. Nové funkce GLU popsané v této části
12. Odkazy na další zdroje na Internetu
13. Zkomprimovaná verze článku i s přílohami
 

1. Postup při práci s NURB křivkami

V dnešním dílu se zaměřím na popis práce s NURB křivkami pomocí funkcí, jež jsou v knihovně GLU dostupné. Práce s NURB je interně založená na evaluátorech, které jsem podrobně popsal ve svém minulém seriálu na Rootu – viz odkazy na konci článku. Postup při práci s NURB křivkami lze shrnout do několika bodů. Všimněte si prosím podobnosti práce s NURB křivkami s již dříve popsaným postupem práce s kvadrikami (OpenGL evaluátory VI, OpenGL evaluátory VII a OpenGL evaluátory VIII):

  1. Nejprve je vhodné korektně nastavit stavový stroj OpenGL. Vzhledem k tomu, že se při vykreslování NURB generuje velký počet vrcholů, je možné zvážit, zda se má pro každý vrchol automaticky počítat i jeho normála. Pokud to není nutné (například se nepoužívá osvětlení), je možné výpočet normálových vektorů zakázat funkcí glDisable(GL_A­UTO_NORMAL), opětovné povolení se provede funkcí glEnable(GL_A­UTO_NORMAL).
  2. Dále se musí vytvořit programový objekt, který reprezentuje zvolenou NURB křivku v operační paměti počítače. Vytvoření se provede pomocí funkce gluNewNurbsRen­derer().
  3. Pro NURB křivky je možné nastavit několik atributů, podobně jako u dříve popsaných kvadrik. O nastavení těchto atributů se stará funkce gluNurbsProper­ty().
  4. Následně je možné provést registraci callback funkce, jež se zavolá v případě výskytu nějaké chyby při vytváření či renderování NURB křivek. Pro registraci je zapotřebí zavolat funkci gluNurbsCallbac­k(). Typů chyb, které mohou při práci s NURBS vzniknout, je velmi mnoho (v používané verzi knihovny GLU celkem 37), proto je vhodné na chyby jasně definovaným způsobem reagovat.
  5. Počátek vytváření NURB křivky (tj. zadávání řídících bodů a uzlového vektoru) se specifikuje zavoláním funkce gluBeginCurve().
  6. Poté je již možné specifikovat jednotlivé řídící body a složky uzlového vektoru přes funkci gluNurbsCurve().
  7. Konec specifikace parametrů NURB křivky, a tím i ukončení jejího vykreslování, je indikován zavoláním funkce gluEndCurve().
  8. Po použití NURBS křivky je možné zrušit i její objekt zavoláním funkce gluDeleteNurbsRen­derer().

Jednotlivé body výše zmíněného postupu si podrobněji popíšeme v následujících odstavcích.

2. Vytvoření objektu pro NURB křivky

Prvním důležitým úkolem při práci s NURB křivkami je vytvoření programového objektu, který reprezentuje NURB křivku v operační paměti počítače. Tento objekt se vytváří pomocí funkce gluNewNurbsRen­derer() a má následující hlavičku:

GLUnurbs *gluNewNurbsRenderer(
    void
);

Jak je z hlavičky předchozí 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, to je čitelnější).

3. Nastavení a zjištění atributů NURB křivek

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

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

V parametru nurb musí být předán ukazatel na objekt (paměťovou strukturu) NURB křivky či plochy. V parametru property je 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 křivkou či plochou jsou její atributy nastaveny tak, aby se křivka/plocha vykreslila „rozumně“, tj. s poměrně malou chybou, ale se zachováním vysoké rychlosti vykreslování. Existují samozřejmě situace, kdy je počáteční nastavení atributů nekorektní, podrobnosti si popíšeme v některém z dalších pokračování tohoto seriálu.

Zjištění nastavených atributů 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ží.

4. Registrace callback funkce, která se volá v případě chyby

Podobně jako při práci s kvadrikami, i při vykreslování NURBS mohou vzniknout různé neošetřené stavy a chyby. Z tohoto důvodu poskytuje knihovna GLU možnost registrace callback funkcí, které jsou v případě vzniku nějaké chyby vyvolány. Koncept callback funkcí je velmi pružný, protože se tyto mohou měnit přímo za běhu aplikace, což je rozdíl oproti např. strukturovaným výjimkám. 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 křivka či 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ů).

Registrace callback funkce se provádí pomocí funkce gluNurbsCallbac­k(), jež má hlavičku:

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

V prvním parametru 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říkazu gluNewNurbsRen­derer(). Ve druhém parametru musí být vždy předána konstanta GLU_ERROR, v budoucnu se však mohou vyskytnout i další události, při kterých se callback funkce bude volat. Třetí parametr 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.

5. Začátek vytváření NURB křivky

Začátek vykreslování NURB křivky se specifikuje zavoláním funkce gluBeginCurve() s hlavičkou:

void gluBeginCurve(
    GLUnurbs * nurb
);

a jediným parametrem nurb, ve kterém je předán ukazatel na objekt NURB křivky. Tato funkce neočekává žádné další parametry.

6. Zadání řídících bodů a knot vektoru NURB křivky

Prakticky nejsložitější funkcí, kterou knihovna GLU tvůrcům aplikací nabízí, je funkce pro bližší specifikaci NURB křivky a plochy. U NURB křivky se jedná o funkci gluNurbsCurve() s hlavičkou:

void gluNurbsCurve(
    GLUnurbs * nurb,
    GLint      knotCount,
    GLfloat  * knots,
    GLint      stride,
    GLfloat  * control,
    GLint      order,
    GLenum     type
);

Pomocí této funkce se specifikují řídící body NURB křivky, váhy jednotlivých řídících bodů a složky uzlového vektoru. Vzhledem ke složitosti této funkce se budu jejím podrobnějším popisem zabývat až v následující části tohoto seriálu.

7. Konec vytváření NURB křivky

Vzhledem k tomu, že začátek vytváření NURB křivky se specifikoval zavoláním funkce gluBeginCurve(), je jednoduché odhadnout jméno funkce, jejímž zavoláním se provede ukončení vytváření zpracovávané křivky. Ano, tato funkce se podle všech očekávání jmenuje gluEndCurve() a má hlavičku:

void gluEndCurve(
    GLUnurbs * nurb;
);

se stejným parametrem, jako má funkce gluBeginCurve().

8. Zrušení objektu pro NURB křivky

Zrušení NURB křivky 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 křivka či plocha (a s ní 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.

9. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí NURB křivka, která je specifikovaná pomocí čtyř řídících bodů. Uzlový vektor musí v tomto případě obsahovat osm položek. Vzhledem k tomu, že první a poslední čtveřice položek je v uzlovém vektoru stejná, prochází křivka svým prvním a posledním řídícím bodem. Kromě křivky jsou zobrazeny i polohy jednotlivých řídících bodů, které jsou pro lepší ilustraci spojeny polyčarou.

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 je v mnohém podobný příkladu prvnímu s tím rozdílem, že je NURB křivka specifikovaná osmi řídícími body a uzlový vektor má dvanáct položek (tedy o čtyři více, než je počet řídících bodů, což odpovídá stupni křivky).

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

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

V dalším pokračování tohoto seriálu bude uveden podrobný popis funkce gluNurbsCurve() s příklady použití. Zaměřím se zejména na popis vlastností uzlového vektoru a významu vah jednotlivých řídících bodů na tvar výsledné NURB křivky.

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

gluNewNurbsRen­derer()
gluDeleteNurbsRen­derer()
gluNurbsProperty()
gluNurbsCallback()
gluNurbsCurve()
gluGetNurbsPro­perty()
gluBeginCurve()
gluEndCurve()
 

12. Odkazy na další zdroje na Internetu

13. 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.