OpenGL a nadstavbová knihovna GLU XII

Pavel Tišnovský



Úvodník

Dnešní pokračování seriálu o nadstavbové grafické knihovně GLU je věnováno podrobnému popisu funkce gluNurbsCurve(), vlivu stupňů NURB křivek na jejich tvar a významu uzlového vektoru. Také si ukážeme, jakým způsobem je možné pomocí NURB křivek zobrazovat kuželosečky, zejména části kružnice a elipsy.

Obsah

1. Podrobný popis funkce gluNurbsCurve()
2. Stupeň vytvořené NURB křivky
3. NURB křivky vyšších stupňů
4. Význam uzlového vektoru
5. Vykreslování kuželoseček pomocí NURB křivek
6. Demonstrační příklady
7. Obsah dalšího pokračování
8. Seznam funkcí OpenGL, GLU a GLUT zmíněných v této části
9. Zkomprimovaná verze článku i s přílohami

1. Podrobný popis funkce gluNurbsCurve()

Již v předchozí části tohoto seriálu jsme si podrobně uvedli, jakým způsobem se postupuje při práci s NURB křivkami vykreslovanými pomocí funkcí z knihovny GLU. Celý postup lze shrnout do sedmi bodů:

  1. vytvoření objektu s NURB křivkou pomocí funkce gluNewNurbsRenderer()
  2. nastavení atributů NURB křivky (i několikerým) voláním funkce gluNurbsProperty()
  3. registrace callback funkce volané při výskytu chyby - gluNurbsCallback()
  4. specifikace začátku vytváření NURB křivky zavoláním funkce gluBeginCurve()
  5. předání souřadnic jednotlivých řídicích bodů, uzlového vektoru apod. opakovaným voláním funkce gluNurbsCurve()
  6. specifikace ukončení vytváření NURB křivky pomocí funkce gluEndCurve()
  7. zrušení objektu, jež reprezentuje NURB křivku v operační paměti počítače, zavoláním funkce gluDeleteNurbsRenderer()

Jedinou funkcí, která minule nebyla dostatečně popsána, je funkce gluNurbsCurve(). Jedná se totiž o jednu z nejsložitějších funkcí z knihovny GLU, alespoň co se týče struktury a významu jejích parametrů. Funkce gluNurbsCurve() má hlavičku:


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

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

Pro některé aplikace může být významná skutečnost, že řídicí body NURB křivky nemusí být zadány pouze ve světovém prostoru (podobně jako souřadnice vrcholů vykreslovaných těles), ale i v "dalších prostorech": texturovacím, barvovém (RGBA) a prostoru, ve kterém jsou specifikovány normálové vektory jednotlivých řídicích bodů. Význam specifikace souřadnic v těchto prostorech jsem již vysvětloval při popisu evaluátorů OpenGL - zejména při práci s NURB plochami se musí pro každý řídicí bod korektně namapovat i jeho texturovací souřadnice, barva a normála.

Nejjednodušším tvarem specifikujícím NURB křivku je sekvence příkazů, kterými se zadají pouze její řídicí body ve světovém prostoru bez dalších informací. Tato sekvence příkazů vypadá následovně:


gluBeginCurve(nurbObj);
    gluNurbsCurve(nurbObj, .., GL_MAP1_VERTEX_3);
gluEndCurve(nurbObj);

případně takto:


gluBeginCurve(nurbObj);
    gluNurbsCurve(nurbObj, .., GL_MAP1_VERTEX_4);
gluEndCurve(nurbObj);

Složitější je situace v případě, že se kromě souřadnic řídicích bodů musí zadat i jejich některé další vlastnosti. V následující sekvenci příkazů je ukázáno, jakým způsobem se specifikují polohy řídicích bodů spolu s jejich barvami (ty jsou, jak již víme, zadány ve čtyřrozměrném barvovém prostoru RGBA):


gluBeginCurve(nurbObj);
    gluNurbsCurve(nurbObj, .., GL_MAP1_COLOR_4);
    gluNurbsCurve(nurbObj, .., GL_MAP1_VERTEX_3);
gluEndCurve(nurbObj);

2. Stupeň vytvořené NURB křivky

Stupeň NURB křivky udává nejvyšší mocninu polynomu, kterým je křivka či její část zadaná. V předchozích částech jsme si uvedli rekurentní vztah, pomocí něhož lze pomocí lineární interpolace/kombinace funkcí vypočítat jakýkoli bod na NURB křivce. Po rozepsání rekurentního vztahu dojdeme k (po částech) polynomiální funkci s jednou nezávislou proměnnou t. Nejvyšší mocnina, kterou je t umocněno, udává stupeň vzniklé NURB křivky. V OpenGL se však místo stupně křivky (degree) udává parametr order, který je vždy o jedničku vyšší, tj. například pro kubiky je hodnota order rovna čtyřem apod.

Čím je stupeň křivky vyšší, tím je křivka vizuálně hladší, ovšem pouze v případě, že neuvažujeme násobné hodnoty v uzlovém vektoru. U NURB křivek má smysl uvažovat o nejnižším stupni rovným jedné (tj. degree=1 => order=2). V tomto případě se jedná o křivku, která je složena z lineárních částí, tj. úseček. Zlomy nastávají v místech řídicích bodů, tj. tam, kde se přechází z jednoho úseku parametrů uzlového vektoru do úseku dalšího (z předcházejících částí také víme, že složky uzlového vektoru musí tvořit neklesající posloupnost hodnot).

Ukázka NURB křivky prvního stupně je zobrazena na prvním ilustračním obrázku spolu s jejími řídicími body.

NURB křivka prvního stupně spolu s navzájem pospojovanými řídicími body

Obrázek 1: NURB křivka prvního stupně spolu s navzájem pospojovanými řídicími body

NURB křivka druhého stupně (order=3) má již pro potřeby počítačové grafiky zajímavější průběh. Křivka obecně neprochází všemi svými řídicími body, v případě požadavku na průchod těmito body (například při animacích) je možné jejich zdvojení. Křivka musí být zadána minimálně třemi řídicími body. Ukázka křivky druhého stupně je zobrazena na druhém ilustračním obrázku.

NURB křivka druhého stupně spolu s navzájem pospojovanými řídicími body

Obrázek 2: NURB křivka druhého stupně spolu s navzájem pospojovanými řídicími body

Prakticky nejpoužívanějšími NURB křivkami jsou však kubiky, tj. křivky či části křivek třetího stupně (degree=3 => order=4). U těchto křivek je parametr t mocněn na třetí a v případě korektně zadaného uzlového vektoru lze zajistit spojitost třídy C2 i G2. Také je možné vytvářet kuželosečky, zejména kružnice a kruhové oblouky. Příklad NURB křivky třetího stupně je zobrazen na třetím ilustračním obrázku.

NURB křivka třetího stupně spolu s navzájem pospojovanými řídicími body

Obrázek 3: NURB křivka třetího stupně spolu s navzájem pospojovanými řídicími body

3. NURB křivky vyšších stupňů

Ukázky NURB křivek vyšších stupňů jsou zobrazeny na dalších třech obrázcích:

NURB křivka čtvrtého stupně

Obrázek 4: NURB křivka čtvrtého stupně

NURB křivka pátého stupně

Obrázek 5: NURB křivka pátého stupně

NURB křivka šestého stupně

Obrázek 6: NURB křivka šestého stupně

4. Význam uzlového vektoru

Hodnoty složek uzlového vektoru mají na tvar NURB křivky stejně významný vliv, jako pozice jejich řídicích bodů. Počet složek uzlového vektoru je závislý jak na počtu řídicích bodů, tak i na stupni křivky. Současně se na hodnoty složek uzlového vektoru kladou další omezující podmínky:

  1. Počet hodnot uložených v uzlovém vektoru je roven součtu řídicích bodů a stupni křivky zvětšeného o jedničku.
  2. Posloupnost hodnot musí být neklesající, tj. ti<=ti+1.
  3. Počet stejných hodnot (ty musí být dle předchozího bodu sousední) musí být menší nebo roven stupni křivky.

Z rekurentního vztahu pro výpočet NURB křivek dále vyplývá, že hodnoty v uzlovém vektoru nejsou nijak omezeny, většinou se však počítá s rozsahem <0, 1>, což odpovídá i rozsahu parametru t u Bézierových křivek. Pokud jsou hodnoty uvedené na začátku a konci uzlového vektoru konstantní (jejich počet je omezen stupněm křivky), prochází křivka svými krajními body.

Na dalších třech ilustračních obrázcích jsou zobrazeny NURB křivky třetího stupně (kubiky), které se liší pouze hodnotami uloženými v jejich uzlových vektorech.

NURB křivka třetího stupně se změněným uzlovým vektorem

Obrázek 7: NURB křivka třetího stupně se změněným uzlovým vektorem

NURB křivka třetího stupně se změněným uzlovým vektorem

Obrázek 8: NURB křivka třetího stupně se změněným uzlovým vektorem

NURB křivka třetího stupně se změněným uzlovým vektorem

Obrázek 9: NURB křivka třetího stupně se změněným uzlovým vektorem

5. Vykreslování kuželoseček pomocí NURB křivek

Pomocí NURB křivek je možné reprezentovat i kuželosečky. V tomto případě je však nutné specifikovat u každého řídicího bodu i jeho váhu. To se provede tak, že se nejprve vytvoří pole řídicích bodů, kde každému bodu odpovídají čtyři souřadnice [x, y, z, w]. Příklad:


GLfloat ctlpoints[][4]={
    // x   y   z   w
    { 50, 400, 0, 1.0},
    {225,  97, 0, 0.5},
    {400, 400, 0, 1.0}
};

Dále je nutné při volání funkce gluNurbsCurve() správně specifikovat parametry, zejména parametr stride, který bude nastaven na hodnotu 4 a parametr type, který by měl být nastaven na hodnotu GL_MAP1_VERTEX_4. Nyní čtvrtá souřadnice řídicích bodů odpovídá jejich váze.

Jak se však kuželosečky vytvoří? Ukážeme si nejjednodušší případ - tvorbu kruhového oblouku, který je speciálním případem oblouku eliptického. Při tvorbě kruhového oblouku nám stačí zadat pouze tři řídicí body a vytvořit tak NURB křivku druhého stupně. Řídicí body by měly být v prostoru rozmístěny tak, aby tvořily vrcholy rovnoramenného trojúhelníka. Uzlový vektor bude mít hodnoty jednotlivých složek nastaveny následovně:

[0,0,0,1,1,1]

To, o jaký typ oblouku se jedná, se specifikuje vahou řídicích bodů. První a poslední řídicí bod má váhu jednotkovou, tj. w0=w2=1, liší se pouze váha prostředního bodu. Pro kruhový oblouk musí platit w1=cos a, kde a je úhel, který svírají úsečky spojující prostřední řídicí bod s body krajními. Celou kružnici je možné vytvořit spojením třech nebo čtyřech kruhových oblouků - tyto lze dokonce vytvořit v rámci jedné NURB křivky, pouze se musí dát pozor na korektní nastavení hodnot uzlového vektoru.

6. Demonstrační příklady

Po spuštění prvního demonstračního příkladu se zobrazí NURB křivka prvního stupně spolu se svými řídicími body. Těmito body je možné pomocí myši pohybovat a měnit tak výsledný tvar křivky.

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 10: Screenshot prvního demonstračního příkladu

Druhý demonstrační příklad je podobný příkladu předchozímu s tím rozdílem, že se zobrazí NURB křivka druhého stupně, tj. kvadrika.

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 11: Screenshot druhého demonstračního příkladu

Třetí demonstrační příklad navazuje na oba příklady předchozí. Rozdíl spočívá pouze v tom, že se zobrazí NURB křivka třetího stupně, tj. kubika.

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 12: Screenshot třetího demonstračního příkladu

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

Další pokračování tohoto seriálu bude věnováno NURB plochám, které vznikají poměrně jednoduchým a přímočarým rozšířením NURB křivek.

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

gluNewNurbsRenderer()
gluDeleteNurbsRenderer()
gluNurbsProperty()
gluNurbsCallback()
gluNurbsCurve()
gluBeginCurve()
gluEndCurve()

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